【问题标题】:Effective java Item no 74(on serialization): Implement Serializable judiciouslyEffective java Item no 74(关于序列化):明智地实现Serializable
【发布时间】:2013-03-21 20:49:02
【问题描述】:

有效的java书的第74项有一段(第74项最后的第2段),如下所述:

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

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

【问题讨论】:

    标签: java effective-java


    【解决方案1】:

    假设你有一个像这样的本地类:

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

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

    【讨论】:

    • 感谢您的努力。你的意思是说 val$a 是编译器存储在内部类中的变量,它 (val$a) 是 out 类的一部分。我对么?如果我是正确的,那么问题是没有序列化内部类。基本上我无法获得您的“但在这种情况下使用的确切名称不是编译器规范的一部分”部分。
    • 我很确定我已经看到了具有命名约定的类文件,它会在这里调用内部类 OuterClass$method$1 而不是 OuterClass$1(尽管我刚刚尝试了几个编译器并且'无法重现)。关键是spec 只要求编译器通过在封闭的类名后面附加一些$、字母和数字的组合来创建一个唯一的名称。如果类名改变了,反序列化就会失败。
    • 我有点困惑...为什么当您序列化 this 时,您的匿名 Runnable 类会被序列化?只有外部类的字段会被序列化。而问题实际上是指内部/本地/匿名类本身是Serializable...
    • @Russell Zahniser 谢谢。只需要符合你所说的关于怀疑的内容。由于没有统一的规范如何命名内部类和成员变量,因此序列化和反序列化内部类可能会失败,因为名称因编译器而异。我对么?如果我确保我使用相同的版本和相同的供应商编译器,另一件事是安全的吗?谢谢!!!
    • @Trying:序列化静态嵌套类是安全的。序列化内部类通常是一种不好的做法,尤其是在它是本地或匿名的情况下。如果你想序列化一个你实现了很多内部类引用的对象,请查看同一本书中关于序列化代理的部分。
    【解决方案2】:

    考虑以下代码:

    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;
    

    这是粗体部分所代表内容的示例。

    【讨论】:

      【解决方案3】:

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

      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的引用,但这并不可靠。

      【讨论】:

        猜你喜欢
        • 2013-08-28
        • 2012-02-17
        • 2014-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-06
        相关资源
        最近更新 更多