寫在前面
今天繼續講Java中的類載入器和lambda表示式的知識!
類載入器和反射
類的載入
當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會透過載入、連線、初始化三步來實現對這個類進行初始化。
-
載入
將.class
檔案讀入記憶體,併為之建立一個Class
物件。任何類被使用時系統都會建立一個Class
物件。 -
連線
- 驗證:檢查類的內部結構是否正確,並與其他類協調一致。
- 準備:為類的靜態成員分配記憶體,並設定預設初始化值。
- 解析:將類的二進位制資料中的符號引用替換為直接引用。
-
初始化
執行類的靜態初始化器和靜態程式碼塊。
一個類的初始化時機
- 建立類的例項。
- 訪問類的靜態變數,或者為靜態變數賦值。
- 呼叫類的靜態方法。
- 使用反射方式強制建立某個類或介面對應的
java.lang.Class
物件。 - 初始化某個類的子類。
- 直接使用
java.exe
命令執行某個主類。
類載入器
負責將 .class
檔案載入到記憶體中,併為之生成對應的 Class
物件。瞭解類載入機制可以幫助更好地理解程式的執行。
類載入器的組成
-
Bootstrap ClassLoader(根類載入器)
負責載入 Java 核心類,如System
、String
等。它在 JDK 的 JRElib
目錄下的rt.jar
檔案中。 -
Extension ClassLoader(擴充套件類載入器)
負責載入 JRE 擴充套件目錄中的 JAR 包。在 JDK 的 JRElib
目錄下的ext
目錄中。 -
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 表示式的場景
- 必須有相應的函式介面,函式介面是指內部有且僅有一個抽象方法的介面。
- 編譯器透過型別推斷機制可以推斷出引數列表的型別。
Lambda 基本語法
Lambda 表示式由 ->
運算子分隔為兩部分:
- 左側:指定 Lambda 表示式需要的所有引數(對應介面中的形參)。
- 右側:指定 Lambda 體,即要執行的功能(方法體)。
Lambda 表示式的分類
- 無引數,無返回值。
- 有一個引數,無返回值(若只有一個引數,小括號可以省略)。
- 有兩個以上的引數,有返回值,並且 Lambda 體中有多條語句。
- 若 Lambda 體中只有一條語句,
return
和大括號都可以省略。 - 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 表示式和抽象函式中引數的型別。
- 不方便進行除錯