大数据必学Java基础七十四:对象流ObjectInputStream和ObjectOutputStream介绍

​对象流ObjectInputStream和ObjectOutputStream介绍

一、对象流

用于存储和读取基本数据类型数据或对象的处理流。

它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。

二、序列化和反序列化

ObjectOutputStream 类 : 把内存中的Java对象转换成平台无关的二进制数据,从而允许把这种二进制数据持久地保存在磁盘上,或通过网络将这种二进制数据传输到另一个网络节点。 -- 序列化

用ObjectInputStream类 : 当其它程序获取了这种二进制数据,就可以恢复成原来的Java对象。 -- 反序列化

三、代码(操作字符串对象)

首先将一个字符串对象写到文件中去:序列化

public class Test01 {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) throws IOException {

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo3.txt")));
        //将内存中的字符串写出到文件中:
        oos.writeObject("你好");
        //关闭流:
        oos.close();
    }
}

查看文件:

我们看不懂文件的内容,但是程序是可以看懂的,所以可以写一个程序读文件中内容(反序列化)

public class Test02 {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //将文件中保存的字符串 读入到 内存:
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo3.txt")));
        //读取:
        String s = (String)(ois.readObject());
        System.out.println(s);
        //关闭流:
        ois.close();
    }
}

控制台:

四、代码(操作自定义类的对象)

自定义的Person类

public class Person {
    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 Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

测试类:

public class Test01 {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) throws IOException {
        //序列化:将内存中对象 ---》 文件:
        //有一个对象:
        Person p = new Person("lili",19);
        //有对象流:
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:\\Demo4.txt")));
        //向外写:
        oos.writeObject(p);
        //关闭流:
        oos.close();
    }
}

运行的时候发现出现异常:

出现异常的原因:

你想要序列化的那个对象对应的类,必须要实现一个接口:

接口内部,什么都没有,这种接口叫 标识接口。

起到标识作用,标识什么呢?只要实现这个接口的类的对象才能序列化,否则不可以。

解决办法:

将Person 实现这个标识接口就可以

public class Person implements Serializable {
    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 Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

测试:

发现序列化成功,Person具备了序列化的能力。

这个二进制数据我们看不懂,但是程序可以看懂,所以我们可以用程序实现反序列化操作:

将这个对象恢复到内存中来:

public class Test02 {
    //这是一个main方法,是程序的入口:
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:\\Demo4.txt")));
        //读入内存:
        Person p = (Person)(ois.readObject());
        System.out.println(p/*.toString()*/);
        //关闭流:
        ois.close();
    }
}

结果:

因为我们没有重写toString方法,所以结果为

证明了反序列化成功,将二进制数据 -- 内存

五、serialVersionUID

凡是实现Serializable接口(标识接口)的类都有一个表示序列化版本标识符的静态常量:

private static final long serialVersionUID;

serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象进行版本控制,有关各版本反序加化时是否兼容。

如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialVersionUID 可能发生变化。故建议,显式声明。

简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(InvalidCastException)

现在在Person类中加入toString方法:

public class Person implements Serializable {
    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 Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

再次运行测试类,出现异常:

出现异常的原因:

解决:

给这个类 加入一个 序列号:serialVersionUID

六、IDEA中配置序列化版本号

在Person类上:alt+enter

回车即可生成

七、序列化细节

1、被序列化的类的内部的所有属性,必须是可序列化的 (基本数据类型都是可序列化的)

2、static,transient修饰的属性 不可以被序列化

public class Person implements Serializable {

    private static final long serialVersionUID = 8027651838638826533L;
    private transient String name;
    private static int age;
    private Famaily f = new Famaily();

    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 Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", f=" + f + ",age=" + age +
                '}';
    }
}

结果:

相关文章

摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠...
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠...
今天犯了个错:“接口变动,伤筋动骨,除非你确定只有你一个...
Writer :BYSocket(泥沙砖瓦浆木匠)微 博:BYSocket豆 瓣:...
本文目录 线程与多线程 线程的运行与创建 线程的状态 1 线程...