十年程序員說說不一樣的Java反射機制

Class對象

虛擬機在class文件的加載階段,把類信息保存在方法區數據結構中,並在Java堆中生成一個Class對象,作為類信息的入口。

十年程序員說說不一樣的Java反射機制

聲明兩個類,Cat.java 和 Dog.java

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對象一般有三種方式:

  1. 通過實例變量方式
public class test {
public static void main(String[] args) {
Dog dog = new Dog();
Class clazz = dog.getClass();
}
}
  1. 通過類名方式
public class test {
public static void main(String[] args) {
Class clazz = Dog.class;
}
}

通過這種方式時,只會加載Dog類,並不會觸發其類構造器的初始化。

  1. 通過Class.forName(String classname)方式
public 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方法聲明如下:

private static native Class> forName0(String name, boolean initialize, ClassLoader loader, Class> caller)

其中initialize參數,用來告訴虛擬機是否需要對加載的類進行初始化,如果initialize為false,則不會進行初始化Dog類。

Class clazz = Class.forName("zzzzzz.Dog", false, Dog.class.getClassLoader());
十年程序員說說不一樣的Java反射機制

反射機制

反射機制reflect可以在運行期間獲取類的字段、方法、父類和接口等信息。

1、獲取類字段

Class class_dog = Dog.class;
Field[] fields = class_dog.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}

2、獲取類方法

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、獲取對應的實例構造器,並生成類實例

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()方法生成類實例

Class class_dog = Dog.class;
Dog dog = class_dog.newInstance();

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、獲取私有變量

Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
return (Unsafe)f.get(null);

這種方式在使用Unsafe類進行黑魔法時經常用到。

反射的性能問題

Stackoverflow上,很多人覺得使用反射reflect會影響系統性能,主要有以下幾點看法:

1、代碼的驗證防禦邏輯過於複雜,本來這塊驗證時在鏈接階段實現的,使用反射reflect時需要在運行時進行;

2、產生過多的臨時對象,影響GC的消耗;

3、由於缺少上下文,導致不能進行更多的優化,如JIT;

不過現代JVM已經運行的足夠快,我們應該把主要重心放在複雜的代碼邏輯上,而不是一開始就進行各種性能優化。

十年程序員說說不一樣的Java反射機制


分享到:


相關文章: