loadClass((&#34;完全限定的类名&#34;)和<classname> .class.getDeclaredConstructors之间的区别是什么?

时间:2018-05-01 18:15:10

标签: 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行之间的区别。我可以通过两种方法获得相同的结果。

1 个答案:

答案 0 :(得分:1)

答案

没有功能的区别。正如您所发现的,有多种方法可以获取Class对象并实例化该类的实例。然而,它们都会导致相同的结果。

一般情况下,除非另有需要,否则始终:

  • 使用班级文字或getClass()获取Class
    • 示例1:Class<Foo> cl = Foo.class;
    • 示例2:Class<? extends Foo> cl = fooInstance.getClass();
  • 使用new关键字实例化实例
    • 示例:Foo f = new Foo();
    • 警告:有时API是使用Builder PatternFactory Method Pattern等设计的......如果是这种情况,那么您必须使用这些方法。在内部,构建器和工厂方法甚至可以使用new关键字。

解释和次要(?)差异

在我的脑海中,这些是我能想到的获取Class对象的方式:

  1. 使用class literals
    • Class<Foo> cl = Foo.class;
  2. 在实例上调用getClass()
    • Class<? extends Foo> cl = fooInstance.getClass();
  3. 致电Class.forName(String)
    • Class<?> cl = Class.forName("some.package.Foo");
    • 这是Class.forName("some.package.Foo", true, currentClassLoader)
    • 的简写
  4. 致电ClassLoader.loadClass(String)
    • Class<?> cl = classLoader.loadClass("some.package.Foo");
    • 不一定加载 Class。如果已加载Class,则将返回该已加载的实例。
  5. 以上所有内容都将获得代表Class的{​​{1}}对象。很有可能(我不确定 100%),方法1,2和3最终都会最终委托给方法4。

    您会注意到some.package.Foo对象(Class部分)的通用签名会有所不同,具体取决于您获取<>的方式。方法1和2知道Class在编译时的类型,因此可以返回具有相应泛型的Class。方法3和方法4不知道Class在运行时将表示什么类型,因此返回ClassClass<?>是通配符)。

    有关方法3的注意事项。如上所述,?Class.forName(String)的简写,其中Class.forName(String, boolean, ClassLoader)将是booleantrue将是是当前的ClassLoaderClassLoader参数确定boolean是否已初始化。初始化类意味着(除其他外?)初始化所有Class变量并运行static初始值设定项。因此,虽然方法1,2和4将初始化static,但方法3 。如果您不希望方法3初始化Class,则需要使用较长版本并制作Class参数boolean

    问题评论中的链接说明了为什么要使用方法3或4。

    创建实例

    再次脱离我的脑海,这些是我能想到的实例化对象的方式:

    1. 使用false关键字
      • new
    2. Using Class.newInstance()
      • Foo f = new Foo();
      • 要求该类具有无参数构造函数
      • 自Java 9以来不赞成使用Foo f = fooClass.newInstance();个对象
    3. 使用其中一个Constructor个对象
      • Constructor
    4. 这里的主要区别是每个方法如何创建一个实例。第一种方法只使用Foo f = fooClass.getConstructor().newInstance();关键字。第二种和第三种方法使用reflection。当您在编译时不知道类型时,反射很有用,但在需要之前应该避免。

      方法3使用new。由于我传递了一个空数组参数类型,因此返回的Class.getConstructor(Class<?>... paramterTypes)是一个无参数构造函数。如果类没有no-arg构造函数,则会失败。

      您使用Constructor只会返回所有构造函数,然后您选择所需的那个并调用getDeclaredConstructors()。我在3中给​​出的例子通过直接进入公共无参数构造函数来绕过这个。使用newInstance还会为您提供非公开构造函数(即getDeclaredConstructors()protectedpackage)。这可以让你调用一个你无法做到的非公开构造函数。但这只有在你有可用的权限来调用构造函数时;您需要来自任何已安装的private的权限以及(如果使用Java 9+),类所在的包必须可反射地访问(SecurityManager)到您的模块。

      一些链接