Class對象
虛擬機在class文件的加載階段,把類信息保存在方法區數據結構中,並在Java堆中生成一個Class對象,作為類信息的入口。
聲明兩個類,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對象一般有三種方式:
- 通過實例變量方式
public class test {
public static void main(String[] args) {
Dog dog = new Dog();
Class clazz = dog.getClass();
}
}
- 通過類名方式
public class test {
public static void main(String[] args) {
Class clazz = Dog.class;
}
}
通過這種方式時,只會加載Dog類,並不會觸發其類構造器的初始化。
- 通過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());
反射機制
反射機制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已經運行的足夠快,我們應該把主要重心放在複雜的代碼邏輯上,而不是一開始就進行各種性能優化。
閱讀更多 程序猿的內心獨白 的文章