Class物件
虛擬機器在class檔案的載入階段,把類資訊儲存在方法區資料結構中,並在Java堆中生成一個Class物件,作為類資訊的入口。
宣告兩個類,Cat.java
和 Dog.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Cat { private String name; private int age; static { System.out.println("Cat is load"); } } class Dog { private String name; private int age; static { System.out.println("Dog is load"); } } |
獲取Class物件一般有三種方式:
- 通過例項變數方式
123456public class test {public static void main(String[] args) {Dog dog = new Dog();Class clazz = dog.getClass();}} - 通過類名方式
12345public class test {public static void main(String[] args) {Class clazz = Dog.class;}}
通過這種方式時,只會載入Dog類,並不會觸發其類構造器的初始化。 - 通過Class.forName(String classname)方式
1234567public class ClassTest {public static void main(String[] args) {try {Class clazz = Class.forName("zzzzzz.Dog");} catch (ClassNotFoundException e) {}}}
在JDK原始碼實現中,forName方法會呼叫Native方法forName0(),它在JVM中呼叫findClassFromClassLoader()載入Dog類,其原理和ClassLoader一樣,將會觸發Dog類的類構造器初始化,forName0方法宣告如下:
1private static native Class<?> forName0(String name, boolean initialize, ClassLoader loader, Class> caller)
其中initialize引數,用來告訴虛擬機器是否需要對載入的類進行初始化,如果initialize為false,則不會進行初始化Dog類。
1Class clazz = Class.forName("zzzzzz.Dog", false, Dog.class.getClassLoader());
反射機制
反射機制reflect可以在執行期間獲取類的欄位、方法、父類和介面等資訊。
1、獲取類欄位
1 2 3 4 5 |
Class class_dog = Dog.class; Field[] fields = class_dog.getDeclaredFields(); for (Field field : fields) { System.out.println(field.getName()); } |
2、獲取類方法
1 2 3 4 5 |
Class class_dog = Dog.class; Method[] methods = class_dog.getDeclaredMethods(); for (Method method : methods) { System.out.println(method); } |
通過method.invoke(obj, ...args)
可以呼叫obj例項的method方法。
3、獲取對應的例項構造器,並生成類例項
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class ClassTest { public static void main(String[] args) throws NoSuchMethodException { Class class_dog = Dog.class; Constructor constructor = class_dog.getConstructor(String.class, int.class); constructor.newInstance("Tom", 10); } } class Dog { private String name; private int age; public Dog(String name, int age) { this.name = name; this.age = age; } } |
如果沒有顯示的宣告預設構造器,class_dog.getConstructor()會丟擲NoSuchMethodException異常。
4、通過newInstance()方法生成類例項
1 2 |
Class class_dog = Dog.class; Dog dog = class_dog.newInstance(); |
5、設定私有變數
1 2 3 4 5 |
Class class_dog = Dog.class; Field name = class_dog.getDeclaredField("name"); name.setAccessible(true); Dog dog = (Dog) class_dog.newInstance(); name.set(dog, "Tom"); |
6、獲取私有變數
1 2 3 |
Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); return (Unsafe)f.get(null); |
這種方式在使用Unsafe類進行黑魔法時經常用到。
反射的效能問題
Stackoverflow上,很多人覺得使用反射reflect會影響系統效能,主要有以下幾點看法:
1、程式碼的驗證防禦邏輯過於複雜,本來這塊驗證時在連結階段實現的,使用反射reflect時需要在執行時進行;
2、產生過多的臨時物件,影響GC的消耗;
3、由於缺少上下文,導致不能進行更多的優化,如JIT;
不過現代JVM已經執行的足夠快,我們應該把主要重心放在複雜的程式碼邏輯上,而不是一開始就進行各種效能優化。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!