目录
- 一、简介
- 1.1 I/O流的分类
- 1.2 I/O流抽象基类
- 1.3 I/O流概览图
- 二、常见IO流的详细介绍与使用
- 2.1 FileInputStream & FileOutputStream(字节流)
- 2.2 InputStreamReader & OutputStreamWriter(字符流-又称转换流)-不推荐使用
- 2.3 FileReader & FileWriter(字符流)
- 2.4 BufferedInputStream & BufferedOutputStream(缓存字节流)-推荐使用
- 2.5 BufferedReader & BufferedWriter(缓存字符流)-推荐使用
- 结语
一、简介
流是一种抽象概念,它代表了数据的无结构化传递。按照流的方式进行输入输出,数据被当成无结构的字节序或字符序列。从流中取得数据的操作称为提取操作,而向流中添加数据的操作称为插入操作。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出。
1.1 I/O流的分类
- 根据处理数据类型的不同分为:字符流和字节流
- 根据数据流向不同分为:输入流和输出流
1.2 I/O流抽象基类
- 字节流的抽象基类:InputStream,OutputStream
- 字符流的抽象基类:Reader,Writer
1.3 I/O流概览图
二、常见IO流的详细介绍与使用
2.1 FileInputStream & FileOutputStream(字节流)
FileInputStream和FileOutputStream,分别是InputStream(字节输入流)和OutputStream(字节输出流)
/**
* FileInputStream & FileOutputStream (字节流)
*/
@Test
public void fileInputStreamAndFileOutputStream() {
File sourceFile = new File("C:\\myFile\\example\\Base64编译过程.png");
File destFile = new File("C:\\myFile\\example\\byteStream\\Base64编译过程(字节流结果).png");
FileInputStream fis = null;
FileOutputStream fos = null;
try {
System.out.println("FileInputStream & FileOutputStream 字节流开始文件读写");
LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
fis = new FileInputStream(sourceFile); //读取源文件的流
fos = new FileOutputStream(destFile); //输出到目标文件的流
System.out.println("当前文件输入流中的字节数为:" + fis.available());
//可以根据文件的大小和内存的大小合理分配大小比如2048
byte[] bytes = new byte[1024];
int len;
//读入多个字节到字节数组中,len为一次读入的字节数
while ((len = fis.read(bytes)) != -1) {
//将字节数组写入到输出流(偏移量从0开始,到len字节)
fos.write(bytes, 0, len);
// System.out.println("当前字节输入流中剩余字节数:" + fis.available());
}
LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
long millis = ChronoUnit.MILLIS.between(startTime, endTime);
System.out.println("FileInputStream & FileOutputStream 字节流文件读写完毕,耗时:" + millis + "毫秒");
} catch (IOException e) {
System.out.println("FileInputStream & FileOutputStream 字节流文件读写异常" + e);
} finally {
try {
if (fos != null) {
fos.close();//关闭输出流
}
if (fis != null) {
fis.close();//关闭输入流
}
} catch (IOException e) {
System.out.println("FileInputStream & FileOutputStream 字节流文件读写关闭异常" + e);
}
}
}
注意:
- FileInputStream要操作的文件必须存在,否则会抛出异常
- FileOutputStream写入的目的文件不存在时会被创建文件,存在的时会被覆盖文件
- FileOutputStream通过构造函数的第二个参数true或者false可以实现文件追加功能
- FileInputStream读取字节的时,返回值len表示本次读取了多少个字节,通常情况下每次读取1024个字节,也根据实际文件大小及内存大小可以合理取值
- FileInputStream读取到字节的末尾,再继续读取,会返回 -1,可以当做我们文件读取完毕的标识符
- 字节流(不包含缓存字节流)是不存在缓冲区的,字节的读取和写入都是通过操作系统来实现的,所以不需要使用flush操作刷新缓冲区
- 流都要进行关闭操作,还需要 try…catch,一般放到finally里
运行结果:
FileInputStream & FileOutputStream 字节流开始文件读写
当前文件输入流中的字节数为:136363
FileInputStream & FileOutputStream 字节流文件读写完毕,耗时:2毫秒
2.2 InputStreamReader & OutputStreamWriter(字符流-又称转换流)-不推荐使用
InputStreamReader 和OutputStreamWriter,分别是Reader(字符输入流)和Writer(字符输出流)
- InputStreamReader 是字节流通向字符流的桥梁,它将字节流转换为字符流。
- OutputStreamWriter是字符流通向字节流的桥梁,它将字符流转换为字节流。
/**
* InputStreamReader & OutputStreamWriter (字符流,不推荐使用,但是可以当转换流)
*/
@Test
public void inputStreamReaderAndOutputStreamWriter() {
File sourceFile = new File("C:\\myFile\\example\\Alian的文章.txt");
File destFile = new File("C:\\myFile\\example\\exchangeStream\\Alian的文章(转换流结果).txt");
InputStreamReader isReader = null;
OutputStreamWriter osWriter = null;
try {
System.out.println("InputStreamReader & OutputStreamWriter 转换流开始文件读写");
LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
FileInputStream fis = new FileInputStream(sourceFile);//读取源文件的流
FileOutputStream fos = new FileOutputStream(destFile);//输出到目标文件的流
isReader = new InputStreamReader(fis, StandardCharsets.UTF_8);//读取时编码
osWriter = new OutputStreamWriter(fos, "gb2312");//输出时编码
System.out.println("当前文件输入流中的字节数为:" + fis.available());
char[] bytes = new char[1024];
int len;
//读入多个字符到字符数组中,len为一次读入的字符数
while ((len = isReader.read(bytes)) != -1) {
//将字符数组写入到输出流(偏移量从0开始,到len字节)
osWriter.write(bytes, 0, len);
// System.out.println(String.copyValueOf(bytes, 0, len));
}
//刷新流
osWriter.flush();
LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
long millis = ChronoUnit.MILLIS.between(startTime, endTime);
System.out.println("InputStreamReader & OutputStreamWriter 转换流文件读写完毕,耗时:" + millis + "毫秒");
} catch (IOException e) {
System.out.println("InputStreamReader & OutputStreamWriter 转换流文件读写异常" + e);
} finally {
try {
if (osWriter != null) {
osWriter.close();//关闭输出流(会先调用flush方法)
}
if (isReader != null) {
isReader.close();//关闭输入流
}
} catch (IOException e) {
System.out.println("InputStreamReader & OutputStreamWriter 转换流文件读写关闭异常" + e);
}
}
}
注意:
- InputStreamReader和OutputStreamWriter内部都存在缓冲区
- InputStreamReader构造函数需要传递字节流和编码格式,没有传编码格式则会使用系统默认编码,容易导致乱码(windows默认编码表为GBK)
- OutputStreamWriter如果是追加文件内容,需要保证原文件之前编码和构造函数传入的编码格式一致,否则当有中文时会出现乱码
- 传递给字符流的字节流不需要单独的进行关闭,在字符流关闭的时候会自动调用字节流的close()方法
运行结果:
InputStreamReader & OutputStreamWriter 转换流开始文件读写
当前文件输入流中的字节数为:454
InputStreamReader & OutputStreamWriter 转换流文件读写完毕,耗时:2毫秒
打开文件,我们可以看到文件的编码变成了gb2312
2.3 FileReader & FileWriter(字符流)
FileReader 和FileWriter,分别是InputStreamReader (字节流转换为字符流)和OutputStreamWriter(字符流转换为字节流)
/**
* FileReader & FileWriter (字符流)
*/
@Test
public void fileReaderAndFileWriter() {
File sourceFile = new File("C:\\myFile\\example\\Alian的文章.txt");
File destFile = new File("C:\\myFile\\example\\charStream\\Alian的文章(字符流结果).txt");
FileReader fReader = null;
FileWriter fWriter = null;
try {
System.out.println("---------------------FileReader & FileWriter 字符流开始文件读写---------------------");
LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
fReader = new FileReader(sourceFile);//读取源文件的流
fWriter = new FileWriter(destFile);//输出到目标文件的流
char[] bytes = new char[1024];
int len;
//读入多个字符到字符数组中,len为一次读入的字符数
while ((len = fReader.read(bytes)) != -1) {
//将字符数组写入到输出流(偏移量从0开始,到len字节)
fWriter.write(bytes, 0, len);
System.out.println(String.copyValueOf(bytes, 0, len));
}
//刷新流
fWriter.flush();
LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
long millis = ChronoUnit.MILLIS.between(startTime, endTime);
System.out.println("---------------------FileReader & FileWriter 字符流文件读写完毕,耗时:" + millis + "毫秒---------------------");
} catch (IOException e) {
System.out.println("FileReader & FileWriter 字符流文件读写异常" + e);
} finally {
try {
if (fWriter != null) {
fWriter.close();//关闭输出流(会先调用flush方法)
}
if (fReader != null) {
fReader.close();//关闭输入流
}
} catch (IOException e) {
System.out.println("FileReader & FileWriter 字符流文件读写关闭异常" + e);
}
}
}
注意:
- FileReader和FileWriter内部都存在缓冲区,默认大小为8192字节
- 没有进行流的关闭操作,数据会存在缓冲区中,不会存储到文件上,只有当数据超过了缓冲区的大小,或者手动调用flush方法,数据才会刷新到文件上
- 调用close方法的内部会先调用flush刷新缓冲区
运行结果:
---------------------FileReader & FileWriter 字符流开始文件读写---------------------
Java实现MD5工具类
Java实现SHA-1、SHA-256和SHA-512加密(原生摘要)
Java实现Base64工具类(编码和解码)
Java实现3DES工具类(包含CBC和ECB)
Java实现AES工具类(包含CBC和ECB)
Java实现RSA工具类(加密、解密、签名、验签)
Java8新特性:Lambda表达式详解及四大函数式接口
Java8新特性:Stream详细使用
Java8新特性:LocalDateTime详细介绍
Java文件基础操作
---------------------FileReader & FileWriter 字符流文件读写完毕,耗时:0毫秒---------------------
2.4 BufferedInputStream & BufferedOutputStream(缓存字节流)-推荐使用
BufferedInputStream 和BufferedOutputStream,分别是FileInputStream(文件输入流)和FileOutputStream(文件输出流) 的子类,用法上也差不多,只不过BufferedInputStream 和BufferedOutputStream提供了一个合适的缓冲区来提高读写性能,在读写大数据的时候效果明显。下例是读取20万行大数据文本文件的测试,不到100毫秒就完成读写操作。
/**
* BufferedInputStream & BufferedOutputStream(缓存字节流)(推荐使用)
*/
@Test
public void bufferedInputStreamAndBufferedOutputStream() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
System.out.println("BufferedInputStream & BufferedOutputStream 缓存字节流开始文件读写");
FileInputStream inputStream = new FileInputStream("C:\\myFile\\example\\20万行大数据文本.txt");
FileOutputStream outputStream = new FileOutputStream("C:\\myFile\\example\\cacheByteStream\\20万行大数据文本(缓存字节流).txt");
LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
// bis = new BufferedInputStream(inputStream);//读取源文件的流
// bos = new BufferedOutputStream(outputStream);//输出到目标文件的流
bis = new BufferedInputStream(inputStream,20*1024);//读取源文件的流,默认是8192
bos = new BufferedOutputStream(outputStream,20*1024);//输出到目标文件的流,默认是8192
System.out.println("当前缓存字节流中的字节数:" + bis.available());
byte[] bytes = new byte[1024];
int len;
//读入多个字节到字节数组中,len为一次读入的字节数
while ((len = bis.read(bytes)) != -1) {
//将字节数组写入到输出流(偏移量从0开始,到len字节)
bos.write(bytes, 0, len);
}
bos.flush();
LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
long millis = ChronoUnit.MILLIS.between(startTime, endTime);
System.out.println("BufferedInputStream & BufferedOutputStream 缓存字节流文件读写完毕,耗时:" + millis + "毫秒");
} catch (IOException e) {
System.out.println("BufferedInputStream & BufferedOutputStream 缓存字节流文件读写异常" + e);
} finally {
try {
if (bos != null) {
bos.close();//关闭输出流(会先调用flush方法)
}
if (bis != null) {
bis.close();//关闭输入流
}
} catch (IOException e) {
System.out.println("BufferedInputStream & BufferedOutputStream 缓存字节流文件读写关闭异常" + e);
}
}
}
注意:
- BufferedInputStream和BufferedOutputStream内部都存在缓冲区,默认大小为8192字节
运行结果:
BufferedInputStream & BufferedOutputStream 缓存字节流开始文件读写
当前缓存字节流中的字节数:56626711
BufferedInputStream & BufferedOutputStream 缓存字节流文件读写完毕,耗时:54毫秒
2.5 BufferedReader & BufferedWriter(缓存字符流)-推荐使用
BufferedReader和BufferedWriter,分别是Reader(字符输入流)和Writer(字符输出流)
/**
* BufferedReader & BufferedWriter(缓存字符流)(推荐使用)
*/
@Test
public void bufferedReaderAndBufferedWriter() {
File sourceFile = new File("C:\\myFile\\example\\45万行大数据文本.txt");
File destFile = new File("C:\\myFile\\example\\cacheCharStream\\45万行大数据文本(缓存字符流).txt");
BufferedReader bufReader = null;
BufferedWriter bufWriter = null;
try {
System.out.println("BufferedReader & BufferedWriter 缓存字符流开始文件读写");
LocalDateTime startTime = LocalDateTime.now();//获取操作开始时间
FileReader reader = new FileReader(sourceFile);
FileWriter writer = new FileWriter(destFile);
bufReader = new BufferedReader(reader,20*1024);//读取源文件的流,默认是8192字节
bufWriter = new BufferedWriter(writer,20*1024);//输出到目标文件的流,默认是8192字节
String line;
//读入一行数据
while ((line = bufReader.readLine()) != null) {
//写入一行数据
bufWriter.write(line);
bufWriter.newLine();//换行(文件最后一个换行无需处理)
}
LocalDateTime endTime = LocalDateTime.now();//获取操作结束时间
long millis = ChronoUnit.MILLIS.between(startTime, endTime);
System.out.println("BufferedReader & BufferedWriter 缓存字符流文件读写完毕,耗时:" + millis + "毫秒");
} catch (IOException e) {
System.out.println("BufferedReader & BufferedWriter 缓存字符流文件读写异常" + e);
} finally {
try {
if (bufWriter != null) {
bufWriter.close();//关闭输出流
}
if (bufReader != null) {
bufReader.close();//关闭输入流
}
} catch (IOException e) {
System.out.println("BufferedInputStream & BufferedOutputStream 缓存字符流文件读写关闭异常" + e);
}
}
}
注意:
- BufferedReader和BufferedWriter内部都存在缓冲区,默认大小为8192字节
- 提供的方法ReadLine(),根据合适的换行符来读取一行数据
- 提供的方法ReadAllBytes(),根据合适的换行符来读取所有字节
- 提供的方法newLine(),会根据操作系统的不同添加合适的换行符
- 按行来读取文件时,如果下一行没有内容,继续读取下一行,则会返回 null,可以当做我们文件读取完毕的标识符
运行结果:
BufferedReader & BufferedWriter 缓存字符流开始文件读写
BufferedReader & BufferedWriter 缓存字符流文件读写完毕,耗时:681毫秒
结语
以上就是今天要讲的内容,本文仅仅简单介绍了Java中常见的IO流及其使用,后续有时间会继续扩充。