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个流,按照数据类型分类:
- 字节缓冲流:
BufferedInputStream
,BufferedOutputStream
- 字符缓冲流:
BufferedReader
,BufferedWriter
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统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
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。