java - loadClass((“完全合格的类名称”)和<ClassName> .class.getDeclaredConstructors之间有什么区别

标签 java data-structures java-stream classloader

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 PatternFactory Method Pattern等设计API。如果是这种情况,则必须使用这些方法。在内部,构建器和工厂方法甚至可以使用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");
这是Class.forName("some.package.Foo", true, currentClassLoader)的简写

呼叫ClassLoader.loadClass(String)


Class<?> cl = classLoader.loadClass("some.package.Foo");
不一定加载Class。如果Class已经加载,则将返回该加载的实例。



上面所有这些都会得到代表Classsome.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将是当前的ClassLoaderboolean参数确定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()也将为您提供非公共构造函数(即protectedpackageprivate)。这可以允许您调用非公开的构造函数,否则您将无法进行构造。但这仅在您有权调用构造函数的情况下;您将需要获得所有已安装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/

相关文章:

c# - System.Collections.ArrayList 有哪些替代方案?

Java8 如何使用流和 lambda 将 3 级嵌套列表转换为嵌套的 HashMap

如果系统属性值包含空格,java getInputArguments() 返回尴尬的结果

java - 如何从二维数组中获取特定行?

java - Java 中 HashMap 的用处

java | Selenium |使用 Stream 从 Web 元素列表中返回单个 Web 元素

Java:流中的消费者接口(interface)无法按预期工作

java - 具有 super 泛型的代码无法按预期工作

java - 调用静态方法时创建静态类变​​量 - Java

c++ - 错误 : cannot convert parameters from 'int [10]' to 'int'