有效的java项目编号74(关于序列化):明智地实现Serializable

时间:2013-03-21 20:49:03

标签: java effective-java

有效的java书的第74号项目有以下提到的段落(项目74的最后的第2段):

  

内部类(第22项)不应实现Serializable。他们使用   编译器生成的合成字段,用于存储对封闭的引用   实例和存储来自封闭的局部变量的值   范围。这些字段如何对应于类定义   未指定,匿名和本地类的名​​称。   因此,内部类的默认序列化形式是   定义

我知道内部类使用编译器生成的合成字段来存储对封闭实例的引用,例如如果封闭类是MyEnclosing而内部类是MyInner,则封闭引用是MyEnclosing.this。但我无法获得 BOLD 部分。请帮我理解意思。感谢!!!

3 个答案:

答案 0 :(得分:6)

假设您有这样的本地类:

 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。在这种情况下,即使使用相同的源文件,在一个编译版本中序列化和在另一个编译版本中反序列化也会失败。

(另外,还有一个问题是匿名内部类没有no-args构造函数。但是由于上面的问题,即使命名的内部类也无法可靠地序列化)

答案 1 :(得分: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;

这是粗体部分所讨论的一个例子。

答案 2 :(得分:0)

内部类是在其他类中定义的非静态类:

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,但这不可靠。