JavaJava系列化机制

java的种类化机制帮忙将对象体系化为本三步跳件或然经过互联网传输至别处,
而反系列化则可以读取流中的数据, 并将其转移为java对象.
被种类化的类须求已毕Serializable接口,
使用ObjectInputStream和ObjectOutputStream举办对象的读写操作.

 

当然, java的系列化机制并非如此不难, 以下是私家统计的一对知识点:

 

  1. 目标读取的相继应该和写入的逐一一致,
    而且读取的次数不大概跨越已写入对象的个数. 比如文件中单单存在2个对象,
    就不可以三番五次调用3次readObject()方法, 除非调用了reset,
    skip等对流重新定位的方法.

 

  1. java系列化机制针对的是目的, 而不是类.
    由此只有非静态成员变量才会被种类化成二进制数据.

 

  1. 使用transient关键字修饰的分子变量不会被系列化为二进制数据.

 

  1. 将目的体系化为二进制数据, 将二进制数据反种类化为java对象,
    那五个操作恐怕位于差其他接纳中, 甚至也或许在不相同的电脑上进展.
    要求确保那二种场面下都有class文件,
    在连串化处和反连串处的class文件须求完结同样, 包蕴包名.

 

  1. 序列化ID的功力. 上边的Person类中定义了体系化ID: private static final
    long serialVersionUID = 1L;

那是一个非强制定义的静态成员, 倘诺不定义种类化ID,
那么eclipse会给出一个碳黑的警戒, 这些警示可以忽略. 

设想这样的境况: Person类定义了种类化ID,
且种类化对象时serialVersionUID的值为1,
而反体系化时serialVersionUID的值不为1, 那么此时将无法反体系化成功.
所以系列化ID可以用来限制某些用户的反种类化.

 

  1. 父类的体系化难点. 依据java的靶子实例化机制可以,
    创设一个子类对象的进程中, 会创立其父类对象, 反连串化也不例外.
    倘诺一个类落成了Serializable接口, 而其父类却不曾落到实处Serializable接口,
    那么在反体系化时, 虚拟机会调用父类的无参构造函数开立父类对象,
    因而反连串化后父类成员变量的值为调用无参构造函数之后的值.
    如若父类既没有已毕Serializable接口, 也不设有无参构造函数,
    那么在反连串化时将生出程序错误.

借使存在一个并未完成Serializable接口的Male类:

 

Java代码  Java 1

  1. public class Male {  
  2.     private String name;  
  3.     private int age;  
  4.   
  5.     public Male() {  
  6.   
  7.     }  
  8.   
  9.     public String getName() {  
  10.         return name;  
  11.     }  
  12.   
  13.     public void setName(String name) {  
  14.         this.name = name;  
  15.     }  
  16.   
  17.     public int getAge() {  
  18.         return age;  
  19.     }  
  20.   
  21.     public void setAge(int age) {  
  22.         this.age = age;  
  23.     }  
  24.   
  25.     @Override  
  26.     public String toString() {  
  27.         return “Male [name=” + name + “, age=” + age + “]”;  
  28.     }  
  29.   
  30. }  

Male的子类MaleStudent则已毕了Serializable接口:

 

Java代码  Java 2

  1. public class MaleStudent extends Male implements Serializable {  
  2.     private int studentID;  
  3.   
  4.     public MaleStudent(int studentID) {  
  5.         super();  
  6.         this.studentID = studentID;  
  7.     }  
  8.       
  9.     public int getStudentID() {  
  10.         return studentID;  
  11.     }  
  12.   
  13.     public void setStudentID(int studentID) {  
  14.         this.studentID = studentID;  
  15.     }  
  16.   
  17.     @Override  
  18.     public String toString() {  
  19.         return “MaleStudent [studentID=” + studentID + “]”;  
  20.     }  
  21. }  

 

则反连串化MaleStudent对象后, 其name和age属性都发出了改观:

 

Java代码  Java 3

  1. MaleStudent student = new MaleStudent(1);  
  2.         student.setName(“coolxing”);  
  3.         student.setAge(24);  
  4.         ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(  
  5.                 “male.obj”));  
  6.         out.writeObject(student);  
  7.         out.close();  
  8.   
  9.         ObjectInputStream in = new ObjectInputStream(new FileInputStream(  
  10.                 “male.obj”));  
  11.         MaleStudent maleStudent = (MaleStudent) in.readObject();  
  12.         System.out.println(maleStudent.getName() + “, ” + maleStudent.getAge()  
  13.                 + “, ” + maleStudent.getStudentID());  

 

程序的出口为:

 

