JavaSE——IO(下)(Properties类、序列化与反序列化)

第3节 IO(下)

一、.properties文件与Properties类

1.1 .properties文件介绍

.properties文件一种属性文件,以键值对 的格式存储内容,在Java中可以使用Properties类来读取这个文件,一般来说它作为一些参数的存储,作为配置文件使用。

1.2 Properties类

它是Hashtable的子类,使用键值对来存储数据。

构造方法:

在这里插入图片描述

方法:

1、根据键获取值:

在这里插入图片描述

2、将键值对打印到指定的输出流:

在这里插入图片描述

3、从输入字节流或字符流中读取全部内容:

在这里插入图片描述

4、返回所有键的枚举:

在这里插入图片描述

5、新增键值对:

在这里插入图片描述

6、存储到字节输出流或字符输出流,comment是备注,写在存储文件的开头:

在这里插入图片描述

7、返回所有的值,以Set类型:

在这里插入图片描述

看个例子:

package com.kaikeba.coreclasslibrary.io;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.util.Properties;

public class properties {
    public static void main(String[] args) throws IOException {
        //.properties文件与Properties类
        //新建一个Properties对象
        Properties ppt = new Properties();
        //新建键值对
        ppt.setProperty("name","三体");
        ppt.setProperty("info","科幻小说");
        //要写入的字符流
        FileWriter fw = new FileWriter("book.properties");
        //往fw中存储该键值对集合的对象,备注为"存储的图书"
        ppt.store(fw, "存储的图书");
        fw.close();
        
        //从.properties文件读取属性
        Properties ppt2 = new Properties();
        Reader r = new FileReader("book.properties");
        ppt2.load(r);
        System.out.println(ppt.get("name"));
        System.out.println(ppt.get("info"));
    }
}

输出为:
三体
科幻小说

且book.properties文件中存储的内容为:

在这里插入图片描述

最上面是备注信息,使用了Unicode编码。

二、序列化与反序列化

Java序列化是指把java对象转换为字节序列的过程 ,Java反序列化是指把字节序列恢复为java对象的过程通过序列化和反序列化实现网络传输、本地存储的目的

在这里插入图片描述

2.1 Serializable实现Java序列化

要实现java对象的序列化,只要将类实现标识接口 ——Serializable接口即可,不需要我们重写任何方法就可以实现序列化。

编写书籍类:

package com.kaikeba.coreclasslibrary.io.serializable;

import java.io.Serializable;

public class Book implements Serializable {
    private String name;
    private String info;
    private Person user;

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", info='" + info + '\'' +
                ", user=" + user +
                '}';
    }

    public Book() {
    }

    public Book(String name, String info, Person user) {
        this.name = name;
        this.info = info;
        this.user = user;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getInfo() {
        return info;
    }

    public void setInfo(String info) {
        this.info = info;
    }

    public Person getUser() {
        return user;
    }

    public void setUser(Person user) {
        this.user = user;
    }
}

编写借书人类:注意Book中有Person类的对象属性,所以Person类也要实现Serializable接口

package com.kaikeba.coreclasslibrary.io.serializable;

import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private int age;

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

    public Person() {
    }

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

编写序列化和反序列化:

package com.kaikeba.coreclasslibrary.io.serializable;

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //序列化
        Person p = new Person("smile", 23);
        Book b = new Book("三体", "科幻小说", p);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("b.txt"));
        oos.writeObject(b);
        oos.close();

        //反序列化
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("b.txt"));
        Book book = (Book) ois.readObject();
        System.out.println(book);
    }
}

结果如下:

原始对象:
Book{name='三体', info='科幻小说', user=Person{name='smile', age=23}}
反序列化之后的对象:
Book{name='三体', info='科幻小说', user=Person{name='smile', age=23}}

主要是使用到了ObjectOutputStreamObjectInputStream类,ObjectOutputStream类使用writeObject将对象写到文件中,ObjectInputStream类使用readObject将对象从文件中读取出来。

2.2 部分属性的序列化

实现部分字段序列化的方式:

  • 使用transient修饰符

  • 使用static修饰符

  • 默认方法writeObject和readObject

  • Externalizable实现

2.2.1 使用transient修饰符

修改实体类,将实体类中不想被序列化的属性添加transient修饰符。

