在effective java book的第74项中,有一段(第74项最后一段的第二段)提到如下:
内部类(第22项)不应该实现Serializable。他们使用编译器生成的合成字段,用于存储对封闭的引用和来存储来自封闭的局部变量的值范围。这些字段是如何对应类定义的未指定,匿名类和本地类的名称也是如此。因此,内部类的默认序列化形式是病态的定义。
我知道内部类使用编译器生成的合成字段来存储对封闭实例的引用,例如,如果封闭类是my,内部类是my,那么封闭引用是my。但我无法获得BOLD部分。请帮我弄明白意思。谢谢! !
假设您有一个这样的本地类:
class OuterClass {
Runnable run;
void method() {
final int a = 8;
this.run = new Runnable() {
public void run() {
System.out.println(a);
}
};
}
}
现在假设我尝试序列化this
,它包含一个这种内部类类型的对象。我的编译器将这个类命名为OuterClass$1
,并给它一个名为val$a
的字段。但是在这种情况下使用的确切名称并不是编译器规范的一部分。另一个编译器可能会选择调用内部类OuterClass$method$1
。在这种情况下,即使使用了相同的源文件,在一个编译版本中序列化和在另一个编译版本中反序列化也会失败。
(另外,还有一个问题是匿名内部类没有无参数构造函数。但是由于上面的问题,即使是命名的内部类也不能可靠地序列化)
考虑以下代码:
public class Main {
public static void main(String[] args) {
final int x = Integer.valueOf(args[0]);
new Object() {
void print() {
System.out.println(x);
}
}.print();
}
}
我的编译器调用匿名内部类Main$1
。当我反汇编它时,我看到来自外部作用域的x
值的副本存储在一个名为val$x
:
private final int val$x;
这是粗体部分所讨论的一个例子。
内部类是在其他类中定义的非静态类:
class Outer implements Serializable {
private String someString;
class Inner implements Serializable {
private int someInt;
}
}
一旦你有了Inner
类的实例,当你序列化它的时候,它必须有一个对外部类的引用(它通过Outer.this
引用在内部访问),对于一个序列化的对象是如何实现的还没有指明。这同样适用于局部类:
class Outer implements Serializable {
private String someString;
Serializable method(final int i) {
class Inner implements Serializable {
Inner() {
System.out.println(i);
}
}
return new Inner();
}
}
如果对method()
返回的值进行序列化,则需要对i
进行引用,但这并不可靠。