day12【IO 流】
第一章 转换流
1.1 编码引出的问题
在 IDEA 中,使用FileReader
读取项目中的文本文件。由于 IDEA 的设置,都是默认的UTF-8
编码,所以没有任何问题。但是,当读取 Windows 系统中创建的文本文件时,由于 Windows 系统的默认是 GBK 编码,就会出现乱码。
public class ReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader("E:\\a.txt");
int read;
while ((read = fileReader.read()) != -1) {
System.out.print((char)read);
}
fileReader.close();
}
}
输出结果:
���
那么如何读取 GBK 编码的文件呢?
1.2 InputStreamReader 类
转换流java.io.InputStreamReader
,是 Reader 的子类,是 FileReader 的父类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
InputStreamReader(InputStream in,String charset)//可以设置编码
InputStreamReader(InputStream in) //使用默认的编码集 FileReader跟他一样,而且还短。
构造方法
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。
构造举例,代码如下:
InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt"));
InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
指定编码读取
package com.itheima.changeStream;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
public class ChangeStreamDemo02 {
public static void main(String[] args) throws Exception {
//在idea当前的默认utf-8环境下 使用普通的字符输入流读取 Windows中的GBK文件
// 使用 InputStreamReader
// InputStreamReader fr = new InputStreamReader(底层字节流,"要解析的文件的字符编码");
//先创建字节流
FileInputStream in = new FileInputStream("E:\\a.txt");
InputStreamReader isr = new InputStreamReader(in, "GBK");
int read;
while ((read = isr.read()) != -1) {
System.out.print((char)read);
}
isr.close();
in.close();
}
}
什么时候用 FIleReader 什么时候用 InputStreamReader?
如果我们不涉及 编码不同情况,直接使用 FileReader。
如果读取的文件跟我们当前项目不一致,那么就是用 InputStreamReader.
1.3 OutputStreamWriter 类
转换流java.io.OutputStreamWriter
,是 Writer 的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
构造方法
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。OutputStreamWriter(OutputStream in, String charsetName)
: 创建一个指定字符集的字符流。
构造举例,代码如下:
OutputStreamWriter isr = new OutputStreamWriter(new FileOutputStream("out.txt"));
OutputStreamWriter isr2 = new OutputStreamWriter(new FileOutputStream("out.txt") , "GBK");
指定编码写出
package com.itheima.changeStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
public class ChangeStreamDemo03 {
/*
FileWriter 写出去文件 都是默认的 编码 UTF-8
我想让写出去的文件 是 GBK编码的呢?
它的父类
OutputStreamWriter
OutputStreamWriter(OutputStream out,String charset) //按照指定编码写数据
OutputStreamWriter(OutputStream out) //按照默认编码写数据
*/
public static void main(String[] args)throws Exception {
//创建 字节输出流
FileOutputStream fos = new FileOutputStream("day12\\gbk_file.txt");
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
//写中文
osw.write("你好");
osw.close();
fos.close();
}
}
转换流理解图解
字符流=字节流+编码表
InputStreamReader 是字节流通向字符流的桥梁 解码 看不懂字节--看得懂字符
OutputStreamWriter 是字符流通向字节流的桥梁: 编码 看得懂字符--看不懂的字节
转换流是字节与字符间的桥梁!
第二章 Properties 集合
2.1 概述
java.util.Properties
继承于 Hashtable
,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。该类也被许多 Java 类使用,比如获取系统属性时,System.getProperties
方法就是返回一个Properties
对象。
2.2 Properties 类
构造方法
public Properties()
:创建一个空的属性列表。
与流相关的方法
public void load(InputStream inStream)
: 从字节输入流中读取键值对。public void load(Reader reader)
: 从字符输入流中读取键值对。
参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。文本数据格式:
filename=a.txt
length=209385038
location=D:\a.txt
加载代码演示:
package com.itheima.properties;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.Properties;
public class Demo {
public static void main(String[] args) throws Exception{
//有个文件 db.properties 放着数据库的 用户名和密码
//读取这个文件
FileReader fr = new FileReader("day12\\db.properties");
//只需要 创建Properties 通过load方法 就可以把流中读取到的 键值对进行解析 存到该集合中
Properties pp = new Properties();
pp.load(fr);
System.out.println(pp);
// 写出去--开发中不做 知道有
FileWriter fw = new FileWriter("day12\\cc.properties");
pp.store(fw,"this is database username and password");
fw.close();
fr.close();
}
}
第三章 序列化
3.1 概述
Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据
、对象的类型
和对象中存储的属性
等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据
、对象的类型
和对象中存储的数据
信息,都可以用来在内存中创建对象。
序列化,就是把我们的对象转换为一个字节序列写出去。这叫对象序列化。
反序列化,就是把字节序列重构成一个对象,这叫对象的反序列化。
想要完成序列化 需要 对象输出流 ObjectOutputStream 也可以叫做序列化流。
反之 ObjectInputStrean 可以叫做 对象输入流 反序列化流。
3.2 ObjectOutputStream 类
java.io.ObjectOutputStream
类,将 Java 对象的原始数据类型写出到文件,实现对象的持久存储。
构造方法
public ObjectOutputStream(OutputStream out)
: 创建一个指定 OutputStream 的 ObjectOutputStream。
构造举例,代码如下:
FileOutputStream fileOut = new FileOutputStream("employee.txt");
ObjectOutputStream out = new ObjectOutputStream(fileOut);
序列化操作
- 一个对象要想序列化,必须满足两个条件:
- 该类必须实现
java.io.Serializable
接口,Serializable
是一个标记接口,不实现此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException
。 - 该类的所有属性必须是可序列化的。如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用
transient
关键字修饰。
public class Employee implements java.io.Serializable {
public String name;
public String address;
public transient int age; // transient瞬态修饰成员,不会被序列化
public void addressCheck() {
System.out.println("Address check : " + name + " -- " + address);
}
}
package com.itheima.objectstream;
import java.io.Serializable;
public class Student implements Serializable {
private String name;
private int age;
public Student() {
}
public Student(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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2.写出对象方法
public final void writeObject (Object obj)
: 将指定的对象写出。
package com.itheima.objectstream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Demo01 {
public static void main(String[] args) throws IOException {
/*
ObjectOutputStream(OutputStream out)
*/
//创建字节输出流 因为最终是按照字节
FileOutputStream fos = new FileOutputStream("day12\\student.stu");
//创建序列化流 --对象输出流
ObjectOutputStream oos= new ObjectOutputStream(fos);
Student stu = new Student("jack马",60);
//实现一个对象序列化
oos.writeObject(stu);
oos.close();
fos.close();
}
}
3.3 ObjectInputStream 类
ObjectInputStream 反序列化流,将之前使用 ObjectOutputStream 序列化的原始数据恢复为对象。
构造方法
public ObjectInputStream(InputStream in)
: 创建一个指定 InputStream 的 ObjectInputStream。
反序列化操作 1
如果能找到一个对象的 class 文件,我们可以进行反序列化操作,调用ObjectInputStream
读取对象的方法:
public final Object readObject ()
: 读取一个对象。
package com.itheima.objectstream;
import java.io.*;
public class Demo02 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
/*
ObjectInputStream(InputStream in)
*/
//创建字节输入流
FileInputStream fis = new FileInputStream("day12\\student.stu");
//创建 反序列化流 对象 输入流
ObjectInputStream ois = new ObjectInputStream(fis);
//什么叫反序列化 把字节文件中的字节序列 重构回对象
Object o = ois.readObject();
System.out.println(o);
}
}
对于 JVM 可以反序列化对象,它必须是能够找到 class 文件的类。如果找不到该类的 class 文件,则抛出一个 ClassNotFoundException
异常。
反序列化操作 2
**另外,当 JVM 反序列化对象时,能找到 class 文件,但是 class 文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException
异常。**发生这个异常的原因如下:
- 该类的序列版本号与从流中读取的类描述符的版本号不匹配
- 该类包含未知数据类型
- 该类没有可访问的无参数构造方法
Serializable
接口给需要序列化的类,提供了一个序列版本号。serialVersionUID
该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
package com.itheima.objectstream02;
import java.io.Serializable;
public class Student implements Serializable {
//序列化版本号
private static final long serialVersionUID = 1L;
// transient 瞬态关键字 那个属性不需要被序列化 就加这个修饰符
public String name;
public transient int age;
public Student() {
}
public Student(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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
如果做复制请使用 字节缓冲流。
如果做图片的上传处理,用字节流。
如果读取文本数据,使用字符流。写文本数据,字符流。
如果我们进行操作的时候,涉及的不同编码文件读写,用转换流。
如果我们要直接读取属性集文件用 Properties.
如果我们要实现对象的序列化 使用序列化流(一般我们不做,但是可能在源码中看到)。