Java反射中获取Class有三种方式:
- new Object().getClass
- Object.class
- Class.forName("com.sherlock.demo")
以下就详细的讲讲这三种方式的区别
首先我们先定义一个Demo类,其中定义了静态代码块、动态代码块和构造函数
public class Demo {
static {
System.out.println("Demo:静态代码块");
}
{
System.out.println("Demo:动态代码块");
}
public Demo(){
System.out.println("Demo:构造方法");
}
}
分别对这三种场景进行测试
public class Test {
@org.junit.jupiter.api.Test
public void test1(){
Class<?> clz = Demo.class;
}
@org.junit.jupiter.api.Test
public void test2() throws ClassNotFoundException {
Class<?> clz = Class.forName("com.sherlock.Demo");
}
@org.junit.jupiter.api.Test
public void test3() {
Class<?> clz = new Demo().getClass();
}
}
- 第一种方式下,没有任何的输出,说明通过Demo.class获取对象的Class对象,不会调用任何的代码块和代码。
- 第二种方式下,会打印:Demo:静态代码块,说明在这种方式下会调用静态代码块的内容。
- 第三种方式下,会打印:Demo:静态代码块
Demo:动态代码块
Demo:构造方法
也就是说,在这种方式下,因为要先实例化对象,所以会打印全部的内容。
接下来我们同时使用这三种方式
@org.junit.jupiter.api.Test
public void test4() throws ClassNotFoundException {
Class<?> clz = Demo.class;
System.out.println("---------------");
clz = Class.forName("com.sherlock.Demo");
System.out.println("---------------");
clz = new Demo().getClass();
}
打印结果
打印结果.jpg
使用Demo.class还是和上面一样,不会调用任何的代码或代码块。使用Class.forName("com.sherlock.Demo")会调用静态代码块。但是在使用new Demo().getClass()时,却没有再次调用静态代码块,说明了静态代码块只会被初始化一次。
再来比较一下这三种情况下获得的对象是不是同一个
@org.junit.jupiter.api.Test
public void test6() throws ClassNotFoundException {
Class<?> clz1 = Demo.class;
Class<?> clz2 = Class.forName("com.sherlock.Demo");
Class<?> clz3 = new Demo().getClass();
System.out.println(clz1 == clz2);
System.out.println(clz2 == clz3);
}
可以发现比较的结果都是true,说明获取的是同一个对象。这是因为对于同一个Classloader对象,不管某个类被加载多少次,对应堆内存中的class对象始终只有一个。也就是说无论通过哪种形式来获取Class对象,获得的都是堆内存中对应的Class对象。
总结一下:
(1)类名.class:JVM将使用类装载器,将类装入内存(前提是:类还没有装入内存),不做类的初始化工作,返回Class的对象。
(2)Class.forName(“类的全限定名”):装入类,并做类的静态初始化,返回Class的对象。
(3)实例对象.getClass():对类进行静态初始化、非静态初始化;返回引用运行时真正所指的对象(子对象的引用会赋给父对象的引用变量中)所属的类的Class的对象。
原文链接:https://www.choupangxia.com/2020/03/27/java-reflect-class/
网友评论