引言
绝大部分程序都会有导入和导出的功能需求,要实现这个功能我们就需要先了解两个概念。一、IO:输入和输出,即Java程序与外界的交互,它使程序能够读取外部数据和将内部数据写出到外部。二、I/O Stream:输入输出流,Java程序执行IO操作的具体机制,流会将不同来源和目标的数据进行统一的抽象,按照数据传输的单位分为两大类,字节流和字符流。下面我将深入解析这里提到的概念和正确的使用方式
概念
字节流
定义:以字节为单位进行读写操作的数据流
使用场景:可以处理任意类型的数据,主要处理二进制数据,如图片、音频、视频等文件
- InputStream:所有字节输入流的基类
- OutputStream:所有字节输出流的基类
字符流
定义:以字符为单位进行读写操作的数据流
使用场景:用于处理文本类型的数据
- Reader:所有字符输入流的基类
- Writer:所有字符输出流的基类
IO模型
BIO
定义:阻塞式输入输出,传统的IO模型,基于字节流和字符流
特点:当用户线程发起IO操作时,需要等待操作系统系统内核返回结果后才能做其他任务
优点:简单易用
缺点:高并发场景会导致程序性能骤降
NIO
定义:非阻塞式输入输出,java1.4版本引入的新IO模型,它的出现是为了解决BIO的缺陷,基于通道(类似于流,可以进行异步非阻塞操作)和缓冲区(用于和通道进行交互,读写数据)
特点,当用户线程发起IO操作时,不需要等待操作系统系统内核返回结果就可以执行其他任务,期间会轮询查看IO操作的结果
优点:不会阻塞,提高系统性能,它还有一个核心组件,Selector:用于管理多个通道的IO事件,即一个线程可以监听多个通道,这使得NIO模型能够高效地处理高并发连接
AIO
定义:异步输入输出,java7引入地新模型,基于NIO模型
特点:当用户线程发起IO操作时,不需要等待操作系统系统内核返回结果就可以执行其他任务,也不需要轮询查看IO操作的结果,因为IO操作完成时会以回调的方式通知相应线程
优点:不会阻塞,提高系统性能,实时性高
对IO模型的描述涉及到计算机系统中的很多概念,因此我查阅了很多相关资料,以我的理解画了一张IO操作流程图,帮助你理解IO操作的原理
代码示例
工具类
/**
* IO工具类
* 使用缓冲字符输入流提高读写性能
* 使用java7特性,自动管理资源
* @author muze
*/
@Slf4j
public class IOUtil {
/**
* 字符输出流
* 输出内容到文本文件
* txt文件
* @param writeContent 输出的内容
* @param outputPath 输出路径
*/
public static void outputContentToTextFile(String writeContent, String outputPath) {
// 创建缓冲字符输出流
try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputPath))) {
// 输出内容
bw.write(writeContent);
} catch (IOException e) {
log.error("IO异常:", e);
}
}
/**
* 字符输入流
* 文本文件内容输入
* txt文件
* @param inputPath 输入路径
*/
public static void textFileContentInput(String inputPath) {
// 创建缓冲字符输入流
try (BufferedReader br = new BufferedReader(new FileReader(inputPath))) {
// 用于记录每行字符串
String line;
while ((line = br.readLine()) != null) {
// 转成字符打印看看
System.out.print(line);
}
} catch (IOException e) {
log.error("IO异常:", e);
}
}
/**
* 字符输入输出流
* 输出内容到文本文件并输入
* txt文件
* @param writeAndInputContent 输出并输入的内容
* @param outputPath 输出路径
*/
public static void outputContentToTextFileAndInput(String writeAndInputContent, String outputPath) {
// 创建缓冲字符输入和缓冲字符输出流
try (BufferedWriter bw = new BufferedWriter(new FileWriter(outputPath));
BufferedReader br = new BufferedReader(new FileReader(outputPath))) {
// 输出内容
bw.write(writeAndInputContent);
// 用于记录每行字符串
String line;
while ((line = br.readLine()) != null) {
// 转成字符打印看看
System.out.print(line);
}
} catch (IOException e) {
log.error("IO异常:", e);
}
}
/**
* 字节输入流
* 非文本文件内容输入
* jpg文件
* @param inputPath 输入地址
*/
public static void nonTextFileContentInput(String inputPath) {
// 创建缓冲字节输入流
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputPath))) {
// 输入所有字节
byte[] bytes = bis.readAllBytes();
// 打印看看
System.out.println(new String(bytes));
} catch (IOException e) {
log.error("IO异常:", e);
}
}
/**
* 字节输入输出流
* 非文本文件内容输入并输出
* jpg文件
* @param inputPath 输入地址
* @param outputPath 输出地址
*/
public static void nonTextFileContentInputAndOutput(String inputPath, String outputPath) {
// 创建缓冲字节输入流和缓冲字节输出流
try (FileInputStream fis = new FileInputStream(inputPath);
FileOutputStream fos = new FileOutputStream(outputPath)) {
// 输入所有字节
byte[] readBytes = fis.readAllBytes();
// 输出所有字节
fos.write(readBytes);
} catch (IOException e) {
log.error("IO异常:", e);
}
}
}
测试类
/**
* IO测试
* @author muze
*/
@SpringBootTest
public class IOTest {
/**
* 测试输出内容到文本文件
*/
@Test
public void testOutputContentToTextFile() {
String writeContent = "测试文本文件输出内容";
String outputPath = "src/test/resources/测试文本文件内容输出.txt";
IOUtil.outputContentToTextFile(writeContent, outputPath);
}
/**
* 测试文本文件内容输入
*/
@Test
public void testTextFileContentInput() {
String inputPath = "src/test/resources/测试文本文件内容输出.txt";
IOUtil.textFileContentInput(inputPath);
}
/**
* 测试输出内容到文本文件并输入
*/
@Test
public void testOutputContentToTextFileAndInput() {
String writeAndInputContent = "测试文本文件输出并输入的内容";
String outputPath = "src/test/resources/测试文本文件输出并输入.txt";
IOUtil.outputContentToTextFileAndInput(writeAndInputContent, outputPath);
}
/**
* 测试非文本文件内容输入
*/
@Test
public void testNonTextFileContentInput() {
String inputPath = "src/test/resources/测试jpg文件输入.jpg";
IOUtil.nonTextFileContentInput(inputPath);
}
/**
* 测试非文本文件内容输入并输出
*/
@Test
public void testNonTextFileContentInputAndOutput() {
String inputPath = "src/test/resources/测试jpg文件输入.jpg";
String outputPath = "src/test/resources/测试jpg文件输入并输出.jpg";
IOUtil.nonTextFileContentInputAndOutput(inputPath, outputPath);
}
}
测试结果
注意事项
- 关闭流:在使用完流后,需要关闭它来释放资源,代码示例中使用java7特性try-with-resource语句自动关闭流
- 异常处理:IO操作时,可能会出现各种IO异常,所以,我们需要捕获进行妥善处理
- 性能优化:使用缓冲流提高读写性能,代码使用中使用Buffered缓冲流,使用默认的缓冲区,也可以设置缓冲区的大小,但请注意大小因业务而定,过大会占用较多内存资源
- 字符编码:字节流本身不涉及字符编码问题,但在处理文本文件时也需要注意字符编码的选择,字符流处理文本文件时应选择合适的编码以避免乱码问题
总结
本文深入讲解了Java中IO的相关概念和简单且优雅的工具类封装并调用测试,我相信大家已经对IO有了一定程度的了解,赶紧动手试试吧,希望这篇文章能够对你有所帮助