我正在阅读Effective Java并且遇到了这一段(目前对我来说有点密集)。有人可以解释为什么内部类不应该更详细地实现Serializable吗?
内部类(第22项)不应实现Serializable。他们使用 编译器生成的合成字段,用于存储对封闭实例和存储的引用 封闭范围的局部变量值。这些字段如何与班级对应 定义未指定,匿名和本地类的名称也是如此。因此, 内部类的默认序列化形式是不明确的。
答案 0 :(得分:1)
所以想象一下:
import java.io.*;
public class A {
private Object mFoo;
public A(Object foo) {
mFoo = foo;
}
public Serializable getData() {
String niceString = "Nice String";
return new B(niceString);
}
public class B implements Serializable {
private Object mBlob;
public B (Object blob) {
mBlob = blob;
}
public String toString() {
return String.format("%s-%s-%s", getClass(), mBlob, mFoo);
}
}
public static void main(String[] args)throws Exception {
A a = new A("Have a nice Day");
Serializable s = a.getData();
System.out.println("Before:" + s);
ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
ObjectOutputStream ostream = new ObjectOutputStream(bytesOut);
ostream.writeObject(s);
ostream.flush();
ostream.close();
ByteArrayInputStream bytesIn = new ByteArrayInputStream(bytesOut.toByteArray());
ObjectInputStream istream = new ObjectInputStream(bytesIn);
System.out.println("After:" + istream.readObject());
}
}
在这种情况下,存在用于mFoo
之类的虚拟结构。可以在toString()
中引用它们,因为这个jvm引用了A
的实例,其中包含“Have a nice Day”字符串。现在,如果它是静态的,那就是另一回事了。但是没有静态,这可能无法序列化。
Before:class A$B-Nice String-Have a nice Day
Exception in thread "main" java.io.NotSerializableException: A
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1165)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1535)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1496)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1413)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1159)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:329)
at A.main(A.java:35)
现在让我们稍微修改一下B
:
public static class B implements Serializable {
private Serializable mData2;
private Object mBlob;
public B (Object blob) {
mBlob = blob;
mData2 = new Serializable() {
String data = "Foo";
public String toString() {
return data;
}
};
}
public String toString() {
return String.format("%s-%s-%s", getClass(), mBlob, mData2);
}
}
现在没有A
的引用,B
有一个匿名类,它指向B,因为它在技术上是一个内部类。除了B也是Serializable之外,这也是同样的问题。什么是输出。我在热点上运行。
Before:class A$B-Nice String-Foo
After:class A$B-Nice String-Foo
因此所有部件都可写得很棒! ......但仍有问题。
注意它才有效,因为我们正在控制一些事情。
让我们说,而不是序列化到缓冲区并重新读取它,在同一个应用程序中我们做一些可能更有用的东西。假设我们将B
的实例保存到文件中。这基本上是在给定运行时之外序列化匿名内部类。
如果您正在向其他人描述课程B
,您会称之为匿名内部课程?这是匿名的。你可能会把它称为对你有意义的东西,并以一贯的方式引用它。这就是sdk /运行时的功能。它命名为不会与类路径中的任何其他类发生冲突的东西。在Hotspot中我认为它将被命名为A.B$1
,因为它是B
的第一个匿名内部类(为什么这不是0索引总是困扰我)。这是Hotspot详细信息的一个实现,我相信。因此,如果您使用相同的源并使用另一个sdk工具集进行编译,然后运行代码,并将其反序列化该文件,如果它没有将该匿名内部类称为同名,则可能会有{ {1}}扔了,你会像HUH?怎么样?哪里?因为谁知道该文件何时被写入,所以追逐是痛苦的。
序列化格式有一个规范,这是这种类型的源头。通常有一些神奇的数字来指定数据的开头,然后是序列化的blob的类名,前面有一个ClassNotFoundException
,所以我认为在这种情况下文件会包含类似L
的内容。如果编译并在Hotspot上运行。因此,当它读取该流寻找LA.B$1
时,所有运行时都可以真正做到,因为它不知道该文件来自哪个运行时或运行时实例。 (在这里关闭内存,所以我跳过很多细节)。