1. 程式人生 > 程式設計 >Java BufferedReader相關原始碼例項分析

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的匿名內部類用法,可以分析出極點內容:

  1. Thread類需要Runnable介面作為引數,其中的抽象run方法是用來指定執行緒任務內容的核心;
  2. 為了指定run的方法體,不得不需要Runnable介面的實現類;
  3. 為了省去定義一個RunnableImpl實現類的麻煩,不得不使用匿名內部類;
  4. 必須覆蓋重寫抽象run方法,所以方法名稱、方法引數、方法返回值不得不在寫一遍,且態寫錯;
  5. 而實際上,似乎只有方法體才是關鍵所在。

程式設計思想轉換

我們真的希望建立一個匿名內部類物件嗎?不,我們只是為了做這件事而不得不建立一個物件,我們真正希望做的事情是:將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表示式的標準格式:

由三部分組成:

  1. 一些引數
  2. 一個箭頭
  3. 一段程式碼

格式:

​ (引數列表) -> {一些重寫方法的程式碼};

解釋說明格式:

​ ():介面中抽象方法的引數列表,沒有引數,就空著;有引數就寫出引數,多個引數使用逗號分隔。

​ ->:傳遞的一絲,把引數傳遞給方法體{}。

​ {}:重寫介面的抽象方法的方法體。

練習:使用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表示式:是可推導,可以省略

可以省略的內容:

  1. (引數列表):括號中引數列表的資料型別,可以省略不寫。

  2. (引數列表):括號中的引數如果只有一個,那麼型別和()都可以省略。

  3. {一些程式碼}:如果{}中的程式碼只有一行,無論是否有返回值,都可以省略{}和return和;。

    注意:要省略{},return,;,必須一起省略。

前提:

Lambda的語法非常簡潔,完全沒有面向物件複雜的束縛,但是使用時有幾個問題需要特別注意:

  1. 使用Lambda必須具有介面,且要求介面中有且僅有一個抽象方法。

    無論是JDK內建的Runnable、Comparator介面還是自定義的介面,只有當介面中的抽象方法存在且唯一時才可以使用Lambda。

  2. 使用Lambda必須具有上下文推斷。

    也就是方法的引數或區域性變數型別必須為Lambda對應的介面型別,才能使用Lambda作為介面的例項。

PS:有且僅有一個抽象方法的介面,稱為“函式式介面”。