次のような内部クラス宣言がある場合:
Class A {
public static class B {
}
}
に続く:
Class<?> implClass = getClass().getClassLoader().loadClass("A");
A $ B内部クラスもロードされますか? B内部クラスが「静的」として宣言されていない場合はどうなりますか?
コードがコンパイルされると、内部クラスのようなものはありません。 javac
の結果を見ると、次の2つのファイルがあります。
A.class
A$B.class
したがって、B
がロードされると、クラスA
はロードされず、B
はhappensが定義されます。 A
で。
たとえば、これら2つのファイルがある場合、
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;
}
}
そのことに注意してください
A$B
には親A
への参照がありませんが、A$C
には参照があります。これは、前者が静的内部クラスであり、後者がそうではないためです。A$B
とA$C
の両方がパッケージプライベートクラスになりました。これは、非静的内部クラスが親インスタンスのフィールドとメソッドを直接参照できる方法であり、その逆も同様です。 (内部クラスで参照される親クラスのプライベートフィールドもパッケージプライベートになります。)
次に、クラスA
の読み込みがA$B
とA$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
を実行します(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
もkuporific.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 });
以下のコードは実行可能であり、他のいくつかの答えを説明できます。
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-プログラムを実行すると、「外部」が初期化されます。静的変数TEST01は、静的ブロックの前に初期化されます。内部は初期化されていません。
2-「main」メソッドが呼び出され、「TEST01」の値が表示されます。その後、
3-System.outは「Inner」への参照を示しています。 内部は初期化されていませんが、ロードされました(そのため、メモリモデルに参照があります)。
4-これが最も興味深い部分です。 System.outは「Inner」(Inner.info())の「info()」メソッドにアクセスする必要があるため、「Inner」クラスはbefore 'info()'メソッドの結果を返します。これがステップ4である理由です。
5-最後に、System.outには表示する必要のあるすべてのデータがあり、最後の行がコンソールに表示されます。
したがって、@ sotirios-delimanolis( Java ClassLoaderは内部クラスをロードしますか? )によってよく指摘されているように、クラスのロードは初期化とは異なります。
ClassLoader
は、要求されない限り(たとえば、loadClass
を使用して)、クラスをロードしません。クラスのロード中に、ClassLoader
は参照されるクラスを要求します。
クラスA
はA.B
を参照しないため、静的であるかどうかに関係なく、A.B
はロードされません。 (正直なところ、A
はA.B
を参照しますが、ClassLoader
がA.B
をロードするような方法ではありません。)
タイプA.B
のフィールドを追加するか、タイプA.B
を別の方法で(たとえば、メソッドの戻り値の型として)使用すると、実際にはA.class
で参照されるため、ロードされます。
いいえ、どちらの場合も、ネストされたクラスはロードされません。