Java ClassLoader是否加载内部类?

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

Class A { public static class B { } } 

其次是:

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

A $ B内部类也会加载吗? 如果B内部类没有被声明为“静态”怎么办?

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

 A.class A$B.class 

因此,在加载A时不加载B类, B 恰好A定义。


编辑

例如,给定这两个文件,

 package kuporific; public class A { private static class B {} private class C {} } 

build.gradle文件(为方便起见):

 apply plugin: 'java' 

首先,通过运行gradle build 。 然后,解压缩生成的JAR文件(位于build/libs ):

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

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

  • 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

     package kuporific; import kuporific.A; class A$C { private A$C(A this$0) { this.this$0 = this$0; } } 

请注意

  1. A$B没有对其父A的引用,而A$C则没有。 这是因为前者是静态内部类,而后者不是,而且
  2. A$BA$C现在都是包私有类。

这就是非静态内部类能够直接引用其父实例的字段和方法的方式,反之亦然。 (内部类中引用的父类的任何私有字段也都是私有的。)

接下来,让我们看看加载A类对A$BA$C

首先,添加以下Java类:

 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"] 

-verbose:class输出JVM加载的所有类(请参阅Java – 获取 JVM中加载的所有类的列表 )。

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

 :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.Mainkuporific.A被加载时,我们看不到kuporific.A$Bkuporific.A$C正在加载。

内部类即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 }); 

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

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

由于您的A类不引用AB ,因此不会加载AB ,无论它是否为静态。 (老实说, A确实引用了AB ,但没有引起ClassLoader加载AB 。)

如果添加AB类型的字段或以另一种方式使用AB类型(例如,作为方法返回类型),它实际上将在A.class引用并因此被加载。

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

 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 – 运行程序时初始化’Outer’。 静态变量TEST01在静态块之前初始化。 内部未初始化。

2 – 调用’main’方法并显示’TEST01’的值; 然后,

3 – System.out显示对’Inner’的引用。 Inner未初始化 ,但已加载(这就是它在内存模型中有引用的原因)。

4 – 这是最有趣的部分。 因为System.out需要访问’Inner’(Inner.info())中的’info()’方法,所以应该返回’info()’方法的结果之前初始化’Inner’类。 这就是为什么这是第4步。

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

因此,@ sotirios-delimanolis( Java ClassLoader是否加载内部类? )加载类与初始化类不同。