class testMe{
void show(){
System.out.println("Hello");
}
}
public class ClassloadersExample {
public static void main(String args[]) {
ClassLoader c = ClassloadersExample.class.getClassLoader(); //Line 1
try {
Class c1 = c.loadClass("test.testMe"); // Line 2
Constructor a[] = c1.getDeclaredConstructors();
for (Constructor constructor : a) {
testMe m = (testMe)constructor.newInstance();
m.show();
}
Constructor con[] = testMe.class.getDeclaredConstructors(); // Line 6
for (Constructor constructor : con) {
constructor.setAccessible(true);
testMe t = (testMe)constructor.newInstance();
t.show();
}
}
catch(Exception e){
System.out.println("exception");
}
}
}
我正在测试上面的代码。两者都给我相同的结果。我试图理解第1,2行和第6行之间的区别。两种方法都可以达到相同的结果。
最佳答案
回答
没有功能上的差异。正如您所发现的,有多种方法可以获取Class
对象并实例化该类的实例。但是,它们都导致相同的结果。
通常,除非另有需要,否则始终:
使用类文字或getClass()
获取Class
示例1:Class<Foo> cl = Foo.class;
示例2:Class<? extends Foo> cl = fooInstance.getClass();
使用new
关键字实例化实例
示例:Foo f = new Foo();
注意:有时使用Builder Pattern,Factory Method Pattern等设计API。如果是这种情况,则必须使用这些方法。在内部,构建器和工厂方法甚至可以使用new
关键字。
解释和次要(?)差异
班级
从我的头顶上,这些是我想到的获取Class
对象的方法:
使用class literalsClass<Foo> cl = Foo.class;
在实例上调用getClass()
Class<? extends Foo> cl = fooInstance.getClass();
呼叫Class.forName(String)
Class<?> cl = Class.forName("some.package.Foo");
这是Class.forName("some.package.Foo", true, currentClassLoader)
的简写
呼叫ClassLoader.loadClass(String)
Class<?> cl = classLoader.loadClass("some.package.Foo");
不一定加载Class
。如果Class
已经加载,则将返回该加载的实例。
上面所有这些都会得到代表Class
的some.package.Foo
对象。在所有可能性下(我不确定100%肯定),方法1、2和3最终最终都委托给方法4。
您会注意到,Class
对象(<>
部分)的通用签名根据获取Class
的方式而有所不同。方法1和2知道Class
在编译时将是哪种类型,因此可以返回带有适当泛型的Class
。而方法3和4不知道Class
在运行时代表什么类型,因此返回Class<?>
(?
是通配符)。
关于方法3的注意事项。如上所述,Class.forName(String)
是Class.forName(String, boolean, ClassLoader)
的简写,其中boolean
将是true
,而ClassLoader
将是当前的ClassLoader
。 boolean
参数确定Class
是否初始化。初始化类意味着(除其他事项外)初始化所有static
变量并运行static
初始化程序。因此,虽然方法1、2和4不会初始化Class
,但方法3会初始化。如果您不希望方法3初始化Class
,则需要使用更长的版本,并设置boolean
参数false
。
问题注释中的链接说明了为什么要使用方法3或4。
创建实例
再次浮出水面,这些是我想到的实例化对象的方法:
使用new
关键字Foo f = new Foo();
Using Class.newInstance()
Foo f = fooClass.newInstance();
要求该类具有无参数构造函数
自Java 9以来不赞成使用Constructor
对象
使用Constructor
对象之一Foo f = fooClass.getConstructor().newInstance();
这里的主要区别是每种方法如何创建实例。第一种方法只是使用new
关键字。第二和第三种方法使用reflection。当您在编译时不知道类型时,反射很有用,但在需要时应避免使用。
方法3使用Class.getConstructor(Class<?>... paramterTypes)
。由于我传递了一个空的参数类型数组,因此返回的Constructor
是一个无参数的构造函数。如果该类没有no-arg构造函数,则将失败。
使用getDeclaredConstructors()
仅返回所有构造函数,然后选择所需的构造函数并调用newInstance
。我在3个示例中给出的示例绕过了这一点,直接使用了公共的无参数构造函数。使用getDeclaredConstructors()
也将为您提供非公共构造函数(即protected
,package
和private
)。这可以允许您调用非公开的构造函数,否则您将无法进行构造。但这仅在您有权调用构造函数的情况下;您将需要获得所有已安装SecurityManager
的许可之类的东西,并且(如果使用Java 9+,则)该类所在的程序包必须对模块可以反射访问(opens
)。
一些链接
object created with “new” keyword and created with reflection
Difference betweeen Loading a class using ClassLoader and Class.forName
Class.forName() vs ClassLoader.loadClass() - which to use for dynamic loading? [duplicate]
Class.forname(“name”).newInstance() vs name.class.newInstance : Difference in usage perspective
关于java - loadClass((“完全合格的类名称”)和<ClassName> .class.getDeclaredConstructors之间有什么区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50121630/