序列化系列之一:java对象的序列化和反序列化
一,对象的序列化,反序列化
1.对象的序列化,就是将Object转换成byte序列,反之叫对象的反序列化
2.做序列化需要有流类,序列化流(ObjectOutputStream),是(字节的)过滤流,有writeObject方法,写一个对象
反序列化 流(ObjectInputStream)----readObject
3.序列化接口(Serializable)
对象必须实现序列化接口,才能进行序列化,否则将出现异常
这个接口,没有任何方法,只是一个标准
/**
* 如果将来希望这个对象能在网络中传输,也需要把这个对象进行序列化。(在网络中是以字节为单位进行传输的)
* 所以当我们要保存这个对象,或在网络中传输这个对象时,都需要对这个对象进行序列化
*/
4.例子
对象实现Serializable接口:
import java.io.Serializable; /** * Created by yaming on 17-8-15. */ public class Student implements Serializable { private Long id; private String name; private char gender; private transient double money; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", gender=" + gender + ", money=" + money + '}'; } }
将对象存储到硬盘上
import java.io.*; /** * Created by yaming on 17-8-15. * 把对象序列化到文件夹(内存),和反序列化读取这个文件中的对象 */ public class Student2Serializable { public static void main(String[] args) { /** * 如果将来希望这个对象能在网络中传输,也需要把这个对象进行序列化。(在网络中是以字节为单位进行传输的) * 所以当我们要保存这个对象,或在网络中传输这个对象时,都需要对这个对象进行序列化 */ String filePath="/home/yaming/Desktop/student.dat"; //1.对象的序列化 // ObjectOutputStream outputStream=null; // try { // outputStream=new ObjectOutputStream(new FileOutputStream(filePath)); // Student student=new Student(); // student.setId(1L); // student.setName("张三"); // student.setGender('男'); // student.setMoney(1); // outputStream.writeObject(student); // outputStream.flush(); // outputStream.close(); // } catch (IOException e) { // e.printStackTrace(); // } //2.对象的反序列化 ObjectInputStream inputStream=null; try { inputStream=new ObjectInputStream(new FileInputStream(filePath)); //使用readObj方法读出来,并进行强制类型转换 Student student=null; try { student=(Student) inputStream.readObject(); System.out.println(student); } catch (ClassNotFoundException e) { e.printStackTrace(); } inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }
二,transient关键字
1,一个对象中有些属性不需要序列化,可以用transient关键子修饰。
用transient修饰的元素,该元素不会进行jvm默认的序列化。也可以自己完成这个元素的序列化
(在网络传输中,有些元素不需要传输,也就不用序列化,这样可以节省网络传输的流量)
如何自己完成序列化呢?
有两个方法签名可以实现。可以参考ArrayList里的两个方法 :
writeObject(java.io.ObjectOutputStream s),readObject(java.io.ObjectInputStream s)
import java.io.Serializable; /** * Created by yaming on 17-8-15. */ public class Student implements Serializable { private Long id; private String name; private char gender; private transient double money;//该元素不会进行jvm默认的序列化 public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } public double getMoney() { return money; } public void setMoney(double money) { this.money = money; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", gender=" + gender + ", money=" + money + '}'; } //自己实现序列化 private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ s.defaultWriteObject();//把jvm能默认序列化的元素进行序列化 s.writeDouble(money);//自己完成money的序列化,也可以用s.writeObject(money) } //自己实现反序列化 private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject();//把jvm能默认反序列化的元素进行反序列化 this.money=s.readDouble();//自己完成money的反序列化 } }
2,ArrayList序列化优化的问题(ArrayList源码)
ArrayList底层是数组,但我们往list中添加数据时,有添加不满的情况。数组中为空的元素就不用序列化了(有效元素才进行序列化,无效元素就不进行序列化了),这样可以提高性能。ArrayList对底层数组自己做序列化。
transient Object[] elementData;
源码:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* Save the state of the <tt>ArrayList</tt> instance to a stream (that
* is, serialize it).
*
* @serialData The length of the array backing the <tt>ArrayList</tt>
* instance is emitted (int), followed by all of its elements
* (each an <tt>Object</tt>) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
//------>数组有效元素的序列化。
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
}
3,序列化中子父类构造函数的问题
如果父类实现了序列化接口,那么父类就可以序列化,子类就不需要再进行序列化接口了。
3.1,一个类实现了序列化接口,那么其子类都可以进行序列化