1.Lambda表達式
1.1體驗Lambda表達式【理解】
-
案例需求
啓動一個線程,在控制檯輸出一句話:多線程程序啓動了
-
實現方式一
- 實現步驟
- 定義一個類MyRunnable實現Runnable接口,重寫run()方法
- 創建MyRunnable類的對象
- 創建Thread類的對象,把MyRunnable的對象作爲構造參數傳遞
- 啓動線程
- 實現步驟
-
實現方式二
- 匿名內部類的方式改進
-
實現方式三
- Lambda表達式的方式改進
-
代碼演示
//方式一的線程類 public class MyRunnable implements Runnable { @Override public void run() { System.out.println("多線程程序啓動了"); } } public class LambdaDemo { public static void main(String[] args) { //方式一 // MyRunnable my = new MyRunnable(); // Thread t = new Thread(my); // t.start(); //方式二 // new Thread(new Runnable() { // @Override // public void run() { // System.out.println("多線程程序啓動了"); // } // }).start(); //方式三 new Thread( () -> { System.out.println("多線程程序啓動了"); } ).start(); } }
-
函數式編程思想概述
函數式思想則儘量忽略面向對象的複雜語法:“強調做什麼,而不是以什麼形式去做”
而我們要學習的Lambda表達式就是函數式思想的體現
1.2Lambda表達式的標準格式【理解】
-
格式:
(形式參數) -> {代碼塊}
-
形式參數:如果有多個參數,參數之間用逗號隔開;如果沒有參數,留空即可
-
->:由英文中畫線和大於符號組成,固定寫法。代表指向動作
-
代碼塊:是我們具體要做的事情,也就是以前我們寫的方法體內容
-
-
組成Lambda表達式的三要素:
- 形式參數,箭頭,代碼塊
1.3Lambda表達式練習1【應用】
-
Lambda表達式的使用前提
-
有一個接口
-
接口中有且僅有一個抽象方法
-
-
練習描述
無參無返回值抽象方法的練習
-
操作步驟
-
定義一個接口(Eatable),裏面定義一個抽象方法:void eat();
-
定義一個測試類(EatableDemo),在測試類中提供兩個方法
-
一個方法是:useEatable(Eatable e)
-
一個方法是主方法,在主方法中調用useEatable方法
-
-
-
示例代碼
//接口 public interface Eatable { void eat(); } //實現類 public class EatableImpl implements Eatable { @Override public void eat() { System.out.println("一天一蘋果,醫生遠離我"); } } //測試類 public class EatableDemo { public static void main(String[] args) { //在主方法中調用useEatable方法 Eatable e = new EatableImpl(); useEatable(e); //匿名內部類 useEatable(new Eatable() { @Override public void eat() { System.out.println("一天一蘋果,醫生遠離我"); } }); //Lambda表達式 useEatable(() -> { System.out.println("一天一蘋果,醫生遠離我"); }); } private static void useEatable(Eatable e) { e.eat(); } }
1.4Lambda表達式練習2【應用】
-
練習描述
有參無返回值抽象方法的練習
-
操作步驟
-
定義一個接口(Flyable),裏面定義一個抽象方法:void fly(String s);
-
定義一個測試類(FlyableDemo),在測試類中提供兩個方法
-
一個方法是:useFlyable(Flyable f)
-
一個方法是主方法,在主方法中調用useFlyable方法
-
-
-
示例代碼
public interface Flyable { void fly(String s); } public class FlyableDemo { public static void main(String[] args) { //在主方法中調用useFlyable方法 //匿名內部類 useFlyable(new Flyable() { @Override public void fly(String s) { System.out.println(s); System.out.println("飛機自駕遊"); } }); System.out.println("--------"); //Lambda useFlyable((String s) -> { System.out.println(s); System.out.println("飛機自駕遊"); }); } private static void useFlyable(Flyable f) { f.fly("風和日麗,晴空萬里"); } }
1.5Lambda表達式練習3【應用】
-
練習描述
有參有返回值抽象方法的練習
-
操作步驟
-
定義一個接口(Addable),裏面定義一個抽象方法:int add(int x,int y);
-
定義一個測試類(AddableDemo),在測試類中提供兩個方法
-
一個方法是:useAddable(Addable a)
-
一個方法是主方法,在主方法中調用useAddable方法
-
-
-
示例代碼
public interface Addable { int add(int x,int y); } public class AddableDemo { public static void main(String[] args) { //在主方法中調用useAddable方法 useAddable((int x,int y) -> { return x + y; }); } private static void useAddable(Addable a) { int sum = a.add(10, 20); System.out.println(sum); } }
1.6Lambda表達式的省略模式【應用】
-
省略的規則
- 參數類型可以省略。但是有多個參數的情況下,不能只省略一個
- 如果參數有且僅有一個,那麼小括號可以省略
- 如果代碼塊的語句只有一條,可以省略大括號和分號,和return關鍵字
-
代碼演示
public interface Addable { int add(int x, int y); } public interface Flyable { void fly(String s); } public class LambdaDemo { public static void main(String[] args) { // useAddable((int x,int y) -> { // return x + y; // }); //參數的類型可以省略 useAddable((x, y) -> { return x + y; }); // useFlyable((String s) -> { // System.out.println(s); // }); //如果參數有且僅有一個,那麼小括號可以省略 // useFlyable(s -> { // System.out.println(s); // }); //如果代碼塊的語句只有一條,可以省略大括號和分號 useFlyable(s -> System.out.println(s)); //如果代碼塊的語句只有一條,可以省略大括號和分號,如果有return,return也要省略掉 useAddable((x, y) -> x + y); } private static void useFlyable(Flyable f) { f.fly("風和日麗,晴空萬里"); } private static void useAddable(Addable a) { int sum = a.add(10, 20); System.out.println(sum); } }
1.7Lambda表達式的注意事項【理解】
-
使用Lambda必須要有接口,並且要求接口中有且僅有一個抽象方法
-
必須有上下文環境,才能推導出Lambda對應的接口
-
根據局部變量的賦值得知Lambda對應的接口
Runnable r = () -> System.out.println(“Lambda表達式”);
-
根據調用方法的參數得知Lambda對應的接口
new Thread(() -> System.out.println(“Lambda表達式”)).start();
-
1.8Lambda表達式和匿名內部類的區別【理解】
-
所需類型不同
- 匿名內部類:可以是接口,也可以是抽象類,還可以是具體類
- Lambda表達式:只能是接口
-
使用限制不同
-
如果接口中有且僅有一個抽象方法,可以使用Lambda表達式,也可以使用匿名內部類
-
如果接口中多於一個抽象方法,只能使用匿名內部類,而不能使用Lambda表達式
-
-
實現原理不同
- 匿名內部類:編譯之後,產生一個單獨的.class字節碼文件
- Lambda表達式:編譯之後,沒有一個單獨的.class字節碼文件。對應的字節碼會在運行的時候動態生成
2.接口組成更新
2.1接口組成更新概述【理解】
-
常量
public static final
-
抽象方法
public abstract
-
默認方法(Java 8)
-
靜態方法(Java 8)
-
私有方法(Java 9)
2.2接口中默認方法【應用】
-
格式
public default 返回值類型 方法名(參數列表) { }
-
範例
public default void show3() { }
-
注意事項
-
默認方法不是抽象方法,所以不強制被重寫。但是可以被重寫,重寫的時候去掉default關鍵字
-
public可以省略,default不能省略
-
2.3接口中靜態方法【應用】
-
格式
public static 返回值類型 方法名(參數列表) { }
-
範例
public static void show() { }
-
注意事項
-
靜態方法只能通過接口名調用,不能通過實現類名或者對象名調用
-
public可以省略,static不能省略
-
2.4接口中私有方法【應用】
-
私有方法產生原因
Java 9中新增了帶方法體的私有方法,這其實在Java 8中就埋下了伏筆:Java 8允許在接口中定義帶方法體的默認方法和靜態方法。這樣可能就會引發一個問題:當兩個默認方法或者靜態方法中包含一段相同的代碼實現時,程序必然考慮將這段實現代碼抽取成一個共性方法,而這個共性方法是不需要讓別人使用的,因此用私有給隱藏起來,這就是Java 9增加私有方法的必然性
-
定義格式
-
格式1
private 返回值類型 方法名(參數列表) { }
-
範例1
private void show() { }
-
格式2
private static 返回值類型 方法名(參數列表) { }
-
範例2
private static void method() { }
-
-
注意事項
- 默認方法可以調用私有的靜態方法和非靜態方法
- 靜態方法只能調用私有的靜態方法
3.方法引用
3.1體驗方法引用【理解】
-
方法引用的出現原因
在使用Lambda表達式的時候,我們實際上傳遞進去的代碼就是一種解決方案:拿參數做操作
那麼考慮一種情況:如果我們在Lambda中所指定的操作方案,已經有地方存在相同方案,那是否還有必要再寫重複邏輯呢?答案肯定是沒有必要
那我們又是如何使用已經存在的方案的呢?
這就是我們要講解的方法引用,我們是通過方法引用來使用已經存在的方案
-
代碼演示
public interface Printable { void printString(String s); } public class PrintableDemo { public static void main(String[] args) { //在主方法中調用usePrintable方法 // usePrintable((String s) -> { // System.out.println(s); // }); //Lambda簡化寫法 usePrintable(s -> System.out.println(s)); //方法引用 usePrintable(System.out::println); } private static void usePrintable(Printable p) { p.printString("愛生活愛Java"); } }
3.2方法引用符【理解】
-
方法引用符
:: 該符號爲引用運算符,而它所在的表達式被稱爲方法引用
-
推導與省略
- 如果使用Lambda,那麼根據“可推導就是可省略”的原則,無需指定參數類型,也無需指定的重載形式,它們都將被自動推導
- 如果使用方法引用,也是同樣可以根據上下文進行推導
- 方法引用是Lambda的孿生兄弟
3.3引用類方法【應用】
引用類方法,其實就是引用類的靜態方法
-
格式
類名::靜態方法
-
範例
Integer::parseInt
Integer類的方法:public static int parseInt(String s) 將此String轉換爲int類型數據
-
練習描述
-
定義一個接口(Converter),裏面定義一個抽象方法 int convert(String s);
-
定義一個測試類(ConverterDemo),在測試類中提供兩個方法
-
一個方法是:useConverter(Converter c)
-
一個方法是主方法,在主方法中調用useConverter方法
-
-
-
代碼演示
public interface Converter { int convert(String s); } public class ConverterDemo { public static void main(String[] args) { //Lambda寫法 useConverter(s -> Integer.parseInt(s)); //引用類方法 useConverter(Integer::parseInt); } private static void useConverter(Converter c) { int number = c.convert("666"); System.out.println(number); } }
-
使用說明
Lambda表達式被類方法替代的時候,它的形式參數全部傳遞給靜態方法作爲參數
3.4引用對象的實例方法【應用】
引用對象的實例方法,其實就引用類中的成員方法
-
格式
對象::成員方法
-
範例
“HelloWorld”::toUpperCase
String類中的方法:public String toUpperCase() 將此String所有字符轉換爲大寫
-
練習描述
-
定義一個類(PrintString),裏面定義一個方法
public void printUpper(String s):把字符串參數變成大寫的數據,然後在控制檯輸出
-
定義一個接口(Printer),裏面定義一個抽象方法
void printUpperCase(String s)
-
定義一個測試類(PrinterDemo),在測試類中提供兩個方法
- 一個方法是:usePrinter(Printer p)
- 一個方法是主方法,在主方法中調用usePrinter方法
-
-
代碼演示
public class PrintString { //把字符串參數變成大寫的數據,然後在控制檯輸出 public void printUpper(String s) { String result = s.toUpperCase(); System.out.println(result); } } public interface Printer { void printUpperCase(String s); } public class PrinterDemo { public static void main(String[] args) { //Lambda簡化寫法 usePrinter(s -> System.out.println(s.toUpperCase())); //引用對象的實例方法 PrintString ps = new PrintString(); usePrinter(ps::printUpper); } private static void usePrinter(Printer p) { p.printUpperCase("HelloWorld"); } }
-
使用說明
Lambda表達式被對象的實例方法替代的時候,它的形式參數全部傳遞給該方法作爲參數
3.5引用類的實例方法【應用】
引用類的實例方法,其實就是引用類中的成員方法
-
格式
類名::成員方法
-
範例
String::substring
public String substring(int beginIndex,int endIndex)
從beginIndex開始到endIndex結束,截取字符串。返回一個子串,子串的長度爲endIndex-beginIndex
-
練習描述
-
定義一個接口(MyString),裏面定義一個抽象方法:
String mySubString(String s,int x,int y);
-
定義一個測試類(MyStringDemo),在測試類中提供兩個方法
-
一個方法是:useMyString(MyString my)
-
一個方法是主方法,在主方法中調用useMyString方法
-
-
-
代碼演示
public interface MyString { String mySubString(String s,int x,int y); } public class MyStringDemo { public static void main(String[] args) { //Lambda簡化寫法 useMyString((s,x,y) -> s.substring(x,y)); //引用類的實例方法 useMyString(String::substring); } private static void useMyString(MyString my) { String s = my.mySubString("HelloWorld", 2, 5); System.out.println(s); } }
-
使用說明
Lambda表達式被類的實例方法替代的時候
第一個參數作爲調用者
後面的參數全部傳遞給該方法作爲參數
3.6引用構造器【應用】
引用構造器,其實就是引用構造方法
-
l格式
類名::new
-
範例
Student::new
-
練習描述
-
定義一個類(Student),裏面有兩個成員變量(name,age)
並提供無參構造方法和帶參構造方法,以及成員變量對應的get和set方法
-
定義一個接口(StudentBuilder),裏面定義一個抽象方法
Student build(String name,int age);
-
定義一個測試類(StudentDemo),在測試類中提供兩個方法
-
一個方法是:useStudentBuilder(StudentBuilder s)
-
一個方法是主方法,在主方法中調用useStudentBuilder方法
-
-
-
代碼演示
public class Student { private String name; private int age; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } public interface StudentBuilder { Student build(String name,int age); } public class StudentDemo { public static void main(String[] args) { //Lambda簡化寫法 useStudentBuilder((name,age) -> new Student(name,age)); //引用構造器 useStudentBuilder(Student::new); } private static void useStudentBuilder(StudentBuilder sb) { Student s = sb.build("林青霞", 30); System.out.println(s.getName() + "," + s.getAge()); } }
-
使用說明
Lambda表達式被構造器替代的時候,它的形式參數全部傳遞給構造器作爲參數