Java 调用类构造函数和使用class.forName().newInstance之间的区别
我一直在试图理解使用Java 调用类构造函数和使用class.forName().newInstance之间的区别,java,reflection,Java,Reflection,我一直在试图理解使用new实例化对象与使用Class.forName(“A”).newInstance()实例化对象之间的区别 我为一个简单的类a运行了以下代码,其中显示了使用class.forname(“a”).newInstance()比仅使用newa()慢70-100倍 我很想知道为什么时间上会有如此大的差异,但我不明白。请有人帮我理解原因 public class Main4test { public Main4test() { } static int tu
new
实例化对象与使用Class.forName(“A”).newInstance()实例化对象之间的区别代码>
我为一个简单的类a
运行了以下代码,其中显示了使用class.forname(“a”).newInstance()
比仅使用newa()
慢70-100倍
我很想知道为什么时间上会有如此大的差异,但我不明白。请有人帮我理解原因
public class Main4test {
public Main4test() {
}
static int turns = 9999999;
public static void main(String[] args) {
new Main4test().run();
}
public void run() {
System.out.println("method1: " + method1() + "");
System.out.println("method2:" + method2() + "");
}
public long method2() {
long t = System.currentTimeMillis();
for (int i = 0; i < turns; i++) {
try {
A a = (A) Class.forName("A").newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return System.currentTimeMillis() - t;
}
public long method1() {
long t = System.currentTimeMillis();
for (int i = 0; i < turns; i++) {
try {
A a = new A();
} catch (Exception e) {
e.printStackTrace();
}
}
return System.currentTimeMillis() - t;
}
}
public class A {
int a;
public A() {
a=0;
}
}
公共类Main4test{
公共维护测试(){
}
静态整数圈数=9999999;
公共静态void main(字符串[]args){
新建Main4test().run();
}
公开募捐{
System.out.println(“method1:+method1()+”);
System.out.println(“method2:+method2()+”);
}
公共长方法2(){
long t=System.currentTimeMillis();
对于(int i=0;i
直接调用new
运算符和A
的构造函数,其中A
在源文本中提到,因此已加载并初始化
A a = (A) Class.forName("A").newInstance();
- 查看是否已加载
A
- 如有必要,将其加载
- 如有必要,初始化它
- 通过反射找到无参数构造函数
- 通过反射调用
new
操作符和no-args构造函数
- 将结果键入
A
使用反射Class.forName(“A”).newInstance()
不仅是一个代价高昂的操作(因为需要在运行时将正确的类加载器委托给类并加载该类),而且会使代码更难调试,并且会失去类型安全的所有优势(在编译期间发生)
结论:
避免反射,除非您必须使用它(例如,如果您正在编写面向方面的插件/库)用于测量速度的测试不是有效的测试。Java性能非常复杂,它首先涉及热点vm、智能编译器和垃圾收集器
在方法1中,java通常足够聪明,只在内存中创建一个实例,并在每次迭代中重用它
在方法2中,您强制VM使用反射和类加载器来生成对象。
这本身就已经慢了传统的new
和newInstance
之间的主要区别在于,newInstance允许灵活地实例化运行时才知道的类,并使代码更具动态性。如果直到运行时才知道该类,则应该使用反射
从中,调用Class.forName(String)
返回与具有给定字符串名称的类或接口关联的类对象,即返回类A
因此A=(A)Class.forName(“A”).newInstance()
分解为:
Class.forName(“A”)
返回类的类型A
Class.forName(“A”).newInstance()
创建由该类对象表示的类的新实例,从而获得类型a的实例。该类被实例化,就好像是由一个带有空参数列表的新表达式实例化一样。如果尚未初始化该类,则初始化该类。这实际上相当于一个新的a(),它返回a的一个新实例。
重要提示:使用此方法可以有效地绕过编译器执行的编译时异常检查
参考:
下面列出了new
操作符和newInstance
方法之间的区别:
- 通过传递构造函数接受的任意数量的参数,new运算符可以与类的任何构造函数一起使用。
newInstance方法要求在为其调用的类中不存在任何参数构造函数。如果要将构造函数与newInstance一起使用,则需要为任何构造函数获取构造函数类的实例,然后调用newInstance,如:
Class class = Class.forName("A");
Constructor const = class.getConstructor(A.class);
A a = null;
const.newInstance(a);
- 使用new操作符不需要显式加载类,因为它是由JVM在内部处理的。
对于newInstance()方法,该类的类对象的实例是必需的(class.forName(“A”).newInstance();或如上所示)。引用基础类的类对象是通过调用forName方法获得的
- 当类的名称在编译时已知时,建议使用new运算符。
由于newInstance使用反射来创建类的对象,因此建议在编译时不知道该类,但在运行时确定该类时使用
- 由于并没有像new操作符中的forName这样的与方法调用相关的额外处理,所以它比newInstance更快。
使用newInstance会导致JVM部分的额外处理(类型检查、安全检查),因此不建议将其用于per
Class class = Class.forName("A");
Constructor const = class.getConstructor(A.class);
A a = null;
const.newInstance(a);