JZ34 二叉樹中和為某一值的路徑(二) 描述 輸入一顆二叉樹的根節點root和一個整數expectNumber,找出二叉樹中結點值的和為expectNumber的所有路徑。 1.該題路徑定義為從樹的根結點開始往下一直到葉子結點所經過的結點 2.葉子節點是指沒有子節點的節點 3.路徑只能從父節點到子 ...
1. 反射的概念
反射 機制指的是,程式在運行時能夠獲取自身的信息。在 java 中只要給定類的名字,就能夠獲取類的所有屬性和方法。
反射是 Java 中很多高級特性的基礎,比如 註解、動態代理 以及 Spring Ioc、AOP 等技術都需要藉助反射來實現。
2. Class 對象的創建
java 中 java.lang.Class 是反射機制的基礎,當我們想要在運行期獲取一個類中的相關信息的時候,必須先獲取其 Class 類。Jvm 會自動將已載入類的 Class 對像載入。
獲取 Class 對象的三種方式:
-
對象實例.getClass()
MyObject obj = new MyObject();
Class clazz = obj.getClass();
-
類名.Class
Class clazz = MyObject.Class;
-
Class.forName()
Class clazz = Class.forName("MyObject");
3. 通過反射創建實例
java 中,最常使用的創建實例方法是通過 new 關鍵字來實現的。使用反射,也有兩種可以 創建實例 的方式。
使用 Class 對象的 newInstance() 方法
Class clazz = MyObject.class;
MyObject myObject = clazz.newInstance();
使用 java.lang.reflet.Constructor 中的 newInstance() 方法
Constructor<MyObject> constructor = MyObject.class.getConstructor();
MyObject myObject = constructor.newInstance();
說明:其實 clazz.newInstance() 創建實例的內部也是通過 Constructor 創建實例的方式來實現的
4. 通過反射獲取類的屬性、方法、註解、構造器
Class 類中有獲取類的所有 屬性、方法、註解、構造器的相關方法。如下:
獲取 非私有的 屬性、方法、註解、構造器
Field[] getFields(); // 獲取屬性
Method[] getMethods(); // 獲取方法
Annotation[] getAnnotations(); // 獲取註解
Constructor<?>[] getConstructors(); // 獲取構造器
獲取** 私有的** 屬性、方法、註解、構造器
Field[] getDeclaredFields(); // 獲取私有屬性
Method[] getDeclaredMethods(); // 獲取私有方法
Annotation[] getDeclaredAnnotations(); // 獲取私有註解
Constructor<?>[] getDeclaredConstructors(); // 獲取私有構造器
使用示例:
Class clazz = MyObject.class;
Methods[] methodList = clazz.getFields();
5. 反射機制的一些缺點
-
反射會破壞封裝性:由於反射允許代碼執行一些在正常情況下不被允許的操作(比如訪問私有的屬性和方法),所以使用反射可能會導致意料之外的副作用--代碼有功能上的錯誤,降低可移植性。反射代碼破壞了抽象性,因此當平臺發生改變的時候,代碼的行為就有可能也隨著變化。
-
反射的性能問題:反射包括了一些動態類型,所以 JVM 無法對這些代碼進行優化。因此,反射操作的效率要比那些非反射操作低得多。我們應該避免在經常被 執行的代碼或對性能要求很高的程式中使用反射。
-
安全性問題:使用反射技術要求程式必須在一個沒有安全限制的環境中運行。如果一個程式必須在有安全限制的環境中運行,如 Applet,那麼這就是個問題了。
6. 反射對單例的破壞
單例是為了控制 類示例 在記憶體中只存在一個的機制。他本身的構造方法是 private 的,對外提供 getSingleton() 方法,統一管理實例的獲取。
而反射可以通過獲取到類中的私有構造方法, 並將其變為可用,通過構造方法生成新的實例,這樣就造成了單例的破壞。
雙重校驗鎖的單例模式:
public class Singleton {
private static volatile Singleton singleton();
private Singleton() {
}
public static Singleton getSingleton() {
if(singleton == null) {
synchronized (Singleton.class) {
if(singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
通過反射破壞單例:
Singleton singleton1 = Singleton.getSingleton();
// 通過反射獲取構造函數
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
// 將構造方法設置為可訪問
constructor.setAccessible(true);
// 通過構造方法創建一個新的實例
Singleton singleton2 = constructor.newInstance();
System.out.print(singleton1 == singleton2); // false
解決方案:在單例類的構造方法中加判斷,當實例已存在的時候,不再創建新的實例
private Singleton() {
if(singleton != null) {
throw new RuntimeException("單例對象只能創建一次...");
}
}