public class Book implements Serializable {
    private String name;
    private transient String info;
    private Person user;
    ... 

   
public class Person implements Serializable {
    private String name;
    private transient int age; 
    ... 

结果如下:

原始对象:
Book{name='三体', info='科幻小说', user=Person{name='smile', age=23}}
反序列化之后的对象:
Book{name='三体', info='null', user=Person{name='smile', age=0}}

transient修饰的属性都没有被序列化,因此反序列化的结果也是默认值。

2.2.2 使用static修饰符

static修饰符修饰的属性会参与序列化,但是如果在反序列化的时候对其进行重新赋值,反序列化后的结果会随之改变:

public class Book implements Serializable {
    private String name;
    private static String info;
    private Person user;
    ...
    
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //序列化
        Person p = new Person("smile", 23);
        Book b = new Book("三体", "科幻小说", p);
        System.out.println("原始对象:\n"+b);
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("b.txt"));
        oos.writeObject(b);
        oos.close();

        //反序列化
        b.setInfo("中国最牛的科幻小说");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("b.txt"));
        Book book = (Book) ois.readObject();
        System.out.println("反序列化之后的对象:\n"+book);
    }
}

结果:

原始对象:
Book{name='三体', info='科幻小说', user=Person{name='smile', age=23}}
反序列化之后的对象:
Book{name='三体', info='中国最牛的科幻小说', user=Person{name='smile', age=23}}

解释:其实就是静态属性被修改之后,因为所有对象都是用的同一个内容,所以反序列化的静态属性也就变了。

2.2.3 默认方法writeObject和readObject
   在Book中添加两个方法:
public class Book implements Serializable {
    private String name;
    private String info;
    private Person user;

    private void writeObject(ObjectOutputStream oos) throws IOException {
        System.out.println("writeObject---------------");
        oos.writeObject(name);
        oos.writeObject(user);
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("readObject----------------");
        name = (String) ois.readObject();
        user = (Person) ois.readObject();
    }
    ...

结果为:

原始对象:
Book{name='三体', info='科幻小说', user=Person{name='smile', age=23}}
writeObject---------------
readObject----------------
反序列化之后的对象:
Book{name='三体', info='null', user=Person{name='smile', age=23}}

info属性没有写入两个函数中,就没有被序列化。

源码分析:

注意:添加的两个方法必须是private void修饰的,否则不生效。

首先看一下Serializable接口的描述:

在这里插入图片描述

ObjectStreamClass类:在序列化(反序列化)的时候,ObjectOutputStreamObjectInputStream)会寻找目标类中私有的writeObjectreadObject)方法,赋值给变量writeObjectMethodreadObjectMethod)。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

通过上面这段代码可以知道,如果writeObjectMethod ≠ null(目标类中定义了私有的writeObject方法),那么将调用目标类中的writeObject方法,如果writeObject == null,那么将调用默认的defaultWriteMethod方法来读取目标类中的属性。

readObject的调用逻辑和WriteObject一样。

总结一下,如果目标类中没有定义私有的writeObjectreadObject方法,那么序列化和反序列化的时候讲调用默认的方法来根据目标类中的属性来进行序列化和反序列化,而如果目标类中定义了私有的writeObjectreadObject方法,那么序列化和反序列化的时候讲调用目标类指定的writeObjectreadObject方法来实现。

2.3 Externalizable实现java序列化

实现部分属性序列化的方式的第四种,通过Externalizable接口。

Externalizable继承自Serializable接口,使用Externalizable接口需要实现readExternal方法和writeExternal方法来实现序列化和反序列化。

在这里插入图片描述

Externalizable接口继承自Serializable接口,所以实现Externalizable接口也能实现序列化和反序列化。Externalizable接口中定义了writeExternalreadExternal两个抽象方法,这两个方法其实对应Serializable接口的writeObjectreadObject方法,可以这样理解:Externalizable接口被设计出来的目的就是为了抽象出writeObjectreadObject这两个方法,但是目前这个接口使用的并不多。

三、Serializable vs Externalizable

区别SerializableExternalizable
实现复杂度实现简单,java对其有内建支持实现复杂,由开发人员自己完成
执行效率所有对象由java统一保存,性能较低开发人员决定哪个对象保存,可能造成速度提升
保存信息保存时占用空间大部分存储,可能造成空间减少
使用频率偏低
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值