name = null, age = 0, studentID = 1

可知, 父类属性值都”丢失”了, name和age都是调用Male无参构造函数之后的值.
倘诺将Male类中的无参构造函数删除,
再加上一个有参的构造函数, 在反体系化时将发生程序错误.

 

  1. 自定义系列化和反连串化操作. 即使为了一点特殊需要,
    需求自定义种类化和反种类化操作,
    只要重写完毕了Serializable接口的类中的writeObject()和readObject()方法即可,
    典型的使用场景是对密码之类的机敏成员开展加密:

Java代码  Java 4

  1. public class User implements Serializable {  
  2.     private static final long serialVersionUID = 1L;  
  3.     private String userName;  
  4.     private String passWord;  
  5.   
  6.     public User(String userName, String passWord) {  
  7.         this.userName = userName;  
  8.         this.passWord = passWord;  
  9.     }  
  10.   
  11.     public User() {  
  12.     }  
  13.   
  14.     private void writeObject(ObjectOutputStream out) {  
  15.         try {  
  16.             PutField field = out.putFields();  
  17.             field.put(“userName”, userName);  
  18.             System.out.println(“加密前: passWord = ” + passWord);  
  19.             // 模拟加密  
  20.             passWord = passWord + “1”;  
  21.             System.out.println(“加密后: passWord = ” + passWord);  
  22.             field.put(“passWord”, passWord);  
  23.             out.writeFields();  
  24.         } catch (IOException e) {  
  25.             e.printStackTrace();  
  26.         }  
  27.   
  28.     }  
  29.   
  30.     private void readObject(ObjectInputStream in) {  
  31.         try {  
  32.             GetField field = in.readFields();  
  33.             userName = (String) field.get(“userName”, “”);  
  34.             passWord = (String) field.get(“passWord”, “”);  
  35.             System.out.println(“读取的原始passWord = ” + passWord);  
  36.             // 模拟解密  
  37.             passWord = passWord.substring(0, passWord.length() – 1);  
  38.             System.out.println(“解密后的passWord = ” + passWord);  
  39.         } catch (Exception e) {  
  40.             e.printStackTrace();  
  41.         }  
  42.     }  
  43. }  

先后的出口为:

加密前: passWord = 1987810

加密后: passWord = 19878101

读取的原始passWord = 19878101

解密后的passWord = 1987810

User [userName=coolxing, passWord=1987810]

 

  1. 双重存储难题. 假诺将同一个目标往往写入文件, 会有啥样的结果?

Java代码  Java 5

  1. File file = new File(“user.obj”);  
  2.         User user = new User(“coolxing”, “1987810”);  
  3.         ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(“user.obj”));  
  4.         out.writeObject(user);  
  5.         System.out.println(file.length());  
  6.         // 改变userName的值后再也将user对象存入文件  
  7.         user.setUserName(“min”);  
  8.         out.writeObject(user);  
  9.         System.out.println(file.length());  
  10.           
  11.         ObjectInputStream in = new ObjectInputStream(new FileInputStream(“user.obj”));  
  12.         User userFromFile1 = (User) in.readObject();  
  13.         User userFromFile2 = (User) in.readObject();  
  14.         System.out.println(userFromFile1.toString());  
  15.         System.out.println(userFromFile2.toString());  

 

先后的出口结果为:

 

109

114

User [userName=coolxing, passWord=1987810]

User [userName=coolxing, passWord=1987810]

程序中调用了2次readObject()方法, 且没有出现程序不当,
由此可见确实向文件中写入了2个对象. 第二次写入user对象时,
系统发现文件中早已存在user对象, 将不再存入user对象的始末,
只写入一个引用和一些说了算音信,
所以第二次写入user对象后文件的大小扩展的很少,
而且userName也从不暴发改变.

 

  1. 一经一个类中含有非基本数据类型的积极分子变量,
    那么不仅类本人必要已毕Serializable接口,
    类中的非基本数据类型也亟需贯彻Serializable接口. java的一部分主导类,
    如String,

着力数据类型的包装类等都曾经落实了Serializable接口,
使用的时候可以查阅文档.

 

  1. 对于富含集合型成员的类来说, 不仅类自个儿需要现Serializable接口,
    集合中所存储的因素也要已毕Serializable接口.

那么集合类(List, Set, Map)到底有没有落到实处Serializable接口呢?
那是自个儿怀疑的地点, 文档中并不曾表明. 可以确定的是,

假诺集合中的成分是可种类化的, 种类化进度就不会出错.

原稿地址:http://coolxing.iteye.com/blog/1222783

相关文章