Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/367.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
有效的java第74项(关于序列化):明智地实现可序列化_Java_Effective Java - Fatal编程技术网

有效的java第74项(关于序列化):明智地实现可序列化

有效的java第74项(关于序列化):明智地实现可序列化,java,effective-java,Java,Effective Java,有效java手册第74项中有一段(第74项最后一段的第2段)提到如下内容: 内部类(第22项)不应实现可序列化。他们使用 编译器生成的合成字段用于存储对封闭对象的引用 实例和来存储封闭的局部变量的值 范围。这些字段与类定义对应的方式是 未指定,匿名类和本地类的名称也未指定。 因此,内部类的默认序列化形式是病态的- 定义 我知道关于内部类使用编译器生成的合成字段来存储对封闭实例的引用,例如,如果封闭类是MynClosing,而内部类是MyInner,则封闭引用是MynClosing.this。但是

有效java手册第74项中有一段(第74项最后一段的第2段)提到如下内容:

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


我知道关于内部类使用编译器生成的合成字段来存储对封闭实例的引用,例如,如果封闭类是MynClosing,而内部类是MyInner,则封闭引用是MynClosing.this。但是我不能得到粗体部分。请帮我弄明白意思。谢谢

假设您有这样一个本地类:

 class OuterClass {
    Runnable run;

    void method() {
       final int a = 8;
       this.run = new Runnable() {
          public void run() {
             System.out.println(a);
          }
       };
    }
 }
现在假设我尝试序列化此,它包含此内部类类型的对象。我的编译器将该类命名为
OuterClass$1
,并给它一个名为
val$a
的字段。但是在这种情况下使用的确切名称不属于编译器规范的一部分。另一个编译器可能会选择调用内部类
OuterClass$方法$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;
    }

}
一旦有了
内部
类的实例,当您序列化它时,它必须有一个对外部类的引用(它通过
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
的引用,但这不可靠。

感谢您的努力。您的基本意思是说is val$a是编译器存储在内部类中的变量,而(val$a)是外部类的一部分。我说得对吗?如果我是正确的,那么问题在于没有序列化内部类。基本上,我无法获得您的“但在这种情况下使用的确切名称不是编译器规范的一部分”部分。我非常确定我看到过具有命名约定的类文件,它们在这里调用内部类
OuterClass$method$1
,而不是
OuterClass$1
(尽管我刚才尝试了几个编译器,但都无法复制)。关键是编译器只需要在封闭的类名后附加一些$、字母和数字的组合来创建一个唯一的名称。如果类名更改,反序列化将失败。我有点困惑……为什么在序列化
时,匿名
可运行的
类会被序列化s
?只有外部类的字段被序列化。问题实际上是指内部/本地/匿名类本身是可序列化的。
。@Russell Zahniser谢谢。只需要符合您所说的疑问。因为没有统一的规范如何命名内部类和成员变量,因此,序列化和反序列化内部类可能会失败,因为每个编译器的名称都不同。我说的对吗?还有一件事是安全的,如果我能确保我使用的是相同的版本和相同的供应商编译器?谢谢!!!@Trying:序列化静态嵌套类是安全的。序列化内部类通常是一种不好的做法,特别是当它是本地的或匿名的时候。如果你想序列化一个你用很多内部类的引用实现的对象,看看这本书中关于序列化代理的部分。