Java------IO流之对象专属流

对象专属流

基本概念

序列化和反序列化的定义:

  • Java序列化:指把Java对象转换为字节序列的过程
  • java反序列化:指把字节序列化恢复为Java对象的过程

序列化和反序列的最重要的作用:

  • 序列化:
    • 在传递和保护对象时,保证对象的完整性和可传递性。对象转换有序字节流,以便在网络上传输或者保护在本地文件种
  • 反序列化:
    • 根据字节流中保存的对象状态及描述信息,通过反序列化重建对象

总结:核心作用就是对象状态的保存和重建(整个过程核心点就是字节流中所保存的对象那个状态及描述信息)

序列化优点:

  1. 将对象转为字节流存储到硬盘上,当JVM停机的时,字节流还会再硬盘上默默等待,等待下一处JVM的启动,把序列化的对象,通过反序列化为原来的对象,并且序列化的二进制序列能够减少存储空间(永久性保存对象)
  2. 序列化成字节流的对象可以进行网络传输(二进制形式),方便网络传输
  3. 通过序列化可以在进程间传递对象

请添加图片描述

代码示例

Student类:

public class Student implements Serializable {

    //Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号
    //这里没有手动写出来,Java虚拟机会默认提供这个序列化版本号

    private String name ;
    private  int age ;

    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 Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "姓名:"+name+"\n"+"年龄:"+age;
    }
}

【序列化】:

public class ObjectOutputStreamTest01 {
    public static void main(String[] args) {
        //创建java对象
        Student student = new Student("佘楽",21);

        ObjectOutputStream objectOutputStream = null ;

        try {
            //序列化
             objectOutputStream = new ObjectOutputStream(new FileOutputStream("E:\\Typora笔记\\JavaSE\\IO流\\students"));
            //序列化对象
             objectOutputStream.writeObject(student);

             //刷新
             objectOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (objectOutputStream!=null) {
                try {
                    //关闭
                    objectOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

1 、报错NotSerializableException:Student对象不支持序列化

2、参与序列化和反序列化的对象,必须实现Serializable接口

3、注意通过源代码发现,Serializable接口只是一个标志接口:

​ public interface Serializable{ }

这个接口当中什么代码都没有

4、那么它起到什么作用呢?

起到标识的作用,标志的作用,Java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇,Serializable这个标志接口是Java虚拟机参考的,Java虚拟机看到这个接口之后,会为该类自动生成一个序列化版本号

【反序列化】:

public class ObjectInputStreamTest01 {
    public static void main(String[] args) {
        ObjectInputStream objectInputStream = null ;

        try {
            objectInputStream = new ObjectInputStream(new FileInputStream("E:\\Typora笔记\\JavaSE\\IO流\\students"));

            //反序列化,读
           Object obj =  objectInputStream.readObject();
           //反序列化回来是一个学生对象,所以会调用学生对象的toString()方法
            System.out.println(obj);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (objectInputStream!=null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • 一次序列化多个对象呢?

    可以,可以将对象做到集合当中,序列化集合

  • 提示:

    ​ 参与序列化的ArrayList集合以及集合中的元素Student都需要实现java.io.Serializable接口

【一次序列化多个对象】:

public class ObjectOutputStreamTest02 {
    public static void main(String[] args) {
        List<Student> list = new ArrayList<>();
        list.add(new Student("唐蛇皮",21));
        list.add(new Student("葛蔓蔓",22));
        list.add(new Student("佘楽樂",21));

        ObjectOutputStream out = null ;

        try {

            out = new ObjectOutputStream(new FileOutputStream("E:\\Typora笔记\\JavaSE\\IO流\\users"));

            //序列化一个集合,这个集合对象中放了很多其他对象
            out.writeObject(list);

            out.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (out!=null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【反序列化多个对象】:

public class ObjectInputStreamTest02 {
    public static void main(String[] args) {

        ObjectInputStream objectInputStream = null ;

        try {
             objectInputStream = new ObjectInputStream(new FileInputStream("E:\\Typora笔记\\JavaSE\\IO流\\users"));

//             Object obj  =  objectInputStream.readObject();
//            System.out.println(obj);

             //判断obj是不是一个List集合
//             System.out.println(obj instanceof List);

            List<Student> studentlist = (List<Student>)objectInputStream.readObject();
            for (Student student : studentlist) {
                System.out.println(student);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (objectInputStream!=null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

运行结果:

姓名:唐蛇皮年龄:21
姓名:葛蔓蔓年龄:22
姓名:佘楽樂年龄:21

如果不用集合,直接存储多个对象,反序列化有什么不同?

存储第二个对象的时候会报错,所以需要List集合


补充:

当希望Student对象中的某个对象不参与序列化时怎么办

使用transient关键字 : 表示游离,不参与序列化

以上代码Student为例

//表示name不参与序列化操作
private transient String name ; 

Student中name添加transient关键字之后运行结果:

姓名:null年龄:21
姓名:null年龄:22
姓名:null年龄:21

添加关键字transient关键字之后,要把序列化程序在执行一边,不然会报错

Student类中添加版本号

    //建议将序列化版本号手动的写出来,不建议自动生成【自动生成快捷键+alt+回车】
    private  static final long serialVersionUID = 1L;//JAVA虚拟机识别一个类

补充:

当过了很久,Student这个类源代码改动了

源代码改动之后,需要重新编译,编译之后会生成全新的字节码文件

并且class类再次运行的时候,Java虚拟机生成的序列化版本号也会发生相应的改变

在Student类中和添加新的属性之后,在执行序列化会报错,如下:

java.io.InvalidClassException:com.IO.lesson07.Student; 
		local class incompatible:
        stream classdesc serialVersionUID = 3456139176802951041, 
        local class serialVersionUID = 1

版本号具有唯一性

序列化版本号有什么用?

Java语言中是采用什么机制来区分类的?

第一:首先通过类名进行比对,如果类名不一样,肯定不是同一类

第二: 如果类名一样,就会根据版本号进行区分

例如:

当不同的人编写同一个类,但“这两个类确定不是同一个类”。这时候序列化版本号就起上作用了

对于Java 虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口

都有默认的序列化版本号,他们的序列化版本号不一样,就区分开了(这是自动生成版本号的好处)


请思考?

​ 这种自动生成序列化版本号有什么缺陷?

​ 这种自动生成的序列化版本号缺点就是:一旦代码确定后,不能进行后续的修改

​ 因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类


最终结论:

凡是一个实现Serializable接口,建议给该类提供一个固定不变的序列化版本号,这样,以后这个类即使代码修改了,但是版本号不变,Java虚拟机会认为是同一个类

【建议将序列化版本号手动写出来,不建议自动生成】

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值