Java高级特性
第三章 输入和输出流(二)
一、字符流
1、为了提高对于字符的读取和写入效率,在字节流的基础之上,完善了对于字符的编码,形成了字符流。
2、字符流有两个顶级接口:Reader和Writer。
其中FileReader和FileWriter属于两个最基本的字符输入流和字符输出流。
3、对于计算机的编码,最初是由美国标准局所提供的一个字节编码,即ASCII编码。
ASCII编码使用了一个字节的7bit的编码,也就是说最高位保持为0,因此编码的个数为0~127。
4、由于所有的编码方案,0~127都采用的是ASCII编码,英文就不会产生乱码现象。但是从128开始,各个编码方案都不相同,因此也就产生了乱码的现象。
5、在1980年左右,中国开始设计自己的编码方案,采用2个字节来编码汉字,形成了最初的编码方案GB-2312。
GB-2312只编写了最常用的汉字,不包括繁体字。
6、在GB-2312的基础上,对改变吗方案进行了进一步的扩充,形成了GBK编码方案。
目前,中文软件都会默认采用GBK编码方案。
7、国际组织为了统一全球的编码方案,提出了Unicode编码,称之为“万国码”。
8、在Unicode编码的影响下,诞生了UTF-8和UTF-16等具体的编码方案。
(1)UTF-8以字节为单位对Unicode进行编码。
(2)UTF-16编码以16位无符号整数为单位。
二、FileReader
1、使用FileReader字符流读取文本内容
// 1、根据所要读取文件路径创建对应的 File 对象
File file = new File( "E:\\src.txt");
// 2、初始化 FileReader 类型变量
FileReader fr = null;
try {
// 3、根据 File 创建 FileReader
fr = new FileReader(file);
// 对于每次读取的内容,将由原来一个字节编码变了一个字符的 Unicode 编码
int i;
while ((i = fr.read()) != -1) {
System.out.println(i);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//5、关闭流
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
解决读取文件时出现乱码的情况:
方法一:
fr = new FileReader(file,Charset.forName("GBK"));
方法二:
String str = new String();
byte[] bytes = str.getBytes("GBK");
str = new String(bytes,"UTF-8");
2、BufferedReader
为了提高对字符进一步操作,字符流提供了包装流来进行操作,其中BufferedReader则是提供了缓冲区的字符流。
(1)读取文件的内容
public static void main(String[] args) {
// 1、根据读取文件的路径,创建其对应的 File 对象
File file = new File(“E:\test1.txt”);
// 2、初始化 FileReader
FileReader fr = null;
//3、 初始化 BufferedReader
BufferedReader br = null;
try {
//4、 通过 File 对象创建 FileReader 对象
fr = new FileReader(file);
// 5、使用 FileReader 创建 BufferedReader 对象
br = new BufferedReader(fr);
// 借助于包装流来读取信息
// 6、此时每次读取的信息就是一个完整的 String 类型的值
String msg;
// 由于使用了 BufferedReader 那么其读取结束的标志就是当 msg 为 null 时。
while ((msg = br.readLine()) != null) {
// 此时的 readLine() 表示一次可以读取一行的内容
System.out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//7、关闭流
br.close();
fr.close();
// 如果直接关闭最外层的包装流,那么就会连同里面的所有流都进行关闭。
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、FileWriter
1、使用FileWriter字符流完成对文件的书写
public static void main(String[] args) {
// 1、根据所需要写出的文件路径创建对应的 File 对象
File file = new File("E:\\123.txt");
// 2、初始化 FileWriter 类型变量
FileWriter fw = null;
try {
// 3、根据 File 对象创建 FileWriter 对象
fw = new FileWriter(file);
// 4、此时使用 FileWriter 可以一次性的将所有信息写出
fw.write("今天的天气很好!!!");
System.out.println("信息写入成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 5、关闭输出流
fw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:字符输出流会默认将数据缓冲到流中,而不会写入到文件中,则必须通过调用flush()或者是close()来强制将缓冲的数据写入到文件中。
2、BufferedWriter
public static void main(String[] args) {
// 1、通过所要写入文件的路径创建 File 对象
File file = new File("E:\\test4.txt");
// 2、初始化 FileWriter 类型变量
FileWriter fw = null;
// 3、初始化 BufferedWriter 类型变量
BufferedWriter bw = null;
try {
// 4、根据 File 对象创建 FileWriter 对象
fw = new FileWriter(file,true);
// 5、根据 FileWriter 对象创建 BufferedWriter 对象
bw = new BufferedWriter(fw);
// 6、通过 BufferedWriter 写入文件信息
bw.write("白日依山尽,\r\n");
// 如果需要用到 BufferedWriter 在输出时产生换行,那么可以使用 newLine()
// bw.newLine();
bw.write("黄河入海流。");
bw.newLine();
bw.write("欲穷千里目,");
bw.newLine();
bw.write("更上一层楼。");
System.out.println("文件写入成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 6、关闭输出流
bw.close();
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(1)关闭流的顺序
- 先打开的后关闭,后打开的先关闭;
- 看依赖关系,如果流a依赖流b,应先关流a,再关流b。
(2)BufferedWriter输出内容换行
- bw.newLine();
- “\n”—MAC系统
“\r\n”—Windows系统
“\r”----linux/unix系统
(3)System.out.println()
其本质就是首先获得系统所提供的一个标准输出流,即System.out。然后再通过该输出流的println()方法将信息写入控制台中。
(4)System.in
Scanner 在创建时,所需要的 System.in 则是一个标注的输入流,将控制台中的数据写入到程序中。
四、InputStreamReader和OutputStreamWriter
如果需要将字节流变为字符流,则可以分别使用:InputStreamReader 和 OutputStreamWriter 来完成。
// 1、根据所要读取的文件路径,创建 File 对象
File file = new File("E:\\ab.txt");
//2、 初始化 FileInputStream 类型变量
FileInputStream in = null;
// 3、初始化 InputStreamReader 类型变量
InputStreamReader isr = null;
//4、 初始化 BufferedReader 类型变量
BufferedReader br = null;
try {
// 5、使用 File 对象创建 FileInputStream
in = new FileInputStream(file);
// 6、使用 FileInputStream 创建 InputStreamReader
isr = new InputStreamReader(in, Charset.forName("GBK"));
// 7、使用 InputStreamReader 创建 BufferedReader
br = new BufferedReader(isr);
//8、 读取文件时,就可以使用 BufferedReader
String msg;
while ((msg = br.readLine()) != null) {
System.out.println(msg);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
// 9、关闭流
br.close();
isr.close();
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
五、使用对象流读写对象信息
当创建好一个对象之后,该对象就是内存中一个存在的数据,那么只要是在程序中所产生的数据,就可以通过输出流写入到文件中。或者使用输入流将数据传入到程序中,要想完成对对象的读入和写出,则需要使用 ObjectInputStreamReader 和 ObjectOutputStream。
1、ObjectInputStream
反序列化:从硬盘中读取数据并重新构建成为对象的过程。
反序列化的步骤:
(1)创建一个对象输入流(ObjectInputStream),它可以包装一个其他类型的输入流,如文件输入流FileInputStream。
(2)通过对象输入流的readObject()方法读取对象,该方法返回一个Object类型的对象,如果程序知道该Java对象的类型,则可以将该对象强制转换成其真实的类型。
public static void main(String[] args) {
// 1、创建所要读取文件的 File 对象
File file = new File("E:\\user.txt");
// 2、分别初始化 FileInputStream 和 ObjectInputStream 对象
FileInputStream in = null;
ObjectInputStream ois = null;
try {
// 3、根据 File 对象创建 FileInputStream 对象
in = new FileInputStream(file);
// 4、根据 FileInputStream 对象创建 ObjectInputStream 对象
ois = new ObjectInputStream(in);
// 5、通过 ObjectInputStream 对象读取文件中的数据,
// 6、在进行反序列化,能够获得一个对象
User user = (User) ois.readObject();
System.out.println(user);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
//6、关闭流
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
fanxu
2、ObjectOutputStream
序列化:把对象类型信息及数据转换为字节流数据存储在硬盘上的过程。
序列化的步骤:
(1)创建一个对象输出流(ObjectOutputStream),它可以包装一个其他类型的输出流,如文件输出流FileOutputStream。
(2)通过对象输出流的writeObject()方法写对象,也就是输出可序列化对象。
//用户类
public class User implements Serializable {
private static final long serialVersionUID = -2534573284634670147L;
//属性
private String name;
private String cellphone;
//构造方法
public User() {}
public User(String name, String cellphone) {
this.name = name;
this.cellphone = cellphone;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCellphone() {
return cellphone;
}
public void setCellphone(String cellphone) {
this.cellphone = cellphone;
}
//重写toString()方法
@Override
public String toString() {
return name + "\t" + cellphone;
}
}
public static void main(String[] args) {
// 1、创建 User 类型的对象
User user = new User("张三", "9527");
//2、 根据写入文件的路径创建 File 对象
File file = new File("E:\\user.txt");
// 3、初始化 FileInputStream 类型变量
FileOutputStream out = null;
// 4、为了能够将对象写入到文件中,则需要使用 ObjectOutputStream 类型的对象
ObjectOutputStream oos = null;
try {
//5、 使用 File 对象创建 FileOutputStream 对象
out = new FileOutputStream(file);
// 6、使用 FileOutputStream 对象创建 ObjectOutputStream 对象
oos = new ObjectOutputStream(out);
//7、 使用 ObjectOutputStream 对象的 writeObject() 将对象写入到文件中
oos.writeObject(user);
System.out.println("对象写入成功!");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//8、关闭流
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注意:实体类需要实现Serializable 接口。