我正在阅读一本书Effective Java,其中有以下示例。在下面的示例中,作者通过以下行复制ObjectOutputStream中存在的对象的引用
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
final class Period {
private final Date start;
private final Date end;
/**
* @param start the beginning of the period
* @param end the end of the period; must not precede start * @throws IllegalArgumentException
* if start is after end
* @throws NullPointerException if start or end is null
*/
public Period(Date start, Date end) {
this.start = new Date(start.getTime());
this.end = new Date(end.getTime());
if (this.start.compareTo(this.end) > 0)
throw new IllegalArgumentException(start + " after " + end);
}
public Date start() {
return new Date(start.getTime());
}
public Date end() {
return new Date(end.getTime());
}
public String toString() {
return start + " - " + end;
}
// Remainder omitted
}
public class MutablePeriod {
// A period instance
public final Period period;
// period's start field, to which we shouldn't have access
public final Date start;
// period's end field, to which we shouldn't have access
public final Date end;
public MutablePeriod() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
// Serialize a valid Period instance
out.writeObject(new Period(new Date(), new Date()));
/*
* Append rogue "previous object refs" for internal * Date fields in Period. For
* details, see "Java Object Serialization Specification," Section 6.4.
*/
byte[] ref = {0x71, 0, 0x7e, 0, 5}; // Ref #5
bos.write(ref); // The start field
ref[4] = 4; // Ref#4
bos.write(ref); // The end field
// Deserialize Period and "stolen" Date references
ObjectInputStream in =
new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
period = (Period) in.readObject();
start = (Date) in.readObject();
end = (Date) in.readObject();
} catch (Exception e) {
throw new AssertionError(e);
}
}
}
为什么此引用指向ObjectOutputStream中存在的日期对象?什么存储在参考文献中?
A + D
答案 0 :(得分:3)
为什么这个引用指向ObjectOutputStream中的日期对象?
当写入第一个对象时,每个对象(和类)都被赋予一个id。当您添加适当的字节以便读者读取引用#5时,您将获得此对象引用。恰好两个日期对象分别是#4和#5,但如果你写了不同的数据,它们会有不同的id。
存储在参考中的是什么?
阅读器存储id和对该id的对象的引用。引用不会存储除指向的对象之外的任何内容。
答案 1 :(得分:2)
这两个流式“ref”与java中的引用无关。 (顺便说一句:字节数组有5个字节,而不仅仅是你在评论中提到的4个字节)。这十个字节是序列化输出中的一些内部魔术。有关此流格式的确切详细信息将在第二章中介绍。 “Java对象序列化规范”的6.4(即使在底部的例子中也出现了值“71 00 7e 00”)。
java程序中的引用包含4或8个字节,具体取决于平台(32对64位)。它是指向(java托管)内存的指针,其中对象实例的数据开始。但幸运的是,你永远不必在java中处理这个值。我想你甚至无法访问它。
答案 2 :(得分:0)
问题中的示例,序列化布局可以视为:
#0 Period Class desc
#1 String Class
#2 Period instance - new
#3 Date Class desc
#4 Date instance - end (name by ascending order)
#5 Date instance - start
更多详细信息链接:The Java serialization algorithm revealed。不同的编译器级别之间存在一些差异,因为序列化版本可能不同。
然后在反序列化中,所有以上解析的结果都按顺序存储在对象数组中,称为条目(保持引用以提高性能,等等)。因此,从“ {0x71,0,0x7e,0,5}”中读取对象等效于获取条目[5],是对“ start”的引用。
在学习Java序列化时,我还阅读了《有效的Java》并编写了序列化演示。您可以像这样进行探测:
byte[] ref = {0x71, 0x0, 0x7E, 0x0, 0}; // ref #0, #1, #2...
bos.write(ref);
ObjectInputStream ois = new ObjectInputStream(new
ByteArrayInputStream(bos.toByteArray()));
period = (Period)ois.readObject();
Object obj = ois.readObject();
System.out.println(obj);
if(obj instanceof ObjectStreamClass)
System.out.println("fields: " + Arrays.toString(((ObjectStreamClass) obj).getFields()));
else
System.out.println(obj.getClass());