loadClass(("fully qualified class name") 和 <ClassName>.class.getDeclaredConstructors 有什么区别
what is the difference between loadClass(("fully qualified class name") and <ClassName>.class.getDeclaredConstructors
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
对象并实例化该 class 的实例。然而,它们都导致相同的结果。
一般来说,除非另有需要,否则始终:
- 使用 class 文字或
getClass()
获得 Class
- 示例 1:
Class<Foo> cl = Foo.class;
- 示例 2:
Class<? extends Foo> cl = fooInstance.getClass();
- 使用
new
关键字实例化实例
- 示例:
Foo f = new Foo();
- 警告:有时 API 是使用 Builder Pattern, the Factory Method Pattern 等设计的...如果是这种情况,那么您将不得不使用这些方法。在内部,构建器和工厂方法甚至可能使用
new
关键字。
解释和次要(?)差异
类
在我脑海中,这些是我能想到的获得 Class
对象的方法:
- 使用class literals
Class<Foo> cl = Foo.class;
- 正在实例上调用
getClass()
Class<? extends Foo> cl = fooInstance.getClass();
- 呼叫
Class.forName(String)
Class<?> cl = Class.forName("some.package.Foo");
- 这是 shorthand
Class.forName("some.package.Foo", true, currentClassLoader)
- 呼叫
ClassLoader.loadClass(String)
Class<?> cl = classLoader.loadClass("some.package.Foo");
- 不一定加载
Class
。如果 Class
已经加载,那么加载的实例将被 returned.
以上都将得到代表some.package.Foo
的Class
对象。很可能(我不是 100% 确定),方法 1、2 和 3 最终都会委托给方法 4。
您会注意到 Class
对象(<>
部分)的通用签名因获取 Class
的方式而异。方法 1 和 2 知道 Class
在编译时是什么类型,return 和 Class
也可以使用适当的泛型。而方法 3 和 4 不知道 Class
在运行时将代表什么类型,因此 return 一个 Class<?>
(?
是一个通配符)。
关于方法 3 的一些注意事项。正如我上面提到的,Class.forName(String)
是 shorthand 对于 Class.forName(String, boolean, ClassLoader)
其中 boolean
将是 true
并且ClassLoader
将是当前的 ClassLoader
。 boolean
参数决定是否初始化Class
。初始化 class 意味着(除其他事项外?)初始化所有 static
变量和 运行 static
初始化器。因此,虽然方法 1、2 和 4 将 不会 初始化 Class
,但方法 3 将 。如果您不希望方法 3 初始化 Class
,则需要使用更长的版本并使 boolean
参数 false
.
问题评论中的link说的是为什么要使用方法3或4。
创建实例
我又想到了这些实例化对象的方法:
- 使用
new
关键字
Foo f = new Foo();
- Using
Class.newInstance()
Foo f = fooClass.newInstance();
- 要求 class 有一个无参数的构造函数
- 从 Java 9 开始弃用,支持使用
Constructor
对象
- 使用
Constructor
对象之一
Foo f = fooClass.getConstructor().newInstance();
这里的主要区别是如何每个方法创建一个实例。第一种方法仅使用 new
关键字。第二和第三种方法使用reflection。当您在编译时不知道类型时,反射很有用,但在需要时应避免使用。
方法三使用Class.getConstructor(Class<?>... paramterTypes)
。由于我传递的是一个空的参数类型数组,因此 returned Constructor
是一个无参数构造函数。如果 class 没有无参数构造函数,这将失败。
您对 getDeclaredConstructors()
的使用只是 returns 所有 构造函数,然后您选择您想要的一个并调用 newInstance
。我在 3 中给出的示例通过直接使用 public 无参数构造函数来绕过这个。使用 getDeclaredConstructors()
还将为您提供 非 public 构造函数(即 protected
、package
和 private
) .这可以让您调用非 public 构造函数,否则您将无法调用。但这仅在您可以访问构造函数的情况下才可以;您需要获得任何已安装 SecurityManager
的许可,并且(如果使用 Java 9+)class 所在的包必须是反射可访问的 (opens
)到你的模块。
一些链接
- object created with “new” keyword and created with reflection
- 使用 ClassLoader 加载 class 和 Class.forName 之间的区别
- Class.forName() vs ClassLoader.loadClass() - which to use for dynamic loading? [duplicate]
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
对象并实例化该 class 的实例。然而,它们都导致相同的结果。
一般来说,除非另有需要,否则始终:
- 使用 class 文字或
getClass()
获得Class
- 示例 1:
Class<Foo> cl = Foo.class;
- 示例 2:
Class<? extends Foo> cl = fooInstance.getClass();
- 示例 1:
- 使用
new
关键字实例化实例- 示例:
Foo f = new Foo();
- 警告:有时 API 是使用 Builder Pattern, the Factory Method Pattern 等设计的...如果是这种情况,那么您将不得不使用这些方法。在内部,构建器和工厂方法甚至可能使用
new
关键字。
- 示例:
解释和次要(?)差异
类
在我脑海中,这些是我能想到的获得 Class
对象的方法:
- 使用class literals
Class<Foo> cl = Foo.class;
- 正在实例上调用
getClass()
Class<? extends Foo> cl = fooInstance.getClass();
- 呼叫
Class.forName(String)
Class<?> cl = Class.forName("some.package.Foo");
- 这是 shorthand
Class.forName("some.package.Foo", true, currentClassLoader)
- 呼叫
ClassLoader.loadClass(String)
Class<?> cl = classLoader.loadClass("some.package.Foo");
- 不一定加载
Class
。如果Class
已经加载,那么加载的实例将被 returned.
以上都将得到代表some.package.Foo
的Class
对象。很可能(我不是 100% 确定),方法 1、2 和 3 最终都会委托给方法 4。
您会注意到 Class
对象(<>
部分)的通用签名因获取 Class
的方式而异。方法 1 和 2 知道 Class
在编译时是什么类型,return 和 Class
也可以使用适当的泛型。而方法 3 和 4 不知道 Class
在运行时将代表什么类型,因此 return 一个 Class<?>
(?
是一个通配符)。
关于方法 3 的一些注意事项。正如我上面提到的,Class.forName(String)
是 shorthand 对于 Class.forName(String, boolean, ClassLoader)
其中 boolean
将是 true
并且ClassLoader
将是当前的 ClassLoader
。 boolean
参数决定是否初始化Class
。初始化 class 意味着(除其他事项外?)初始化所有 static
变量和 运行 static
初始化器。因此,虽然方法 1、2 和 4 将 不会 初始化 Class
,但方法 3 将 。如果您不希望方法 3 初始化 Class
,则需要使用更长的版本并使 boolean
参数 false
.
问题评论中的link说的是为什么要使用方法3或4。
创建实例
我又想到了这些实例化对象的方法:
- 使用
new
关键字Foo f = new Foo();
- Using
Class.newInstance()
Foo f = fooClass.newInstance();
- 要求 class 有一个无参数的构造函数
- 从 Java 9 开始弃用,支持使用
Constructor
对象
- 使用
Constructor
对象之一Foo f = fooClass.getConstructor().newInstance();
这里的主要区别是如何每个方法创建一个实例。第一种方法仅使用 new
关键字。第二和第三种方法使用reflection。当您在编译时不知道类型时,反射很有用,但在需要时应避免使用。
方法三使用Class.getConstructor(Class<?>... paramterTypes)
。由于我传递的是一个空的参数类型数组,因此 returned Constructor
是一个无参数构造函数。如果 class 没有无参数构造函数,这将失败。
您对 getDeclaredConstructors()
的使用只是 returns 所有 构造函数,然后您选择您想要的一个并调用 newInstance
。我在 3 中给出的示例通过直接使用 public 无参数构造函数来绕过这个。使用 getDeclaredConstructors()
还将为您提供 非 public 构造函数(即 protected
、package
和 private
) .这可以让您调用非 public 构造函数,否则您将无法调用。但这仅在您可以访问构造函数的情况下才可以;您需要获得任何已安装 SecurityManager
的许可,并且(如果使用 Java 9+)class 所在的包必须是反射可访问的 (opens
)到你的模块。
一些链接
- object created with “new” keyword and created with reflection
- 使用 ClassLoader 加载 class 和 Class.forName 之间的区别
- Class.forName() vs ClassLoader.loadClass() - which to use for dynamic loading? [duplicate]