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行之间的区别。我可以通过两种方法获得相同的结果。
答案 0 :(得分:1)
没有功能的区别。正如您所发现的,有多种方法可以获取Class
对象并实例化该类的实例。然而,它们都会导致相同的结果。
一般情况下,除非另有需要,否则始终:
getClass()
获取Class
Class<Foo> cl = Foo.class;
Class<? extends Foo> cl = fooInstance.getClass();
new
关键字实例化实例
Foo f = new Foo();
new
关键字。 在我的脑海中,这些是我能想到的获取Class
对象的方式:
Class<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
的{{1}}对象。很有可能(我不确定 100%),方法1,2和3最终都会最终委托给方法4。
您会注意到some.package.Foo
对象(Class
部分)的通用签名会有所不同,具体取决于您获取<>
的方式。方法1和2知道Class
在编译时的类型,因此可以返回具有相应泛型的Class
。方法3和方法4不知道Class
在运行时将表示什么类型,因此返回Class
(Class<?>
是通配符)。
有关方法3的注意事项。如上所述,?
是Class.forName(String)
的简写,其中Class.forName(String, boolean, ClassLoader)
将是boolean
而true
将是是当前的ClassLoader
。 ClassLoader
参数确定boolean
是否已初始化。初始化类意味着(除其他外?)初始化所有Class
变量并运行static
初始值设定项。因此,虽然方法1,2和4将不初始化static
,但方法3 将。如果您不希望方法3初始化Class
,则需要使用较长版本并制作Class
参数boolean
。
问题评论中的链接说明了为什么要使用方法3或4。
再次脱离我的脑海,这些是我能想到的实例化对象的方式:
false
关键字
new
Class.newInstance()
Foo f = new Foo();
Foo f = fooClass.newInstance();
个对象 Constructor
个对象
Constructor
这里的主要区别是每个方法如何创建一个实例。第一种方法只使用Foo f = fooClass.getConstructor().newInstance();
关键字。第二种和第三种方法使用reflection。当您在编译时不知道类型时,反射很有用,但在需要之前应该避免。
方法3使用new
。由于我传递了一个空数组参数类型,因此返回的Class.getConstructor(Class<?>... paramterTypes)
是一个无参数构造函数。如果类没有no-arg构造函数,则会失败。
您使用Constructor
只会返回所有构造函数,然后您选择所需的那个并调用getDeclaredConstructors()
。我在3中给出的例子通过直接进入公共无参数构造函数来绕过这个。使用newInstance
还会为您提供非公开构造函数(即getDeclaredConstructors()
,protected
和package
)。这可以让你调用一个你无法做到的非公开构造函数。但这只有在你有可用的权限来调用构造函数时;您需要来自任何已安装的private
的权限以及(如果使用Java 9+),类所在的包必须可反射地访问(SecurityManager
)到您的模块。