zoukankan      html  css  js  c++  java
  • Java对象序列化

    Java对象序列化

    1、什么是序列化

    Java序列化是指把Java对象转换为字节序列的过程;而反序列化是指把字节序列恢复为Java对象的过程。从而实现网络传输、本地存储等需求;

    • 一般Java对象的生命周期比Java虚拟机短,而实际开发中如果需要JVM停止后能够继续持有对象,则需要用到序列化技术将对象持久化到磁盘或数据库;

    • 在多个项目进行RPC(Remote Procedure Call)调用时,需要在网络上传输JavaBean对象,而网络上只允许二进制形式的数据进行传输,这时则需要用到序列化技术;

    2、如何实现序列化

    将被序列化的类实现Serializable或Externalizable接口即可;在实现Serializable接口的默认情况下,Java会自动将非transient(短暂的,临时的)关键字修饰的属性序列化到指定文件中去;

    3、Serializable接口示例

     1 //JavaBean
     2   public class StudentBean implements Serializable{
     3       
     4       private String code;
     5       private String name ;
     6       private Integer age ;
     7       private List<Score> scores  ;
     8       // get/set/toString ...
     9 10   }
    11   public class Score implements Serializable {
    12       
    13       private Integer code ;
    14       private String name;
    15       private Integer score ;
    16       // get/set/toString ...
    17   }
    18   //SerializableUtils
    19   public class SerializableUtils {
    20       
    21       /**
    22        * 序列化
    23        * 
    24        * @param obj   实现了Serializable接口的类
    25        * @param fileName 文件输出路径
    26        * @throws IOException
    27        */
    28       public static void serialize(Object obj, String fileName)
    29               throws IOException {
    30    
    31           FileOutputStream fos = new FileOutputStream(fileName);
    32           BufferedOutputStream bos = new BufferedOutputStream(fos);
    33           ObjectOutputStream oos = new ObjectOutputStream(bos);
    34           oos.writeObject(obj);
    35           oos.close();
    36       }
    37       
    38       /**
    39        * 反序列化
    40        * 
    41        * @param fileName  反序列化时读取的文件路径
    42        * @return
    43        * @throws IOException
    44        * @throws ClassNotFoundException
    45        */
    46       public static Object deserialize(String fileName) throws IOException, ClassNotFoundException {
    47           FileInputStream fis = new FileInputStream(fileName);
    48           BufferedInputStream bis = new BufferedInputStream(fis);
    49           ObjectInputStream ois = new ObjectInputStream(bis);
    50           Object obj = ois.readObject();
    51           ois.close();
    52           return obj;
    53       }
    54   }
    55   // TestClass
    56   public class MyTest {
    57 58       private StudentBean stu;
    59 60       @Before
    61       public void before() {
    62           stu = new StudentBean();
    63           stu.setAge(18);
    64           stu.setCode("101");
    65           stu.setName("张三");
    66           Score s1 = new Score(101, "高等数学", 100);
    67           Score s2 = new Score(102, "计算机", 100);
    68           Score s3 = new Score(103, "大学物理", 100);
    69           List<Score> list = new ArrayList<Score>();
    70           list.add(s1);
    71           list.add(s2);
    72           list.add(s3);
    73           stu.setScores(list);
    74       }
    75 76       @Test
    77       public void Test() throws IOException, ClassNotFoundException {
    78           // 序列化
    79           SerializableUtils.serialize(stu, "student.txt");
    80 81           // 反序列化
    82           StudentBean student = (StudentBean) SerializableUtils.deserialize("student.txt");
    83           System.out.println(student.getName());
    84           for (Score s : student.getScores()) {
    85               System.out.println(s);
    86           }
    87       }
    88   }

    4、serialVersionUID

    • 实现Serializable接口之后可选择添加一个serialVersionUID 属性作为该类的一个序列化版本号,该编码可选择自动生成或者自定义编辑,其主要作用是为当前类添加一个版本标识,UID不变则认为类的内容没变。反序列化时会判断UID是否未发生变化,若序列化后修改了此UID值,则会导致反序列化失败 ,抛出InvalidClassException异常;

    • 不指定版本号另一个明显隐患是,不利于jvm间的移植,可能class文件没有更改,但不同jvm可能计算的规则不一样,这样也会导致无法反序列化;

    5、Externalizable接口示例

    Externalizable(可外部化的)继承自Serializable,须手动实现writeExternal以及readExternal方法;

     1 //修改StudentBean
     2   public class StudentBean implements Externalizable{
     3  4       private String code;
     5       private String name ;
     6       private Integer age ;
     7       private List<Score> scores  ;
     8  9       /*
    10        * 自定义序列化规则
    11        */
    12       @Override
    13       public void writeExternal(ObjectOutput out) throws IOException {
    14           out.writeObject(code);
    15           out.writeObject(name);
    16           out.writeInt(age);
    17           out.writeObject(scores);
    18       }
    19 20       /*
    21        * 自定义反序列化规则
    22        */
    23       @Override
    24       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    25           
    26           code = (String)in.readObject();
    27           name = (String)in.readObject();
    28           age = in.readInt();
    29           scores = (List<Score>)in.readObject();
    30       }
    31       // get/set/toString
    32   }

    6、部分属性序列化

    6.1 transient关键字

    transient关键字,在实现Serializable接口情况下,只需要对不需要序列化的属性使用transient关键字进行修饰即可实现在序列化时忽略该字段;

    1   private transient String code

    6.2 添加writeObject和readObject方法

    添加writeObject和readObject方法,在实现Serializable接口的情况下添加如下两个方法,自定义选择序列化哪些字段,但注意若该字段没有参加序列化,则反序列化时亦不能参与;

    defaultWriteObject()会执行默认序列化操作即序列化所有非static、transient属性;transient关键字修饰的属性可以用writeObject()方式实现序列化;

    原理:调用ObjectOutputStream中writeObject方法时会检查序列化对象是否实现了自己的writeObject方法,如果是则跳过常规序列化转流程转而调用writerObject方法实现序列化readObject同理;

     1       private Integer code ;
     2       private transient String name;
     3       private Integer score ;
     4  5       //序列化
     6       private void writeObject(ObjectOutputStream oos) throws IOException {
     7           oos.defaultWriteObject();
     8           oos.writeObject(name);
     9       }
    10       //反序列化
    11       private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
    12           ois.defaultReadObject();
    13           name = (String)ois.readObject();
    14       }

    6.3 修改writeExternal和readExternal方法

    修改writeExternal和readExternal方法,原理同上,且序列化与反序列化属性须一致;

     1 /*
     2        * 自定义序列化规则
     3        */
     4       @Override
     5       public void writeExternal(ObjectOutput out) throws IOException {
     6           //out.writeObject(code);
     7           out.writeObject(name);
     8           out.writeInt(age);
     9           out.writeObject(scores);
    10       }
    11 12       /*
    13        * 自定义反序列化规则
    14        */
    15       @Override
    16       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    17           
    18           //code = (String)in.readObject();
    19           name = (String)in.readObject();
    20           age = in.readInt();
    21           scores = (List<Score>)in.readObject();
    22       }

    7、总结

    • Serializable是标识接口,没有需要实现的方法;Externalizable必须实现writeExternal和readExternal方法;

    • Serializable不需要有空构造,但Externalizable若含有参构造则必须有无参构造函数;

    • Serializable有两种实现方式,默认方式下自动序列化非static、transient关键字修饰的属性;

    • Serializable默认方式下使用反射实现序列化,性能稍弱,对属性顺序无要求;Externalizable要求属性的序列化与反序列化顺序一致,否则抛出 java.io.OptionalDataException 异常;

    • 所有需要网络传输的对象都需要实现序列化接口,通过建议所有的javaBean都实现Serializable接口;

    • 同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化;

    • 建议所有可序列化的类加上serialVersionUID 版本号,方便项目升级、维护;

    • 序列化可以实现深拷贝;

    8、预告:Redis中的序列化

  • 相关阅读:
    [模板] 循环数组的最大子段和
    [最短路][几何][牛客] [国庆集训派对1]-L-New Game
    [洛谷] P1866 编号
    1115 Counting Nodes in a BST (30 分)
    1106 Lowest Price in Supply Chain (25 分)
    1094 The Largest Generation (25 分)
    1090 Highest Price in Supply Chain (25 分)
    树的遍历
    1086 Tree Traversals Again (25 分)
    1079 Total Sales of Supply Chain (25 分 树
  • 原文地址:https://www.cnblogs.com/lijizhi/p/13300483.html
Copyright © 2011-2022 走看看