一、IO流的定义
水管承载的是水,油管承载的是油,io流中承载是数据,是数据的载体。
1.按流的方向分:输入流、输出流
以jvm作为参照,将数据从外部读入到jvm中称为输入流;将数据从jvm写入到外部称为输出流。
2.按流的数据单位分:字节流、字符流
字节流可以传输所有的数据,如文本、图片、音频、视频。字符流专门传输文本。
3.按流的功能流:节点流、过滤流
节点流是专门完成传输功能的流,过滤流是对节点流进行增强的流。
二、字节流
字节流的抽象基类InputStream、OutputStream。
1.FileInputStream:
int read():每次从文件中读一个字节,并且把读到的内容返回。返回的是字节对应的ascii码值。
int read(byte[] b):从文件中读b.length个字节到b数组中,返回实际读取到的字节数。
int read(byte[] bs, int off, int len):从文件中读取多个字节存储到bs数组中,len为读取的字节个数,字节在数组的存储位置由off,len决定。返回实际读取到的字节数。
2.FileOutputStream:
void write(int v) : 写入一个字节,int类型的最后一个字节。
void write(byte[] bs) : 写入一个数组。
void write(byte[] bs, int off, int len) :写入数组的一部分,内容由off、len决定。
3.异常处理
使用try…catch... finally规范io流书写过程。
字节流代码示例:
public static void main(String[] args) throws IOException {
//读单个字节
readSingle();
//读多个字节到字节数组
readBytes();
//读多个字节到字节数组的指定位置
readBytesByCondition();
//写一个字节数组到io流中
writeBytes();
}
private static void writeBytes() {
//规范化异常处理过程
OutputStream outputStream = null;
try {
outputStream = new FileOutputStream("D:/writer.txt");
String str = "hello world";
byte[] bytes = str.getBytes();
//将字节数组写入io流中
outputStream.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static void readBytesByCondition() throws IOException {
InputStream inputStream = new FileInputStream("D:/iotest.txt");
byte[] b = new byte[6];
//读取多个字节到字节数组中
while ((inputStream.read(b, 1, 5)) != -1) {
System.out.println(new String(b, "gbk"));
b = new byte[6];
}
inputStream.close();
}
private static void readBytes() throws IOException {
InputStream inputStream = new FileInputStream("D:/iotest.txt");
byte[] b = new byte[6];
//读取多个字节到字节数组中
while ((inputStream.read(b)) != -1) {
System.out.println(new String(b, "gbk"));
}
inputStream.close();
}
private static void readSingle() throws IOException {
FileInputStream inputStream = new FileInputStream("D:\\iotest.txt");
//读取一个字节并返回
int ch;
while ((ch = inputStream.read()) != -1) {
System.out.println(ch);
}
inputStream.close();
}
4.字节过滤流(字符过滤流类似)
关键在于过滤流增强了什么功能,以及在什么情况下要使用过滤流功能。
特征:
1.创建过滤流时需要先创建节点流。节点流才具备数据传输功能。
2.一个节点流可以被多个过滤流封装。
3.关闭流时,只需要关闭最外层,内层流会随着外层流的关闭而关闭。
下面介绍下常用的过滤流:
5.DataOutputStream/DataInputStream
增强点:增强了8中基本类型和字符串的读写功能。
底层实现:底层会将基本类型做字节的拆分,比如double类型会被拆分成8个字节,在写入到io流中。
6.BufferedOutputStream/BufferedInputStream
增强点:增强了缓冲区的功能。一般来说,每一次的read,write都是一次io操作,需要跨越jvm的边界,因此非常耗性能。通过引入缓冲机制,对于每次的读写,不直接进行io操作,而是先记录到缓冲区,当缓冲区满了后再进行io操作。
底层实现:底层是一个节点数组,用于存在读写的数据。
以字节输出流为例,缓冲区数据写入文件时机:1.缓冲区已满(待写入字节长度大于字节数组长度)。2.调用close方法,关闭io流。3.调用flush方法,清空缓冲区。
7.PrintStream
增强点:1.缓冲区功能;2.写8中基本类型和字符串数据功能;3.写对象。
底层实现:引用了BufferedWriter,写基本类型还写对象的本质是写对应的字符串。
8.ObjectOutputStream/ObjectInputStream
增强点:1.缓冲区功能;2.读写基本类型和字符串数据功能;3.读写对象。
底层实现:底层使用了字节数组实现缓冲功能;写基本类型数据和data流相同,使用了data流。
序列化:把对象放在流上进行传输的过程,成为“对象序列化”。一个对象如果能在留上传输,我们称这个对象是可序列化的。如果某个字段不需要序列化,使用transient修饰。
三、字符流
Reader、Writer是字符流的抽象基类。
1.字符编码
编码:字符转换成数字(二进制)的过程。
解码:数字转换成字符的过程。
2.编码规范
世界上任何一种编码方式,都与 ASCII 编码兼容,也就是说,任何 一种编码方式下面, A都对应65,a都对应97,没有例外。
ASCII:最早的编码方式,规定了英文字母和英文标点对应的编码。
ISO-8859-1:规定了西欧字符和西欧标点对应的编码。
GBK:规定了简体中文对应的编码,Windows系统默认编码方式。
Big5:规定了繁体中文对应的编码,台湾地区使用广泛。
UTF-8:国际通用的编码方式,包含了简体中文和繁体中文。注意,同一个汉字在GBK和UTF-8两种编码方式下编码是不同的。
3.乱码问题:
本质:编码解码的方式不一致。
//获取系统默认的编码格式
System.out.println(System.getProperty("file.encoding"));
System.out.println(Charset.defaultCharset());
4.桥转换
字节流转换为字符流。通过桥转换可以指定io流的编码方式。
桥转换的步骤:
1.创建字节流。
2.桥转换为字符流,可指定编码方式。
3.在字符流的基础上可封装过滤字符流。
4.读写数据。
5.关闭外层流。
5.字符过滤流
常用的字符过滤流有BufferedReader(增强是缓冲区),PrintWriter(1.增强缓冲区;2.支持基本数据类型的输出,以字符形式输出,注意和data流的区别;3.写对象,以字符形式输出)。
private static void readLine() throws IOException {
//创建字节流
InputStream inputStream = new FileInputStream("d://string.txt");
//通过桥转换转为字符流
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "GBK");
//封装过滤流
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
//读数据
String content = null;
while((content = bufferedReader.readLine()) != null ) {
System.out.println(content);
}
//关闭外层流
bufferedReader.close();
}
private static void printStr() throws Exception {
//当做过滤流使用
OutputStream outputStream = new FileOutputStream("d://print.txt");
Writer out = new OutputStreamWriter(outputStream);
PrintWriter writer = new PrintWriter(out);
writer.println("我在清华园");
writer.println("我在tl");
writer.println("我在技术部");
writer.close();
//当做节点流使用,内部做了包装
PrintWriter writer1 = new PrintWriter("d://print1.txt");
writer1.println("我是谁");
writer1.close();
//当做桥转换使用
PrintWriter writer2 = new PrintWriter(new FileOutputStream("d://print2.txt"));
writer2.println("我在哪里");
writer2.close();
}