Java BufferedReader相關原始碼例項分析
函數語言程式設計思想概述
在數學中,函式就是有輸入量、輸入量的一套計算方案,也就是“拿什麼東西做什麼事情”,相對而言,面向物件過分強調“必須通過物件的形式來做事情”,而函式式思想則儘量忽略面向物件的複雜語法--強調做什麼,而不是以什麼形式做。
面向物件的思想:
做一件事,找一個能解決這個事情的物件,呼叫物件的方法,完成事情。
函數語言程式設計思想:
只要能獲取到結果,誰去做的,怎麼做的都不重要,重視的是結果,不重視過程
冗餘的Runnable程式碼
程式碼演示:
package day1; /* 使用實現Runnable介面的方式實現多執行緒程式 */ public class Demo01 { public static void main(String[] args) { //建立Runnable介面的實現類物件 RunnableImpl run = new RunnableImpl(); //建立Thread類物件,構造方法中傳遞Runnable介面的實現類 Thread t = new Thread(run); t.start(); //簡化程式碼,使用匿名內部類,實現多執行緒程式 Runnable r = new Runnable() { @Override public void run() { } }; new Thread(r).start(); //簡化程式碼 new Thread(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"新執行緒建立了"); } }).start(); } }
package day1;
/*
建立Runnable介面的實現類,重寫Run方法,設定執行緒任務
*/
public class RunnableImpl implements Runnable{
@Override
public void run(){
System.out.println(Thread.currentThread().getName()+"新執行緒建立");
}
}
程式碼分析
對於Runnable的匿名內部類用法,可以分析出極點內容:
- Thread類需要Runnable介面作為引數,其中的抽象run方法是用來指定執行緒任務內容的核心;
- 為了指定run的方法體,不得不需要Runnable介面的實現類;
- 為了省去定義一個RunnableImpl實現類的麻煩,不得不使用匿名內部類;
- 必須覆蓋重寫抽象run方法,所以方法名稱、方法引數、方法返回值不得不在寫一遍,且態寫錯;
- 而實際上,似乎只有方法體才是關鍵所在。
程式設計思想轉換
我們真的希望建立一個匿名內部類物件嗎?不,我們只是為了做這件事而不得不建立一個物件,我們真正希望做的事情是:將run方法體內的程式碼傳遞給Thread類知曉。
“傳遞一段程式碼”--這才是我們真正的目的。而建立物件只是受限於面對物件語法而不得不採取的一種手段方式。那,有沒有更簡單的方法?如果我們將關注點從“怎麼做”迴歸到“做什麼”的本質上,就會發現只要能夠更好的達到目的,過程和形式其實並不重要。
簡單來說,就是使用更簡單的方式,更簡潔的程式碼來實現本來需要很多程式碼的地方。
體驗Lambda的更優寫法
程式碼演示:
package day1;
public class Demo02 {
public static void main(String[] args) {
//使用匿名內部類的方式,實現多執行緒
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"新執行緒建立了");
}
}).start();
//使用Lambda表示式,實現多執行緒
//簡化程式碼
new Thread(()-> {
System.out.println(Thread.currentThread().getName()+"新執行緒建立了");
}
).start();
}
}
匿名內部類的好處和弊端
一方面,匿名內部類可以幫我們省去實現類的定義;另一方面,匿名內部類的語法--確實太複雜了!
語義分析
仔細分析該程式碼中的語義,Runnable介面只有一個run方法的定義:
- public abstract void run();
即指定了一種做事情的方案(其實就是一個函式):
- 無引數:不需要任何條件即可執行該方案。
- 無返回值:該方案不產生任何結果。
- 程式碼塊(方法體):該方案的具體執行步驟。
同樣的語義體現在Lambda語法中,要更簡單:
() -> System.out.println("多執行緒任務執行!")
- 前面的一個小括號即run方法的引數(無),代表不需要任何條件;
- 中間的一個箭頭代表將前面的引數傳遞給後面的程式碼;
- 後面的輸入預計即業務執行邏輯。
Lambda表示式的標準格式
Lambda表示式的標準格式:
由三部分組成:
- 一些引數
- 一個箭頭
- 一段程式碼
格式:
(引數列表) -> {一些重寫方法的程式碼};
解釋說明格式:
():介面中抽象方法的引數列表,沒有引數,就空著;有引數就寫出引數,多個引數使用逗號分隔。
->:傳遞的一絲,把引數傳遞給方法體{}。
{}:重寫介面的抽象方法的方法體。
練習:使用Lambda標準格式(無參無返回)
題目:給定一個廚子cook介面,內含唯一的抽象方法makeFood,無引數,無返回值。使用Lambda的標準格式,呼叫invokeCook方法,列印輸出"吃飯啦!"字樣。
package day1;
public class Cook01 {
public static void main(String[] args) {
//呼叫invokeCook方法,引數Cook介面,傳遞Cook介面的匿名內部類物件
invokeCook(new Cook() {
@Override
public void makeFood() {
System.out.println("吃飯了!");
}
});
//使用Lambda表示式,簡化匿名內部類的步驟
invokeCook(() ->{
System.out.println("吃飯了!");
});
}
//定義一個方法,引數傳遞cook介面,方法內部呼叫cook介面中的方法makeFood
public static void invokeCook(Cook cook){
cook.makeFood();
}
}
Lambda表示式有引數有返回值的練習
需求:
使用陣列儲存多個Person物件
對陣列中的Person物件使用Arrays的sort方法通過年齡進行升序排序
package day1;
import java.util.Arrays;
import java.util.Comparator;
public class Arrays01 {
public static void main(String[] args) {
//使用陣列儲存多個Person物件
Person[] arr = {
new Person("小明",18),
new Person("阿狗",15),
new Person("咖啡",35)
};
//對陣列中的Person物件使用Arrays的sort方法通過年齡進行升序(前邊-後邊)排序
Arrays.sort(arr, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
});
//使用Lambda表示式,簡化匿名內部類
Arrays.sort(arr,(Person o1,Person o2) ->{
return o1.getAge()-o2.getAge();
});
//遍歷陣列
for (Person p:arr){
System.out.println(p);
}
}
}
練習:使用Lambda標準格式(有引數有返回)(自定義介面)
需求:
給定一個計算器Calculator介面,內含抽象方法calc可以將兩個int數字相加得到的和值。
請使用Lambda的標準格式呼叫invokeCalc方法,完成120和130的相加計算。
package day1;
public class DemoCalculator {
public static void main(String[] args) {
//使用匿名內部類
invokeCalc(10, 20, new Calculator() {
@Override
public int calc(int a, int b) {
return a+b;
}
});
//使用Lambda表示式簡化匿名內部類的書寫
invokeCalc(120,130,(int a,int b) ->{
return a+b;
});
}
/*
定義一個方法
引數傳遞兩個int型別的整數
引數傳遞Calculator介面
方法內部呼叫Calculator中的方法calc計算兩個整數的和。
*/
public static void invokeCalc(int a,int b,Calculator c){
int sum = c.calc(a,b);
System.out.println(sum);
}
}
Lambda表示式省略寫法&Lambda表示式使用前提
Lambda表示式:是可推導,可以省略
可以省略的內容:
-
(引數列表):括號中引數列表的資料型別,可以省略不寫。
-
(引數列表):括號中的引數如果只有一個,那麼型別和()都可以省略。
-
{一些程式碼}:如果{}中的程式碼只有一行,無論是否有返回值,都可以省略{}和return和;。
注意:要省略{},return,;,必須一起省略。
前提:
Lambda的語法非常簡潔,完全沒有面向物件複雜的束縛,但是使用時有幾個問題需要特別注意:
-
使用Lambda必須具有介面,且要求介面中有且僅有一個抽象方法。
無論是JDK內建的Runnable、Comparator介面還是自定義的介面,只有當介面中的抽象方法存在且唯一時才可以使用Lambda。
-
使用Lambda必須具有上下文推斷。
也就是方法的引數或區域性變數型別必須為Lambda對應的介面型別,才能使用Lambda作為介面的例項。
PS:有且僅有一個抽象方法的介面,稱為“函式式介面”。