即使我们看不到任何可见的引用,它仍然存在。请参见下面的代码。
package jetty;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.Arrays;
public class SOExample2 {
private static Object staticField = new Object () { };
private Object nonStaticField = new Object () { };
private static Object getAnonStatic() {
return new Object() { };
}
private Object getAnonNonStatic() {
return new Object() { };
}
public static void main(String[] args) throws NoSuchFieldException, SecurityException {
System.out.println("Started");
class StaticMethodLocal {
}
System.out.println("############## Fields ##############");
printClassInfo(staticField.getClass());
printClassInfo(new SOExample2().nonStaticField.getClass());
System.out.println("############## Methods ##############");
printClassInfo(getAnonStatic().getClass());
printClassInfo(new SOExample2().getAnonNonStatic().getClass());
System.out.println("############## Method Local ##############");
printClassInfo(new StaticMethodLocal().getClass());
printClassInfo(new SOExample2().getNonStaticMethodLocal().getClass());
}
public static <T>void printClassInfo(Class<T> klass) {
System.out.println("Class : " + klass);
String prefix = "\t";
System.out.println(prefix + "Number fields : " + klass.getDeclaredFields().length);
if(klass.getDeclaredFields().length > 0) {
System.out.println(prefix + "fields : " + Arrays.toString(klass.getDeclaredFields()));
} else {
System.out.println(prefix + "no fields");
}
System.out.println(prefix + "modifiers : " + Modifier.toString(klass.getModifiers()));
Constructor<?>[] constructors = klass.getDeclaredConstructors();
for(Constructor<?> constructor : constructors) {
System.out.println(prefix + "constructor modifiers : " + Modifier.toString(constructor.getModifiers()));
System.out.println(prefix + "constructor parameters : " + Arrays.toString(constructor.getParameterTypes()));
}
System.out.println("");
}
private Object getNonStaticMethodLocal () {
class NonStaticMethodLocal {
}
return new NonStaticMethodLocal();
}
}
输出:
Started
Class : class jetty.SOExample2$1
Number fields : 0
no fields
modifiers :
constructor modifiers :
constructor parameters : []
Class : class jetty.SOExample2$2
Number fields : 1
fields : [final jetty.SOExample2 jetty.SOExample2$2.this$0]
modifiers :
constructor modifiers :
constructor parameters : [class jetty.SOExample2]
Class : class jetty.SOExample2$3
Number fields : 0
no fields
modifiers :
constructor modifiers :
constructor parameters : []
Class : class jetty.SOExample2$4
Number fields : 1
fields : [final jetty.SOExample2 jetty.SOExample2$4.this$0]
modifiers :
constructor modifiers :
constructor parameters : [class jetty.SOExample2]
Class : class jetty.SOExample2$1StaticMethodLocal
Number fields : 0
no fields
modifiers :
constructor modifiers :
constructor parameters : []
Class : class jetty.SOExample2$1NonStaticMethodLocal
Number fields : 1
fields : [final jetty.SOExample2 jetty.SOExample2$1NonStaticMethodLocal.this$0]
modifiers :
constructor modifiers :
constructor parameters : [class jetty.SOExample2]
我已经添加了两种匿名类作为字段值和两个方法局部类。
从上面的输出可以清楚地看出,在非静态上下文中生成的类只有一个构造函数,而这个构造函数具有封闭类类型的参数。
正如输出所建议的那样,JVM在创建匿名/方法局部类时添加了一些额外的代码来保存封闭类实例引用。
这也可以在反编译器输出中看到。
class SOExample2$1
{
SOExample2$1()
{
}
}
class SOExample2$2
{
final SOExample2 this$0;
SOExample2$2()
{
this$0 = SOExample2.this;
super();
}
}
class SOExample2$3
{
SOExample2$3()
{
}
}
class SOExample2$4
{
final SOExample2 this$0;
SOExample2$4()
{
this$0 = SOExample2.this;
super();
}
}
class SOExample2$1StaticMethodLocal
{
SOExample2$1StaticMethodLocal()
{
}
}
class SOExample2$1NonStaticMethodLocal
{
final SOExample2 this$0;
SOExample2$1NonStaticMethodLocal()
{
this$0 = SOExample2.this;
super();
}
}
结论:
- 编译器在生成类文件时会执行一些我们看不到的操作。例如,向类中添加一个默认构造函数或向构造函数中添加一个默认的超类构造函数调用
super()
,该构造函数没有显式调用任何this()
自身构造函数或super()
构造函数。同样,添加封闭类型的引用也是如此,其中没有任何魔法。我们可以轻松地定义这样的类,编译器只是为我们做了这件事,使我们的生活更加轻松。
- 这与编写自己的字节码相同。因此,通过绕过编译器本身,我们可以做到实际语言无法做到的事情。
- 如果匿名类中没有
modifier
输出,则可以得出结论它们只是方法局部类(所有类修饰符public、protected、private、abstract、static在方法内失去意义)。它们只是被称为匿名类,而实际上它们是方法局部类。
this
或外部类。 - Peter LawreySOExample.this.someField = "foo"
。 - Robert Bainprivate SOExample foobar
成员变量,被初始化为外部实例 - 即使你没有使用它的代码,它仍然存在。这个对外部实例的引用是因为Java规范规定了如此 - 即使没有使用它的代码,也没有特殊情况会省略掉这个引用。 - nos