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