Object
及其克隆(正确完成)字节表示不应该相同吗?
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(p);
byte[] byteArr1 = bos.toByteArray();
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(p.clone());
byte[] byteArr2 = bos.toByteArray();
Arrays.equals(byteArr1, byteArr2) == true
?
在比较对象及其克隆时,尝试查看是否有不实现equals方法的替代方案。是的,当然,这些不是最佳实践,但我只是想了解Serializable Object
的字节流表示。
是的,它们是相等的。序列化依赖于类和对象字段。只要类和字段都是相同的,序列化的数据就会相同。
public class Test implements Cloneable, Serializable {
int i = 1;
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws Exception {
Test t = new Test();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(t);
byte[] byteArr1 = bos.toByteArray();
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(t.clone());
byte[] byteArr2 = bos.toByteArray();
System.out.println(Arrays.equals(byteArr1, byteArr2));
}
}
打印true
还可以看看常见的langEqualsBuilder.reflectionEquals(obj1, obj2)
,它可以通过分析它们的字段来测试任何对象的相等性
不一定:两个不同对象的序列化版本不需要字节相等,两个对象才能成为equals
。
Serializable协定中没有任何内容可以在equals
和writeObject
的结果之间建立任何关系。此外,来自Hashtable的序列化表单(writeObject)文档:
。。[和Hashtable序列化]由Hashtable表示的每个键值映射的键(Object)和值(Object)键值映射不按特定顺序发出。
当我说上面两个不同的对象时,我的意思是";"独立创建";,因为我认为这是一个更好的总体情况,让我们考虑一下Object.clone的合同,以确定它没有添加额外的限制(事实上,clone
失去了equals
限制):
创建并返回此对象的副本。";复制";可能取决于对象的类。的一般意图是,对于任何对象x,表达式:
x.clone() != x
将是真的,并且表达式:
x.clone().getClass() == x.getClass()
确实如此,但这些并不是绝对的要求。而通常的情况是:
x.clone().equals(x)
这不是绝对的要求。
作为clone
暗示具有完全相同序列化表示的对象的另一个反例,考虑一个使用序列化(在某些语言中完全有效)实现clone
的对象:由于序列化没有字节相等的要求(例如排序,如Hastable.writeObject的情况),则也不能保证这种序列化/反序列化(以及以后的序列化)的恢复顺序。
仅仅因为这样的规则适用于实践中使用的所有类型/实现(而且很可能会),不是否意味着byteEquals(serialize(a), serialize(clone(a))
不为真的类型违反了任何规定的合同或以其他方式被错误地实现。
这里有一些使用Hashtable
的代码(我选择这种类型是因为它确实实现了Serializable,并且我确实找到了如上所述的文档);等于";而不是";当被序列化时字节等于":
import java.util.*;
import java.lang.*;
import java.io.*;
class Ideone
{
public static byte[] serialize(Object v) throws java.lang.Exception {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(v);
return bos.toByteArray();
}
public static void main (String[] args) throws java.lang.Exception
{
Hashtable v1 = new Hashtable();
Hashtable v2 = new Hashtable();
int r = 1000;
for (int x = 0; x <= r; x++) {
v1.put(x, 0);
v2.put(r - x, 0);
}
byte[] byteArr1 = serialize(v1);
byte[] byteArr2 = serialize(v2);
System.out.println(v1.equals(v2));
System.out.println(Arrays.equals(byteArr1, byteArr2));
}
}
而且,虽然在Ideone上,它对我来说总是正确/正确的,根据OpenJDK实现(以及之前的文档摘录),但不必是这样,因为条目的顺序(在冲突和链接的情况下)没有定义好:
933 private void More ...writeObject(java.io.ObjectOutputStream s)
934 throws IOException {
935 Entry<K, V> entryStack = null;
936
937 synchronized (this) {
938 // Write out the length, threshold, loadfactor
939 s.defaultWriteObject();
940
941 // Write out length, count of elements
942 s.writeInt(table.length);
943 s.writeInt(count);
944
945 // Stack copies of the entries in the table
946 for (int index = 0; index < table.length; index++) {
947 Entry<K,V> entry = table[index];
948
/* order of chained entries is not well defined!! */
949 while (entry != null) {
950 entryStack =
951 new Entry<>(0, entry.key, entry.value, entryStack);
952 entry = entry.next;
953 }
954 }
955 }
956
957 // Write out the key/value objects from the stacked entries
958 while (entryStack != null) {
959 s.writeObject(entryStack.key);
960 s.writeObject(entryStack.value);
961 entryStack = entryStack.next;
962 }
963 }
(OpenJDK实现的Hashtable.clone确实维护了条目的顺序,但我省略了它,因为它不支持上面代码中概述的情况;创建一个符合Clonable/Serializable的自定义类型很简单,它违反了最初提出的问题,同时又不违反任何合同要求。)