java之 属性集 & 缓冲流 & 序列化流

0x01、属性集

1、Properties类的概述

java.util.Properties 继承于 Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多Java类使用,比如获取系统属性时,System.getProperties 方法就是返回一个Properties对象。加载文件中配置信息等等

2、Properties类的构造方法

public Properties()` :创建一个空的属性列表。

3、Properties类存储方法

public Object setProperty(String key, String value) : 保存一对属性。  
public String getProperty(String key)  :使用此属性列表中指定的键搜索属性值。
public Set<String> stringPropertyNames()  :所有键的名称的集合。

示例代码:

public static void main(String[] args) throws FileNotFoundException {
        // 创建属性集对象
        Properties properties = new Properties();
        // 添加键值对元素
        properties.setProperty("filename", "a.txt");
        properties.setProperty("length", "209385038");
        properties.setProperty("location", "D:\\a.txt");
        // 打印属性集对象
        System.out.println(properties);
        // 通过键,获取属性值
        System.out.println(properties.getProperty("filename"));
        System.out.println(properties.getProperty("length"));
        System.out.println(properties.getProperty("location"));

        // 遍历属性集,获取所有键的集合
        Set<String> strings = properties.stringPropertyNames();
        // 打印键值对
        for (String key : strings ) {
          	System.out.println(key+" -- "+properties.getProperty(key));
        }
    }

输出结果:

{filename=a.txt, length=209385038, location=D:\a.txt}
a.txt
209385038
D:\a.txt
filename -- a.txt
length -- 209385038
location -- D:\a.txt

4、Properties类与流相关的方法

public void load(InputStream inStream): 从字节输入流中读取键值对。
public void load(Reader reader): 从字节输入流中读取键值对。 

参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。

加载代码演示:

public class Test3 {
    public static void main(String[] args) throws Exception{
        /*
		 注意:
          1.文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。
          2.如果配置文件中有中文,那么加载文件文件时,使用字符流,但是开发中一般配置文件中不要写中文
         */
        
        // 创建Properties对象
        Properties pro = new Properties();

        // 往pro对象中添加键值对
        pro.setProperty("username", "root");
        pro.setProperty("password", "123456");
        pro.setProperty("url", "http://www.baidu.com");

        // 把pro对象中的键值对写入到文件中
        FileOutputStream fos = new FileOutputStream("db2.properties");
        pro.store(fos,"itheima");

        // 关闭流
        fos.close();


    }

加载(读取)配置文件中的数据

    private static void method01() throws IOException {
        // 1.加载(读取)配置文件中的数据
        // 创建Properties对象
        Properties pro = new Properties();

        // 调用load方法加载配置文件中的数据
        //FileInputStream fis = new FileInputStream("a.txt");
        FileInputStream fis = new FileInputStream("db2.properties");
        pro.load(fis);// 把关联文件中的数据以键值对的形式存储到pro对象中

        // 关闭流,释放资源
        fis.close();

        // 打印pro对象
        System.out.println(pro);

        System.out.println("--------------------------------------------------");
        // 注意事项: 如果配置文件中有中文 使用字符流
        // 创建Properties对象
        Properties pro2 = new Properties();

        // 调用load方法加载配置文件中的数据
        FileReader fr = new FileReader("a.txt");
        pro2.load(fr);// 把关联文件中的数据以键值对的形式存储到pro对象中

        // 关闭流,释放资源
        fr.close();

        // 打印pro对象
        System.out.println(pro2);
    }
}

0x02、缓冲流

缓冲流,也叫高效流,是对4个基本的FileXxx 流的增强,所以也是4个流,按照数据类型分类:

  • 字节缓冲流BufferedInputStreamBufferedOutputStream
  • 字符缓冲流BufferedReaderBufferedWriter

缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。

1、字节缓冲流

public BufferedInputStream(InputStream in) :创建一个 新的缓冲输入流。 
public BufferedOutputStream(OutputStream out): 创建一个新的缓冲输出流。

构造举例,代码如下:

// 创建字节缓冲输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));

使用缓冲流读写一个字节拷贝文件

public class BufferedDemo {
    public static void main(String[] args) throws Exception {
       long start = System.currentTimeMillis();
        // 创建字节输入流对象,关联数据源文件路径
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("B_test.exe"));
        // 创建字节输出流对象,关联目的地文件路径
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E_test.exe"));
        // 定义一个int类型的变量,用来存储读取到的字节数据
        int len;
        // 循环读取
        while ((len = bis.read()) != -1) {
            // 在循环中,写出数据
            bos.write(len);
        }
        // 关闭流,释放资源
        bos.close();
        bis.close();
        long end = System.currentTimeMillis();
        System.out.println("总共需要:" + (end - start) + "毫秒");// 大约32秒
    }
}

使用缓冲流读写一个数组字节拷贝文件

public class BufferedDemo {
    public static void main(String[] args) throws FileNotFoundException {
        long start = System.currentTimeMillis();
        // 创建字节输入流对象,关联数据源文件路径
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("B_test.exe"));
        // 创建字节输出流对象,关联目的地文件路径
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E_test.exe"));
        // 定义一个字节数组,用来存储读取到的字节数据
        byte[] bys = new byte[8192];
        // 定义一个int类型的变量,用来存储读取到的字节数据
        int len;
        // 循环读取
        while ((len = bis.read(bys)) != -1) {
            // 在循环中,写出数据
            bos.write(bys,0,len);
        }
        // 关闭流,释放资源
        bos.close();
        bis.close();
        long end = System.currentTimeMillis();
        System.out.println("总共需要:" + (end - start) + "毫秒");// 大约4秒
    }
}

0x03、序列化流

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。看图理解序列化:

1、ObjectOutputStream类

(1)构造方法:

public ObjectOutputStream(OutputStream out): 创建一个指定OutputStream的ObjectOutputStream。

代码示例:

FileOutputStream fileOut = new FileOutputStream("employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);

一个对象要想序列化,必须满足两个条件:

  • 该类必须实现java.io.Serializable 接口,Serializable 是一个标记接口
public final void writeObject (Object obj) : 将指定的对象写出。

示例代码:

public class Test {
    public static void main(String[] args)throws Exception {
        // 创建Student对象
        Student s = new Student("张三", 18);
        // 需求:将s对象写入到 C:\\Users\\86183\\Desktop\\ser.txt 文件中
        
        // 创建序列化流对象,关联目的地文件路径
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\86183\\Desktop\\ser.txt"));
        // 写出对象
        oos.writeObject(s);
        // 关闭流,释放资源
        oos.close();
    }
}

 对象序列化包括如下步骤:

1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2) 通过对象输出流的writeObject()方法写对象。

2、ObjectInputStream类

ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。

(1)构造方法

public ObjectInputStream(InputStream in) : 创建一个指定InputStream的ObjectInputStream

如果能找到一个对象的class文件,我们可以进行反序列化操作,调用ObjectInputStream读取对象的方法:

public final Object readObject () : 读取一个对象。

示例代码:

public class Test {
    public static void main(String[] args) throws Exception{
        // 反序列化:读取之前序列化的对象
        // 1.创建反序列化流对象,关联数据源文件路径
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\Users\86183\Desktop\ser.txt"));
        // 2.读取一个对象
        Object obj = ois.readObject();
        System.out.println(obj);
        // 3.关闭流,释放资源
        ois.close();
    }
}

对象反序列化的步骤如下:

1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2) 通过对象输入流的readObject()方法读取对象。

3、小结

  我们可以看到,先通过输入流创建一个文件,再调用ObjectOutputStream类的 writeObject方法`,把序列化的数据写入该文件;

​ 然后调用ObjectInputStream类的readObject方法反序列化数据并打印数据内容

反序列化的注意事项

  • 对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个 ClassNotFoundException 异常。

  • 另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:

    • 该类的序列版本号与从流中读取的类描述符的版本号不匹配
    • 该类包含未知数据类型
    • 该类没有可访问的无参数构造方法

Serializable 接口给需要序列化的类,提供了一个序列版本号。serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

posted @ 2021-01-24 12:13  0X7e  阅读(85)  评论(0编辑  收藏  举报