CoreJava 笔记总结-第六章 接口、lambda表达式与内部类
文章目錄
- 第六章 接口、lambda表達(dá)式與內(nèi)部類
- ==接口==
- 接口的概念
- 接口的屬性
- 接口與抽象類
- 靜態(tài)和私有方法
- 默認(rèn)方法
- 解決默認(rèn)方法沖突
- 接口與回調(diào)
- `Comparator`接口
- 對象克隆
- ==`lambda`表達(dá)式==
- ==函數(shù)式接口==
- 方法引用
- 構(gòu)造器引用
- 變量作用域
- 處理`lambda`表達(dá)式
- 再談`Comparator`類
- 內(nèi)部類
- 使用內(nèi)部類訪問對象的狀態(tài)
- 局部內(nèi)部類
- 由外部方法訪問變量
- 匿名內(nèi)部類
- 靜態(tài)內(nèi)部類
- 代理
- 何時(shí)使用代理
- 創(chuàng)建代理對象
- 代理的特性
第六章 接口、lambda表達(dá)式與內(nèi)部類
- 接口: 描述類應(yīng)該做什么, 不指定如何做
- lambda表達(dá)式: 表示使用回調(diào)或者可變行為的代碼
接口
接口的概念
-
接口: 不是類, 而是對希望符合這個(gè)接口的類的一組需求
-
Arrays類中的sort方法對對象數(shù)組進(jìn)行排序, 要求對象所屬的內(nèi)必須實(shí)現(xiàn)Comparable接口
- 接口中的所有方法自動為public
- 接口中可以包含多個(gè)方法, 但是接口不會有實(shí)例字段
- 讓一個(gè)類實(shí)現(xiàn)一個(gè)接口:
- 將類聲明為實(shí)現(xiàn)給定的接口
- 對接口中的所有方法提供定義
- 將類聲明為實(shí)現(xiàn)為某個(gè)接口, 使用關(guān)鍵字implements
- Java API建議equals, compareTo方法兼容
- 例外:x = BigDecimal("1.0"); y = BigDecimal("1.00"); x.equals(y)//false x.compareTo(y) == 0
接口的屬性
- 接口不是類, 不能用new運(yùn)算符實(shí)例化一個(gè)接口
- 能夠聲明接口變量: Comparable x ; //OK
- 接口變量必須引用實(shí)現(xiàn)了這個(gè)接口的類對象 x = new Employee(...);
- instanceof: 1. 檢查一個(gè)對象是否屬于某個(gè)特定的類 2. 一個(gè)對象是否實(shí)現(xiàn)了某個(gè)特定的接口
- 與建立類的繼承層次類似, 可以擴(kuò)展接口
- 接口不能包含實(shí)例字段但是可以包含常量
- 接口的方法總是public, 接口的字段總是public static final, 都可以省略, 建議省略
- 每個(gè)類只有一個(gè)超類, 卻可以實(shí)現(xiàn)多個(gè)接口, 有點(diǎn)像C++的多重繼承
接口與抽象類
- 抽象類問題: 每個(gè)類只能擴(kuò)展一個(gè)類
- java可以擴(kuò)展一個(gè)基類并且派生多個(gè)接口
靜態(tài)和私有方法
- 標(biāo)準(zhǔn)庫中成對出現(xiàn)的接口和實(shí)用工具類: Collection/Collections, Path/paths
- 允許在接口中增加靜態(tài)方法, 一般做法是放在伴隨類中
- java9中接口中的方法可以是private
默認(rèn)方法
- 接口方法提供一個(gè)默認(rèn)實(shí)現(xiàn), default修飾符標(biāo)記
-
如果迭代器是只讀的就不用實(shí)現(xiàn)remove方法
public interface Iterator<E> {boolean hasNext();E next();default void remove(){throw new UnsupprotedOperationException("remove");} } -
另一個(gè)作用是接口演化, 實(shí)現(xiàn)源代碼兼容
解決默認(rèn)方法沖突
- 超類優(yōu)先
- 同時(shí)實(shí)現(xiàn)的兩個(gè)接口中由完全同名并且參數(shù)類型相同的方法, 要求這個(gè)類實(shí)現(xiàn)該方法覆蓋接口的方法
- class Student extends Person implements Named{...}只會考慮超類方法, 類優(yōu)先原則
接口與回調(diào)
- 回調(diào): 指定某個(gè)特定事件發(fā)生時(shí)應(yīng)該采取的動作
Comparator接口
- 對于對象數(shù)組進(jìn)行排序, 前提是這些對象是實(shí)現(xiàn)了Comparable接口類的實(shí)例
- 如果按照長度而不是字典順序?qū)τ谧址M(jìn)行排序, 使用Arrays.sort的另一種版本, 一個(gè)數(shù)組和比較器作為參數(shù)
- 比較器實(shí)現(xiàn)Comparator接口
對象克隆
- 拷貝: 一個(gè)包含對象引用的變量建立副本時(shí),原變量和副本都是對同一個(gè)對象的引用, 任何一個(gè)對象的引用都會改變另一個(gè)變量
- 克隆: 希望變量是一個(gè)新的對象, 初始狀態(tài)和原變量相同, 之后會有各自不同的狀態(tài)
- 默認(rèn)的克隆操作是淺拷貝: 逐個(gè)字段拷貝, 對于數(shù)值和其他基本類型克隆, 但是對于包含對象引用的子對象也會共享一些信息
- 如果原對象和淺克隆對象共享的子對象是不可變的, 那么淺拷貝的共享安全
- 深拷貝: 子對象可變的, 必須重新定義clone方法建立深拷貝, 克隆所有對象
- Cloneable: 標(biāo)記接口, 不包含任何方法(一般的接口確保一個(gè)類實(shí)現(xiàn)一組特定的方法), 作用:允許類型查詢中使用instanceof
- 所有數(shù)組類型都有一個(gè)公共的clone方法, 不是受保護(hù)的
lambda表達(dá)式
-
lambda表達(dá)式就是一個(gè)代碼塊,以及必須傳入的代碼變量規(guī)范
-
形式: 參數(shù), ->, 一個(gè)表達(dá)式
- (String first, String second)->{if(first.length() < second.length()) return -1;else return 1; }
-
lambda沒有參數(shù),`()-> {…};
-
如果可以推導(dǎo)出lambda表達(dá)式參數(shù)類型, 可以忽略其類型
-
只有一個(gè)參數(shù)并且類型可以推到, 可以省略小括號
-
無需指定返回類型
函數(shù)式接口
- 函數(shù)式接口: 對于只有一個(gè)抽象方法的接口,需要這種接口對象時(shí), 可以提供一個(gè)lambda表達(dá)式
- lambda表達(dá)式可以轉(zhuǎn)換為接口
- ArrayList類的removeIf方法參數(shù)是Predicate
- supplier沒有參數(shù), 調(diào)用時(shí)會生成T類型的值, 用于實(shí)現(xiàn)懶計(jì)算
方法引用
- var timer = new Timer(1000, event->System.out.println(event)); var timer = new Timer(1000, System.out::println);
-
System.out::println是一個(gè)方法引用, 它指示編譯器生成一個(gè)函數(shù)式接口的實(shí)例,覆蓋這個(gè)接口的抽象方法來調(diào)用給定的方法
-
上面的例子, 會生成一個(gè)ActionListener, 他的actionPerformed(ActionEvent e)方法要調(diào)用System.out.println(e)
-
方法引用不是對象, 為一個(gè)類型為函數(shù)式接口的變量賦值時(shí)會生成一個(gè)對象
-
方法引用示例與等價(jià)的lambda表達(dá)式見P248
-
當(dāng)lambda表達(dá)式的體只調(diào)用一個(gè)方法而不做其他操作的時(shí)候才能把方法引用重寫為方法引用
構(gòu)造器引用
- Person::new就是Person構(gòu)造器的一個(gè)引用
- int[]::new是一個(gè)構(gòu)造器引用, 他有一個(gè)參數(shù),即數(shù)組的長度, 等價(jià)于x->new int[x]
變量作用域
- lambda表達(dá)式三個(gè)部分: 代碼塊, 參數(shù), 自由變量的值
- 可以把一個(gè)lambda表達(dá)式轉(zhuǎn)換為一個(gè)包含方法的對象, 自由變量的值會復(fù)制到這個(gè)對象的實(shí)例變量中
- lambda表達(dá)式是閉包的
- lambda表達(dá)式可以捕獲外圍作用域中變量的值, 確保值是明確定義的(事實(shí)最終變量, 初始化后不會改變)
處理lambda表達(dá)式
-
lambda表達(dá)式重點(diǎn)是延遲執(zhí)行
-
Runnable作為無參數(shù)或返回值的動作運(yùn)行, action.run()會調(diào)用lambda表達(dá)式主體
package test;import java.util.function.IntConsumer;public class lambda {public static void main(String[] args){repeat(10, ()->System.out.println("hello, world"));repeat(10, (i)->System.out.println("Countdown:" + (9-i)));}public static void repeat(int n, Runnable action){for(int i = 0; i < n; i++) action.run();}public static void repeat(int n, IntConsumer action) {for(int i = 0; i < n; i++)action.accept(i);} }
再談Comparator類
- P255
內(nèi)部類
- 定義在另一個(gè)類中的類
- 內(nèi)部類可以對同一個(gè)包中的其他類隱藏, 內(nèi)部類方法可以訪問定義這個(gè)類的作用域中的數(shù)據(jù),包括原本的私有數(shù)據(jù)
使用內(nèi)部類訪問對象的狀態(tài)
- 一個(gè)內(nèi)部類方法可以訪問自身的數(shù)據(jù)字段,也可以訪問創(chuàng)建它的外圍類對象的數(shù)據(jù)字段
- 內(nèi)部類對象總有一個(gè)隱式引用指向創(chuàng)建它的外部類對象
- 這個(gè)引用在構(gòu)造器中設(shè)置, 編譯器會修改所有內(nèi)部類的構(gòu)造器,添加一個(gè)對應(yīng)外圍類引用的參數(shù)
局部內(nèi)部類
-
TimePrinter名字只出現(xiàn)了一次,在start方法中創(chuàng)建這個(gè)類型對象時(shí)使用了一次.可以在一個(gè)方法中局部定義這個(gè)類
public void start() {class TimerPrinter implements ActionListener {public void actionPerformed(ActionEvent event) {System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));if (beep) {Toolkit.getDefaultToolkit().beep();}}}var listener = new TimerPrinter();var timer = new Timer(interval, listener);timer.start(); } -
局部內(nèi)部類聲明時(shí)候不能有訪問修飾符public, private
-
優(yōu)勢: 對外部完全隱藏,除了start代碼
由外部方法訪問變量
-
局部內(nèi)部類不僅能夠訪問外部類字段,還可以訪問局部變量(事實(shí)最終變量)
- public void start(int interval, boolean beep) {class TimerPrinter implements ActionListener {public void actionPerformed(ActionEvent event) {System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));if (beep) {Toolkit.getDefaultToolkit().beep();}}}var listener = new TimerPrinter();var timer = new Timer(interval, listener);timer.start(); }
匿名內(nèi)部類
-
匿名內(nèi)部類不需要為類指定名字
-
以下代碼: 創(chuàng)建了一個(gè)類的新對象,這個(gè)類實(shí)現(xiàn)了ActionListener接口, 需要實(shí)現(xiàn)的方法{}中定義
public void start(int interval, boolean beep) {var listener = new ActionListener();{public void actionPerformed(ActionEvent event) {System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));if (beep) {Toolkit.getDefaultToolkit().beep();}}};var timer = new Timer(interval, listener);timer.start(); }//用lambda表達(dá)式 public void start(int interval boolean beep) {var timer = new Timer(interval, event->{System.out.println("At this tone, the time is " + Instant.ofEpochMilli(event.getWhen()));if (beep) Toolkit.getDefaultToolkit().beep();}) } -
語法如下
SuperType可以是接口,內(nèi)部類就要實(shí)現(xiàn)這個(gè)接口;如果是一個(gè)類,內(nèi)部類就要擴(kuò)展這個(gè)類
- 構(gòu)造器名字必須和類名相同,匿名內(nèi)部類沒有類名所以沒有構(gòu)造器
- 構(gòu)造參數(shù)要傳遞給超類構(gòu)造器
- 注意: 構(gòu)造一個(gè)類的新對象和構(gòu)造一個(gè)擴(kuò)展了那個(gè)類的匿名內(nèi)部類的對象之間的差別
-
匿名內(nèi)部類不能有構(gòu)造器但是可以提供一個(gè)對象的
-
雙括號初始化:
- var f = new ArrayList<String>(); f.add("Harry"); f.add("Alice"); invite(f); ---> invite(new ArrayList<String>)(){{add("Harry"); add("Alice");}} //外層括號建立了一個(gè)匿名子類,內(nèi)層括號是一個(gè)初始化塊
-
得到匿名內(nèi)部類的外部類類名不能直接getClass,這個(gè)方法帶調(diào)用this.getClass(), 靜態(tài)方法沒有隱式參數(shù)
new Object()建立Object的匿名子類的一個(gè)匿名對象,getEnclosingClass則得到其外圍類,也就是包含這個(gè)靜態(tài)方法的類
靜態(tài)內(nèi)部類
- 只要內(nèi)部類不需要訪問外圍類對象,就應(yīng)該使用靜態(tài)內(nèi)部類
- 接口中聲明的內(nèi)部類自動為public, static
代理
何時(shí)使用代理
- 代理類在運(yùn)行時(shí)闖將全新的類,這樣代理類可以實(shí)現(xiàn)你指定的接口
- 代理類包含的方法: 指定接口所需要的全部方法, Object類中的全部方法(equals, toStirng等)
創(chuàng)建代理對象
- 需要使用Proxy類的newProxyInstance方法, 有三個(gè)參數(shù)
- 一個(gè)類加載器(這里指定系統(tǒng)類加載器)
- 一個(gè)Class對象數(shù)組,每個(gè)元素對應(yīng)需要實(shí)現(xiàn)的各個(gè)接口
- 一個(gè)調(diào)用處理器
- 代理作用: 將方法調(diào)用路由到遠(yuǎn)程服務(wù)器;在運(yùn)行中的程序?qū)⒂脩艚缑媸录c動作關(guān)聯(lián)起來;為了調(diào)試,跟蹤方法使用
代理的特性
- 代理是在運(yùn)行過程中創(chuàng)建的,一旦創(chuàng)建就變成了常規(guī)類
- 代理類都是擴(kuò)展Proxy, 一個(gè)代理類只有一個(gè)實(shí)例字段即調(diào)用處理器,在超類Proxy中定義
- 所有的代理類都要覆蓋toString, hasCode, equals方法, 這些方法只是在調(diào)用處理器上調(diào)用invoke.
總結(jié)
以上是生活随笔為你收集整理的CoreJava 笔记总结-第六章 接口、lambda表达式与内部类的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何将图片转换成可编辑的电子文档呢?教你
- 下一篇: CoreJava 笔记总结-第七章 异常