• Java从零开始学三十九(对象序列化)


    一、序列化

    将对象的状态存储到特定存储介质中的过程

    对象序列化,就是把一个对象变为二进制的数据流的一种方法,通过对象序列化可以方便的实现对象的传输或存储。
     
    序列化保存对象的“全景图”,构建对象的“全景天窗”.
    如果一个类的对象想被序列化,则对象所在的类必须实现java.io.Serializable接口

    二、对象的序列化和反序列化

    要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream)
    使用对象输出流输出序列化对象的步骤,有时也称为序列化,而使用对象输入流读入对象的过程,有时也称为反序列化
    序列化步骤:
    1. 创建一个对象输出流ObjectOutputStream
    2. writeObject()方法输出序列化对象

    反序列化步骤:

    1. 创建一个对象输入流ObjectInputStream
    2. readObject()方法读取流中的对象

     三、对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream)

    3.1、对象输出流:ObjectOutputStream

    No.
    方法或常量
    类型
    描述
    1
    public ObjectOutputStream(OutputStream out) throws IOException
    构造
    传入输出的对象
    2
    public final void writeObject(Object obj) throws IOException
    普通
    输出对象
    此类的使用形式与PrintStream非常的相似,在实例化时也需要传入一个OutputStream的子类对象,之后根据传入的OutputStream子类的对象不同,输出的位置也不同。

    3.2、对象输入流:ObjectInputStream

    No.
    方法或常量
    类型
    描述
    1
    public ObjectInputStream(InputStream in) throws IOException
    构造
    构造输入对象
    2
    public final Object readObject() throws IOException, ClassNotFoundException
    普通
    从指定位置读取对象
    此类也是InputStream的子类,与PrintStream类的使用类似,此类同样需要接收InputStream类的实例才可以实例化

    四、transient关键字

    当使用Serializable接口实现序列化操作时,如果一个对象中的某个属性不希望被序列化的话,则可以使用transient关键字进行声明
     
    import java.io.Serializable;
    public class Person implements Serializable {     // 此类的对象可以被序列化
        private transient String name;            // 此属性将不被序列化
        private int age;                // 此属性将被序列化
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
        public String toString() {            // 覆盖toString(),输出信息
            return "姓名:" + this.name + ";年龄:" + this.age;
        }
    }

    五、实现反序列化注意事项

    • 反序列化过程无需要使用构造器生成对象
    • 按顺序反序列化恢复对象
    • 父类Serializable或者存在无参数构造方法

     六、例子

    package com.pb.serializable;
    
    import java.io.Serializable;
    
    /*
     * 学生类 并实现接口Serializable接口,使用之可以序列化
     */
    /*
     * 序列化
     * 1.创建一个对象,这个对象将被序列化,并写入文件中,前提是这个类实现了Serializable接口
     * 2.实例化ObjectOutputStream的对象
     * 3.调用ObjectOutputStream的writerObject()方法,将对象写入流中
     * 4.关闭流
     * transient关键字
     * 可以保护对象中的敏感信息不被写入到文件中
     * 反序列化
     * 1.实例化ObjectInputStream对象
     * 2.调用ObjectInputStream的readObject()方法,获取对象时,要进行强制类型转换
     * 3.关闭流
     */
    public class Student implements Serializable {
        private String name; // 姓名
        private int age; // 年龄
        private String gender; // 性别
        private transient String password; // 密码属性不能被序列化
    
        // 构造方法
        public Student() {
            // 无参
        }
    
        // 有参数
        public Student(String name, int age, String gender, String password) {
    
            this.name = name;
            this.age = age;
            this.gender = gender;
            this.password = password;
        }
        //getter、setter方法
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            if(age>0&&age<150)
            this.age = age;
        }
    
        public String getGender() {
            return gender;
        }
    
        public void setGender(String gender) {
            if(gender.equals("男") || gender.equals("女"))
            this.gender = gender;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        // 自我介绍
        public void say() {
            System.out.println("姓名:" + this.name + "	年龄:" + this.age + "	性别:"
                    + this.gender+"	密码 : "+this.password);
        }
    
    }

    序列化:

    package com.pb.serializable;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.util.ArrayList;
    import java.util.List;
    
    /*
     * 序列化
     * 1.创建一个对象,这个对象将被序列化,并写入文件中,前提是这个类实现了Serializable接口
     * 2.实例化ObjectOutputStream的对象
     * 3.调用ObjectOutputStream的writerObject()方法,将对象写入流中
     * 4.关闭流
     * transient关键字
     * 可以保护对象中的敏感信息不被写入到文件中
     */
    public class SerializableObj {
    
        public static void main(String[] args) {
    
                try {
                    //1.声明一个文件输出流
                    FileOutputStream    fos = new FileOutputStream("d:/test/obj.txt");
                    //2.声明ObjectOutputStream流对象
                    ObjectOutputStream oos=new ObjectOutputStream(fos);
                    //也可以2步合一步
                    //ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("d:/test/obj.txt"));
                    Student stu1=new Student("张三", 23, "男", "123456");
                    Student stu2=new Student("李四", 24, "女", "123123");
                    Student stu3=new Student("王五", 25, "男", "123321");
                    Student stu4=new Student("赵六", 26, "男", "999999");
                    //创建集合并添加
                    List<Student> list=new ArrayList<Student>();
                    list.add(stu1);
                    list.add(stu2);
                    list.add(stu3);
                    list.add(stu4);
                    //3.将student对象序列化,写入输出oos流
                    oos.writeObject(stu1);
                    oos.writeObject(stu2);
                    oos.writeObject(stu3);
                    oos.writeObject(stu4);
                    oos.writeObject(list);
                    //4.关闭流
                    oos.close();
                    fos.close();
                    System.out.println("=======序列化完成======");
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
            
        }
    
    }

    反序列化:

    package com.pb.serializable;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.util.List;
    
    /*
     * 反序列化
     * 1.实例化ObjectInputStream对象
     * 2.调用ObjectInputStream的readObject()方法,获取对象时,要进行强制类型转换
     * 3.关闭流
     */
    public class ReadSerializableObj {
    
        public static void main(String[] args) {
            
            try {
                //1.声明一个文件输出流
                FileInputStream    fis = new FileInputStream("d:/test/obj.txt");
                //2.实例化ObjectInputStream
                ObjectInputStream ois=new ObjectInputStream(fis);
                //也可以全2为一
                //ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/test/obj.txt"));
                //3.声明一个Student对象和集合
                System.out.println("=====反序列化学生对象=======");
                Student stu1=    (Student) ois.readObject();
                stu1.say();
                Student stu2=    (Student) ois.readObject();
                stu2.say();
                Student stu3=    (Student) ois.readObject();
                stu3.say();
                Student stu4=    (Student) ois.readObject();
                stu4.say();
                System.out.println("=====反序列化集合对象=======");
                List<Student> list=(List<Student>) ois.readObject();
                for (Student s : list) {
                    s.say();
                }
                
                //4.关闭流
                ois.close();
                fis.close();
                System.out.println("====反序列完成====");
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    
    }

    结果:

    =====反序列化学生对象=======
    姓名:张三    年龄:23    性别:男    密码 : null
    姓名:李四    年龄:24    性别:女    密码 : null
    姓名:王五    年龄:25    性别:男    密码 : null
    姓名:赵六    年龄:26    性别:男    密码 : null
    =====反序列化集合对象=======
    姓名:张三    年龄:23    性别:男    密码 : null
    姓名:李四    年龄:24    性别:女    密码 : null
    姓名:王五    年龄:25    性别:男    密码 : null
    姓名:赵六    年龄:26    性别:男    密码 : null
    ====反序列完成====

    从结果中可以看到:password使用transient关键字后,没有被序列化,反序列化中,读出的是默认字段类型的默认值null

    七、包含引用类型属性的对象序列化

    引用类必须也为可序列化

    public class Teacher implements Serializable {
        private String name; // 姓名
        private int age; // 年龄
        private String gender; // 性别
        private transient String password; // 密码属性不能被序列化
        
        private Student stu;          //这里的Student类必须也是可以序列化的,
    
    }

    这里有个Teacher类,但属性中有一个Student类的对象,这时Teacher要实现序列时,Student必须也要实现Serializable接口才可以

     7.1、序列化算法

    • 对象分配序列号
    • 当程序试图序列化一个对象时,将会检查是否已经被序列化,只有序列化后的对象才能被转换成字节序列输出
    • 如果对象已经被序列化,则程序直接输出一个序列化编号,而不在重新序列化

    7.2、序列化机制

    Teacher类:

    package com.pb.serializable;
    
    import java.io.Serializable;
    /*
     * 教师类实例Serializable接口
     */
    public class Teacher implements Serializable {
        private String name; // 姓名
        private int age; // 年龄
        private String gender; // 性别
        private transient String password; // 密码属性不能被序列化
        
        private Student stu;          //这里的Student类必须也是可以序列化的,
    
        //构造方法
        public Teacher() {
            //无参数
        }
    
        public Teacher(String name, int age, String gender, String password,
                Student stu) {
            //有参数
            this.name = name;
            this.age = age;
            this.gender = gender;
            this.password = password;
            this.stu = stu;
        }
    
        //getter、setter方法
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getGender() {
            return gender;
        }
    
        public void setGender(String gender) {
            this.gender = gender;
        }
    
        public String getPassword() {
            return password;
        }
    
        public void setPassword(String password) {
            this.password = password;
        }
    
        public Student getStu() {
            return stu;
        }
    
        public void setStu(Student stu) {
            this.stu = stu;
        }
    
        // 自我介绍
            public void say() {
                System.out.println("姓名:" + this.name + "	年龄:" + this.age + "	性别:"
                        + this.gender+"	密码 : "+this.password);
            }
        
        
        
    }

    序列化和化序列化类:

    package com.pb.serializable;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    
    /*
     * 老师类的序列化和反序列化 2个老师引用同一个学生对象
     */
    public class WriterTeacherObj {
    
        public static void main(String[] args) {
            
            /*
             * 序列化
             * 1.序列化时时候,会查找当前对象的序列化编号是否存在,不存在,保存该对象
             * 2.写入操作时,当对象的序列化编号存在时候,会输出一个当前对象的序列化编号,而不是当前对象
             * 3.反序列化对象时, 程序会自动查找,当前对象的序列化编号,之后如果,发现当前的对象的序列化编号被其它对象引用时,则返回同一个对象
             */
            try {
                //1.实例化ObjectOutputStream
                ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("d:/test/teacher.txt"));
                //2.实例化老师对象
                Student student=new Student("马达", 23, "男", "123123");
                //2个老师引用同一个学生对象
                Teacher teacher_han=new Teacher("韩冰", 22, "女", "123456789", student);
                Teacher teacher_zhang=new Teacher("张三丰", 108, "男", "123456789", student);
                //3.对象序列
                oos.writeObject(teacher_han);
                oos.writeObject(teacher_zhang);
                //4.关闭流
                oos.close();
                System.out.println("====================序列化完成=================");
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            /*
             * 反序列化
             */
            System.out.println("==================开始反序列化================");
            try {
                //1.实例化ObjectInputStream对象
                ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/test/teacher.txt"));
                //2.反序列声明一个Teacher对象强制类型转换
                Teacher teacher_han=(Teacher) ois.readObject();
                Teacher teacher_zhang=(Teacher) ois.readObject();
                //3.输入Teacher中的信息
                System.out.println("==================老师信息================");
                teacher_han.say();
                teacher_zhang.say();
                System.out.println("=================老师中学生信息=============");
                teacher_han.getStu().say();
                teacher_zhang.getStu().say();
                //4.关闭流
                ois.close();
                System.out.println("===========反序列化完成=========");
                
                
                //判断2个老师的学生是否为同一个
                if(teacher_han.getStu()==teacher_zhang.getStu()){
                    System.out.println("张老师和韩老师教的是同一个学生");
                }else{
                    System.out.println("张老师和韩老师教的不是同一个学生");
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
    
        }
    
    }

    结果:

    ====================序列化完成=================
    ==================开始反序列化================
    ==================老师信息================
    姓名:韩冰    年龄:22    性别:女    密码 : null
    姓名:张三丰    年龄:108    性别:男    密码 : null
    =================老师中学生信息=============
    姓名:马达    年龄:23    性别:男    密码 : null
    姓名:马达    年龄:23    性别:男    密码 : null
    ===========反序列化完成=========
    张老师和韩老师教的是同一个学生
  • 相关阅读:
    ASP.NET Page 那点事
    .Net项目分层与文件夹结构大全(最佳架子奖,吐槽奖,阴沟翻船奖揭晓)
    bash_profile和.bashrc的区别
    limits.conf生效问题
    有关snprintf返回值
    snprintf和strncpy对比
    Hadoop技术论坛
    Ubuntu系统微调
    interpreter和state模式的区别
    ANTLR实现的SQL解析器 OQL
  • 原文地址:https://www.cnblogs.com/liunanjava/p/4307833.html
Copyright © 2020-2023  润新知