Java ClassLoader是否加载内部类?

时间:2014-07-02 18:31:31

标签: java classloader

如果我有一个内部类声明,例如:

Class A {
    public static class B {
    }
}

接下来是:

Class<?> implClass = getClass().getClassLoader().loadClass("A");

A $ B内部类也会被加载吗? 如果B内部类没有被声明为&#34;静态&#34; ?

5 个答案:

答案 0 :(得分:27)

编译代码后,就没有内部类。如果您查看javac的结果,您会看到两个文件:

A.class
A$B.class

加载B时未加载课程AB仅在<{1}}中定义发生


修改

例如,给定这两个文件,

A

package kuporific; public class A { private static class B {} private class C {} } 文件(为方便起见):

build.gradle

首先,通过运行apply plugin: 'java' 来构建。然后,解压缩生成的JAR文件(位于gradle build):

build/libs

打开每个文件(例如,在IntelliJ中),显示编译器已完成的操作:

  • ├── META-INF │   └── MANIFEST.MF └── kuporific ├── A$B.class ├── A$C.class └── A.class

    A.class
  • package kuporific; public class A { public A() { } private class C { public C() { } } private static class B { public B() { } } }

    A$B.class
  • package kuporific; class A$B { private A$B() { } }

    A$C.class

请注意

  1. package kuporific; import kuporific.A; class A$C { private A$C(A this$0) { this.this$0 = this$0; } } 没有对其父级A$B的引用,A也是如此。这是因为前者是静态内部类,而后者不是,和
  2. A$CA$B现在都是包私有类。
  3. 这是非静态内部类能够直接引用其父实例的字段和方法,反之亦然。 (内部类中引用的父类的任何私有字段也都是私有的。)

    接下来,让我们看看加载类A$CAA$B的影响。

    首先,添加以下Java类:

    A$C

    现在将以下内容添加到package kuporific; public class Main { public static void main(String[] args) throws ClassNotFoundException { Main.class.getClassLoader().loadClass("kuporific.A"); } } 文件中:

    build.gradle

    apply plugin: 'application' mainClassName = 'kuporific.Main' applicationDefaultJvmArgs = ["-verbose:class"] 输出JVM加载的所有类(参见Java - Get a list of all Classes loaded in the JVM)。

    在命令行上运行-verbose:class(运行gradle run方法main);输出(带有我添加的注释)是

    Main

    我们可以看到加载:compileJava :processResources UP-TO-DATE :classes :run [Opened /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] [Loaded java.lang.Object from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] # Lots of omitted output... [Loaded kuporific.Main from file:/tmp/build/classes/main/] ^ here! [Loaded sun.launcher.LauncherHelper$FXHelper from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] [Loaded java.lang.Class$MethodArray from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] [Loaded kuporific.A from file:/tmp/build/classes/main/] ^ here! [Loaded java.lang.Shutdown from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] [Loaded java.lang.Shutdown$Lock from /Library/Java/JavaVirtualMachines/jdk1.8.0_20.jdk/Contents/Home/jre/lib/rt.jar] BUILD SUCCESSFUL Total time: 6.502 secs kuporific.Main的时间,我们看不到加载kuporific.Akuporific.A$B

答案 1 :(得分:4)

内部类,即class B不能存在于父类之外。您需要首先构造父类,即class A

如果从内部类中删除静态,即非静态内部类,则需要在构造内部类时传递父类。

Object a = Class.forName("A").newInstance();    //object of outer class

//object of inner class
Object b = implClass.getDeclaredConstructor(new Class[] { a.getClass() })
        .newInstance(new Object[] { a });

答案 2 :(得分:1)

不,在任何一种情况下都不会加载嵌套类。

答案 3 :(得分:1)

除非已请求,否则ClassLoader将不会加载类(例如,使用loadClass)。在加载类时,ClassLoader将请求引用的类。

由于您的班级A未引用A.B,因此无法加载A.B,无论是否为静态。 (说实话,A确实引用了A.B,但没有引起ClassLoader加载A.B的方式。)

如果添加A.B类型的字段或以其他方式使用类型A.B(例如作为方法返回类型),它实际上将在A.class中引用,因此加载。

答案 4 :(得分:1)

以下代码是可运行的,可以说明其他一些答案:

public class Outer
{

   private static final String TEST01 = "I'm TEST01";

   static
   {
        System.out.println("1 - Initializing class Outer, where TEST01 = " + TEST01);
   }

   public static void main(String[] args)
   {
       System.out.println("2 - TEST01       --> " + TEST01 );
       System.out.println("3 - Inner.class  --> " + Inner.class);
       System.out.println("5 - Inner.info() --> " + Inner.info() );
   }

   private static class Inner
   {

       static
       {
          System.out.println("4 - Initializing class Inner");
       }

       public static String info()
       {
           return "I'm a method in Inner";
       }
    }
}

请注意序列号,特别是在这一行:

System.out.println("5 - Inner.info() --> " + Inner.info() );

运行程序时,您将在控制台上看到以下结果:

1 - Initializing class Outer, where TEST01 = I'm TEST01
2 - TEST01       --> I'm TEST01
3 - Inner.class  --> class com.javasd.designpatterns.tests.Outer$Inner
4 - Initializing class Inner
5 - Inner.info() --> I'm a method in Inner

每个步骤的更多细节:

1 - &#39;外面&#39;在运行程序时初始化。静态变量TEST01在静态块之前初始化。内部未初始化。

2 - &#39; main&#39;调用方法并显示&#39; TEST01&#39;的值;然后,

3 - System.out显示对&#39; Inner&#39;的引用。 内部未初始化,但已加载(这就是它在内存模型中引用的原因)。

4 - 这是最有趣的部分。因为System.out需要访问&#39; info()&#39;内在方法&#39; (Inner.info()),&#39; Inner&#39; class应该在之前初始化返回&#39; info()&#39;的结果。方法。这就是为什么这是第4步。

5 - 最后,System.out包含它需要显示的所有数据,然后在控制台上显示最后一行。

所以,正如@ sotirios-delimanolis(Does the Java ClassLoader load inner classes?)指出的那样,加载一个类与初始化类不同。