JavaSE基礎知識分享(十四)

ikestu小猪發表於2024-08-23

寫在前面

今天繼續講Java中的類載入器和lambda表示式的知識!

類載入器和反射

類的載入

當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會透過載入、連線、初始化三步來實現對這個類進行初始化。

  1. 載入
    .class 檔案讀入記憶體,併為之建立一個 Class 物件。任何類被使用時系統都會建立一個 Class 物件。

  2. 連線

    • 驗證:檢查類的內部結構是否正確,並與其他類協調一致。
    • 準備:為類的靜態成員分配記憶體,並設定預設初始化值。
    • 解析:將類的二進位制資料中的符號引用替換為直接引用。
  3. 初始化
    執行類的靜態初始化器和靜態程式碼塊。

一個類的初始化時機

  1. 建立類的例項。
  2. 訪問類的靜態變數,或者為靜態變數賦值。
  3. 呼叫類的靜態方法。
  4. 使用反射方式強制建立某個類或介面對應的 java.lang.Class 物件。
  5. 初始化某個類的子類。
  6. 直接使用 java.exe 命令執行某個主類。

類載入器

負責將 .class 檔案載入到記憶體中,併為之生成對應的 Class 物件。瞭解類載入機制可以幫助更好地理解程式的執行。

類載入器的組成

  1. Bootstrap ClassLoader(根類載入器)
    負責載入 Java 核心類,如 SystemString 等。它在 JDK 的 JRE lib 目錄下的 rt.jar 檔案中。

  2. Extension ClassLoader(擴充套件類載入器)
    負責載入 JRE 擴充套件目錄中的 JAR 包。在 JDK 的 JRE lib 目錄下的 ext 目錄中。

  3. System ClassLoader(系統類載入器)
    負責載入來自 java 命令的 .class 檔案,以及 classpath 環境變數所指定的 JAR 包和類路徑。

Java 反射機制

Java 反射機制允許在執行時動態獲取類的資訊以及呼叫物件的方法。

透過反射獲取構造方法並使用

  • 獲取構造方法

    • getConstructors()
    • getDeclaredConstructors()
  • 建立物件

    con.newInstance("zhangsan", 20);
    

透過反射獲取成員變數並使用

  • 獲取所有成員

    • getFields()
    • getDeclaredFields()
  • 獲取單個成員

    • getField()
    • getDeclaredField()
  • 修改成員的值

    field.set(obj, value);
    

透過反射獲取成員方法並使用

  • 獲取所有方法

    • getMethods()
    • getDeclaredMethods()
  • 獲取單個方法

    • getMethod()
    • getDeclaredMethod()
  • 暴力訪問

    method.setAccessible(true);
    

Lambda 表示式

從 JDK 1.8 開始,Lambda 表示式簡化了程式碼開發,支援函數語言程式設計。

寫 Lambda 表示式的場景

  1. 必須有相應的函式介面,函式介面是指內部有且僅有一個抽象方法的介面。
  2. 編譯器透過型別推斷機制可以推斷出引數列表的型別。

Lambda 基本語法

Lambda 表示式由 -> 運算子分隔為兩部分:

  • 左側:指定 Lambda 表示式需要的所有引數(對應介面中的形參)。
  • 右側:指定 Lambda 體,即要執行的功能(方法體)。

Lambda 表示式的分類

  1. 無引數,無返回值。
  2. 有一個引數,無返回值(若只有一個引數,小括號可以省略)。
  3. 有兩個以上的引數,有返回值,並且 Lambda 體中有多條語句。
  4. 若 Lambda 體中只有一條語句,return 和大括號都可以省略。
  5. Lambda 表示式的引數列表的資料型別可以省略,JVM 編譯器透過上下文推斷出資料型別。

Java 內建函式式介面

  • Predicate<T>:斷言型介面

    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }
    
  • Function<T, R>:函式型介面

    @FunctionalInterface
    public interface Function<T, R> {
        R apply(T t);
    }
    
  • Supplier<T>:供給型介面

    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }
    
  • Consumer<T>:消費型介面

    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
    }
    

Lambda 用法再簡潔之方法引用

  • 物件的方法引用

    物件引用::方法名
    
  • 靜態方法引用

    類名::靜態方法名
    
  • 構造方法引用

    類名::new
    
  • 陣列建立引用

    元素型別[]::new
    

容易形成的誤區

Lambda 表示式底層被封裝成了主類的一個私有方法,並透過 invokedynamic 指令進行呼叫,簡化了匿名內部類的寫法。Lambda 表示式使程式碼更簡潔,但也可能降低可讀性。

優缺點

  • 優點

    • 減少程式碼書寫,減少匿名內部類的建立,節省記憶體佔用。
    • 使用時無需記憶介面和抽象函式。
  • 缺點

    • 易讀性較差,需要熟悉 Lambda 表示式和抽象函式中引數的型別。
    • 不方便進行除錯

相關文章