And*_*erg 33 java anonymous-class inner-classes
package com.test;
public class OuterClass {
public class InnerClass {
public class InnerInnerClass {
}
}
public class InnerClass2 {
}
//this class should not exist in OuterClass after dummifying
private class PrivateInnerClass {
private String getString() {
return "hello PrivateInnerClass";
}
}
public String getStringFromPrivateInner() {
return new PrivateInnerClass().getString();
}
}
Run Code Online (Sandbox Code Playgroud)
当javac在命令行上运行时Sun JVM 1.6.0_20,此代码生成6个.class文件:
OuterClass.class
在OuterClass $ 1.class
在OuterClass $ InnerClass.class
在OuterClass $ InnerClass2.class
在OuterClass $ $将InnerClass InnerInnerClass.class
在OuterClass $ PrivateInnerClass.class
在eclipse中运行JDT时,它只生成5个类.
OuterClass.class
$在OuterClass 1.class
OuterClass
$ InnerClass.class
OuterClass $ InnerClass2.class OuterClass $ InnerClass $ InnerInnerClass.class OuterClass
$ PrivateInnerClass.class
反编译时,OuterClass$1.class什么都不包含.这个额外的课程来自哪里,为什么会创建?
Oak*_*Oak 26
我正在使用polygenelubricants的小片段.
请记住,字节码中没有嵌套类的概念; 但是,字节码知道访问修饰符.编译器试图绕过的问题是
该方法instantiate()需要创建一个新的实例PrivateInnerClass.但是,OuterClass无权访问PrivateInnerClass构造函数(OuterClass$PrivateInnerClass将在没有公共构造函数的情况下生成为受包保护的类).
那么编译器可以做什么呢?显而易见的解决方案是更改PrivateInnerClass为具有包保护的构造函数.这里的问题是,这将允许与该类接口的任何其他代码创建一个新实例PrivateInnerClass,即使它被显式声明为私有!
为了尝试防止这种情况,javac编译器正在做一个小技巧:不是让PrivateInnerClass其他类可见的常规构造函数,而是将其保留为隐藏(实际上它根本没有定义它,但是从外面看它是一样的) .相反,它会创建一个新的构造函数,该构造函数接收特殊类型的附加参数OuterClass$1.
现在,如果你看一下instantiate(),它会调用新的构造函数.它实际上null作为第二个参数(类型OuterClass$1)发送 - 该参数仅用于指定此构造函数是应该被调用的那个.
那么,为什么要为第二个参数创建一个新类型呢?为什么不使用比方说Object?它只用于将它与常规构造函数区分开来并且null无论如何都会被传递!答案是,由于OuterClass$1OuterClass是私有的,因此合法编译器永远不会允许用户调用特殊OuterClass$PrivateInnerClass构造函数,因为OuterClass$1隐藏了一个必需的参数类型.
我猜JDT的编译器使用另一种技术来解决同样的问题.
pol*_*nts 12
我没有答案,但我能够确认,并将代码段减少到以下内容:
public class OuterClass {
private class PrivateInnerClass {
}
public void instantiate() {
new PrivateInnerClass();
}
}
Run Code Online (Sandbox Code Playgroud)
这创造了 OuterClass$1.class
Compiled from "OuterClass.java"
class OuterClass$1 extends java.lang.Object{
}
Run Code Online (Sandbox Code Playgroud)
这是javap -c为了OuterClass.class:
Compiled from "OuterClass.java"
public class OuterClass extends java.lang.Object{
public OuterClass();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public void instantiate();
Code:
0: new #2; //class OuterClass$PrivateInnerClass
3: dup
4: aload_0
5: aconst_null
6: invokespecial #3; //Method OuterClass$PrivateInnerClass."<init>":
//(LOuterClass;LOuterClass$1;)V
9: pop
10: return
}
Run Code Online (Sandbox Code Playgroud)
并为OuterClass$PrivateInnerClass:
Compiled from "OuterClass.java"
class OuterClass$PrivateInnerClass extends java.lang.Object{
final OuterClass this$0;
OuterClass$PrivateInnerClass(OuterClass, OuterClass$1);
Code:
0: aload_0
1: aload_1
2: invokespecial #1; //Method "<init>":(LOuterClass;)V
5: return
}
Run Code Online (Sandbox Code Playgroud)
如您所见,合成的构造函数接受一个OuterClass$1参数.
因此,javac创建默认构造函数以获取类型的额外参数$1,并且该默认参数的值为5: aconst_null.
我发现,$1如果满足以下任一条件,则不会创建:
public class PrivateInnerClassPrivateInnerClassnew它static嵌套等).在名为test的目录中创建以下源:
Run Code Online (Sandbox Code Playgroud)package test; public class testClass { private class Inner { } public testClass() { Inner in = new Inner(); } }从父目录编译文件
javac test/testClass.java请注意,该文件
testClass$1.class是在当前目录中创建的.不知道为什么甚至创建了这个文件,因为还有一个test/testClass$Inner.class创建.评估
该
testClass$1.class文件用于私有内部类的私有构造函数的"访问构造函数"所需的虚拟类testClass$Inner.Dissassembly显示正确记录了此类的完全限定名称,因此不清楚为什么类文件最终位于错误的目录中.
小智 7
根据polygenelubricants的答案,我猜这个神秘的类会阻止任何其他人(即外部OuterClass)实例化a OuterClass$PrivateInnerClass,因为他们无法访问OuterClass$1.
| 归档时间: |
|
| 查看次数: |
4877 次 |
| 最近记录: |