序列化就是将对象表示为一个字节序列,包括了该对象的数据,有关对象的类型信息和存储在对象中数据的类型。
序列化对象写入文件之后,可以从文件中读取出来,并进行反序列化,在内存中新建对象。
java中序列化和反序列化的关键的两个方类是ObjectInputStream和ObjectOutputStream类
1 2 3
|
public final void writeObject(Object x) throws IOException
|
1 2 3
|
public final Object readObject() throws IOException, ClassNotFoundException
|
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
| public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
public String name; public String address; public transient int SSN; public int number; public void mailCheck() { System.out.println("Mailing a check to " + name + " " + address); } }
public class SerializeDemo { public static void main(String[] args) { Employee e = new Employee(); e.name = "test"; e.address = "hangzhou"; e.SSN = 12345678; e.number = 110;
try { FileOutputStream fos = new FileOutputStream("./employee.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(e); oos.close(); fos.close(); System.out.println("saved"); } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } } }
public class DeserializeDemo { public static void main(String[] args) { Employee e = null;
try { FileInputStream fis = new FileInputStream("./employee.ser"); ObjectInputStream ois = new ObjectInputStream(fis); e = (Employee) ois.readObject(); ois.close(); fis.close(); } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } catch (ClassNotFoundException e1) { System.out.println("employee class not found"); e1.printStackTrace(); }
System.out.println("Deserialized Employee..."); System.out.println("Name: " + e.name); System.out.println("Address: " + e.address); System.out.println("SSN: " + e.SSN); System.out.println("Number: " + e.number); } }
Deserialized Employee... Name: test Address: hangzhou SSN: 0 Number: 110
|
深入思考
上面就是序列化的一个简单例子,但是还有一些细节仍然需要注意
序列号问题
如果Employee中不显式的写序列号,那么会很容易出现各种莫名其妙的问题,序列号是编译器生成的,所以如果当对象序列化完成之后,突然改了一点对象的内容,重新编译后,没有再次重新的序列化直接就反序列化,这时候肯定会出错,因为序列号变了,序列号其实就相当于版本控制,改变的对象之后,版本号肯定要发生变化,所以有时为了避免该问题,最好手动添加序列号
静态属性序列化
对Employee对象进行修改,增加一个静态属性
在实例化之后,更改静态属性的值,再反序列化,输出静态属性的值,得到的却是更改后的值。
这里面的原因其实显而易见,序列化是对对象进行实例化,而static是类的属性,所以不会对静态成员进行序列化,读取该对象的值,获取的是类的属性值。因此 序列化并不保存静态属性。
父类序列化
如果子类实现了Serializable接口,父类没有实现Serializable接口,那么将子类进行序列化之后,反序列化之后获取的值,父类的属性值全部变为成员变量对应类型的默认值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| public class Animal { public String name;
public int type; }
public class Cat extends Animal implements Serializable {
public String color; }
public class ExtendsSerDeserDemo { public static void main(String[] args) { Cat cat = new Cat(); cat.name = "bosi"; cat.type = 0; cat.color = "yellow";
try { FileOutputStream fos = new FileOutputStream("./cat.ser"); ObjectOutputStream oos = new ObjectOutputStream(fos); oos.writeObject(cat); oos.close(); fos.close(); System.out.println("saved");
FileInputStream fis = new FileInputStream("./cat.ser"); ObjectInputStream ois = new ObjectInputStream(fis); Cat cat1 = (Cat) ois.readObject(); ois.close(); fis.close();
System.out.println(cat1.name); System.out.println(cat1.type); System.out.println(cat1.color); } catch (FileNotFoundException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
null 0 yellow
|
还有一个需要注意的地方就是,如果没有实现Serializable接口,那么父类必须含有无惨的构造函数,否则在反序列化时会报错
上面有这么多问题,其实解决方法很简单,将父类也实现Serializable接口即可
Transient关键字
该关键字的使用方法在上面已经有出现过,Employee里面的SSN属性,使用方法就是在属性前加上transient即可,这样就可以阻止该属性被序列化。在反序列化时会将该属性的值直接设置为初始值
虚拟机调用
在序列化过程中,虚拟机会试图调用对象里面的writeObject和readObject方法,进行用户自定义的序列化和反序列化。如果没有,则默认调用ObjectInputStream里面的defaultReadObject和ObjectOutputStream里面的defaultWriteObject方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class Cat extends Animal implements Serializable {
public String color;
public Cat() { super(1); }
private void writeObject(ObjectOutputStream out) { try { ObjectOutputStream.PutField putField = out.putFields(); System.out.println("原始颜色: " + color); color = "加密了"; System.out.println("加密后的颜色: " + color); putField.put("color", color); out.writeFields(); } catch (IOException e) { e.printStackTrace(); } }
private void readObject(ObjectInputStream in) { try { ObjectInputStream.GetField getField = in.readFields(); Object color = getField.get("color", ""); System.out.println("解密前的颜色: " + color); color = "yellow"; System.out.println("解密后的颜色: " + color);
} catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
|