前段时间复习了一下对象的序列化技术,其中涉及到了java语言规范中的transient关键字的用法,根据官方的解释,如果一个对象的属性用该关键字修饰,那么在序列化对象的时候,将忽略该属性(即不会进行存储,反序列化时该属性值将丢失)。1.新建一个实现Serializable接口的类/**
* 该类对象可以被序列化
* @author yuanli
*
*/
public class Book implements Serializable {
/**
*
*/
private static final long serialVersionUID = 3173949523199923358L;
private int id;
private String name;
private transient float price;
public Book() {
super();
} public Book(int id, String name, float price) {
super();
this.id = id;
this.name = name;
this.price = price;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public float getPrice() {
return price;
} public void setPrice(float price) {
this.price = price;
}
@Override
public String toString() {
return "id=" + this.id + ",name=" + this.name + ",price=" + this.price;
}
}
2.编写一个测试类public class Test {public static void main(String[] args) {
Test t = new Test();
t.testTransient();
}public void testTransient() {
try {
Book book = new Book(1,"aa",12.5f);
System.out.println("book序列化之前: " + book.toString());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
//序列化book对象
oos.writeObject(book);
//反序列化book对象
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
Book book_serializable = (Book)ois.readObject();
System.out.println("book反序列化之后:" + book_serializable.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}3.测试结果如下:
book序列化之前: id=1,name=aa,price=12.5
book反序列化之后:id=1,name=aa,price=0.0
从执行结果中可以发现,反序列化之后book对象的price属性变为默认值了,实践证明,被transient关键字修饰的域(属性)确实不会被序列化4.但是下面的代码让我困惑public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0; /**
* Constructs an empty list.
*/
public LinkedList() {
header.next = header.previous = header;
} /**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
......
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
......
}研究LinkedList源码发现size和head属性被transient修饰,这样的属性应该不会被序列化,可实践证明可以正常序列化,反序列化后调用size()方法返回的结果同序列化前相同。那么这样设计的目的何在?求解中...............
* 该类对象可以被序列化
* @author yuanli
*
*/
public class Book implements Serializable {
/**
*
*/
private static final long serialVersionUID = 3173949523199923358L;
private int id;
private String name;
private transient float price;
public Book() {
super();
} public Book(int id, String name, float price) {
super();
this.id = id;
this.name = name;
this.price = price;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public float getPrice() {
return price;
} public void setPrice(float price) {
this.price = price;
}
@Override
public String toString() {
return "id=" + this.id + ",name=" + this.name + ",price=" + this.price;
}
}
2.编写一个测试类public class Test {public static void main(String[] args) {
Test t = new Test();
t.testTransient();
}public void testTransient() {
try {
Book book = new Book(1,"aa",12.5f);
System.out.println("book序列化之前: " + book.toString());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
//序列化book对象
oos.writeObject(book);
//反序列化book对象
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
Book book_serializable = (Book)ois.readObject();
System.out.println("book反序列化之后:" + book_serializable.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}3.测试结果如下:
book序列化之前: id=1,name=aa,price=12.5
book反序列化之后:id=1,name=aa,price=0.0
从执行结果中可以发现,反序列化之后book对象的price属性变为默认值了,实践证明,被transient关键字修饰的域(属性)确实不会被序列化4.但是下面的代码让我困惑public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0; /**
* Constructs an empty list.
*/
public LinkedList() {
header.next = header.previous = header;
} /**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
......
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
......
}研究LinkedList源码发现size和head属性被transient修饰,这样的属性应该不会被序列化,可实践证明可以正常序列化,反序列化后调用size()方法返回的结果同序列化前相同。那么这样设计的目的何在?求解中...............
它还有俩私有方法是进行写入对象和读取对象的 /**
* Save the state of this <tt>LinkedList</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The size of the list (the number of elements it
* contains) is emitted (int), followed by all of its
* elements (each an Object) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject(); // Write out size
s.writeInt(size); // Write out all elements in the proper order.
for (Entry e = header.next; e != header; e = e.next)
s.writeObject(e.element);
} /**
* Reconstitute this <tt>LinkedList</tt> instance from a stream (that is
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
// Read in any hidden serialization magic
s.defaultReadObject(); // Read in size
int size = s.readInt(); // Initialize header
header = new Entry<E>(null, null, null);
header.next = header.previous = header; // Read in all elements in the proper order.
for (int i=0; i<size; i++)
addBefore((E)s.readObject(), header);
}
起初看源码的时候没有深入研究,还很纳闷,写两个私有方法又没有调用意图何在,经2楼一提醒,我才想到了反射机制,果然在ObjectInputStream/ObjectOutputStream中通过放射调用了这两个私有方法!只是不解的是为什么要这样设计???将以下两个属性的修饰符transient去掉会有什么影响呢???
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0;
至于size还真不清楚有什么弊端,源码采用单独将size序列化 而不是放在对象中一起序列化 感觉放对象里面一起序列化也没什么区别
size在writeObject中写入,并且不需要存储Entry对象而只要依次存储它的数据项,
你看源码写入的是e.element。写入的都是有用而不冗余的信息。
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
private transient Entry<E> header = new Entry<E>(null, null, null);
private transient int size = 0; /**
* Constructs an empty list.
*/
public LinkedList() {
header.next = header.previous = header;
} /**
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
......
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
......
}
看起来有点乱,不过认真看还是可以看出来的
/**
* Save the state of this <tt>LinkedList</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The size of the list (the number of elements it
* contains) is emitted (int), followed by all of its
* elements (each an Object) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out any hidden serialization magic
s.defaultWriteObject(); // Write out size
s.writeInt(size); // Write out all elements in the proper order.
for (Entry e = header.next; e != header; e = e.next)
s.writeObject(e.element);
}
仔细看代码
s.defaultWriteObject();
将会保存当前对象的所有非transient fields和Methodss.writeInt(size);
是为了保存element的个数,否则解序列的时候,就不知道该读几个对象了。如果size 不是transient的,那么size就会被这两条语句保存两次。所以size 被设成是transient,为的就是0冗余。
放在对象里序列化当然也是可以,没什么大区别。唯一的区别是,
放在对象里序列化,将会多调用一次反射,因为size是私有变量,
所以反射时还需要到security manager转一圈。