如果我有一个内部类声明,例如:
Class A {
public static class B {
}
}
接下来是:
Class<?> implClass = getClass().getClassLoader().loadClass("A");
A $ B内部类也会被加载吗? 如果B内部类没有被声明为&#34;静态&#34; ?
答案 0 :(得分:27)
编译代码后,就没有内部类。如果您查看javac
的结果,您会看到两个文件:
A.class
A$B.class
加载B
时未加载课程A
,B
仅在<{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
请注意
package kuporific;
import kuporific.A;
class A$C {
private A$C(A this$0) {
this.this$0 = this$0;
}
}
没有对其父级A$B
的引用,A
也是如此。这是因为前者是静态内部类,而后者不是,和A$C
和A$B
现在都是包私有类。这是非静态内部类能够直接引用其父实例的字段和方法,反之亦然。 (内部类中引用的父类的任何私有字段也都是私有的。)
接下来,让我们看看加载类A$C
对A
和A$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.A
或kuporific.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?)指出的那样,加载一个类与初始化类不同。