Java 中的 I/O(输入/输出)是一个核心概念,涉及数据的读取和写入。Java I/O API 提供了丰富的类和接口,用于处理不同类型的数据流。了解 Java I/O 的基础知识对于面试和日常编程都非常重要。所以今天来整理一下:
1. File类
File类主要是JAVA为文件的操作(如增删改查)而设计的相关类,它的包名是java.io,其实现了Serializable, Comparable两大接口以便于其对象可序列化和比较,是文件和目录路径名的抽象表示。
1.1. 常用API
构造方法:
方法名称 | 说明 |
File(String pathname) | 通过将给定的路径名,字符串转换为抽象路径名来创建新的File实例 |
File(String parent,String child) | 从父路径名,字符串和子路径名,字符串创建新的File实例 |
File(File parent,String child) | 从父抽象路径名和子路径名,字符串创建新的File实例 |
常用方法
方法 | 作用 |
创建方法 | |
boolean createNewFile() | 创建新的文件,如果文件已存在则失败返回false |
boolean mkdir() | 创建这个路径的文件夹 |
boolean mkdirs() | 创建路径中所有不存在的文件夹 |
判断方法 | |
boolean isDirectory() | 判断是不是文件夹 |
boolean isFile() | 判断是不是文件 |
boolean exists() | 判断是否存在 |
获取方法 | |
String getAbsolutePath() | 获取绝对路径 |
String getName() | 获取文件或者文件夹的名称 |
String getPath() | 获取文件或者文件夹的路径 |
String[] list() | 获取目录下所有的内容--名称 |
File[] listFiles() | 获取目录下所有的文件 |
删除方法 | |
boolean delete() | 删除文件或者文件夹 |
void deleteOnExit() | 删除存在的文件或者文件夹 |
2. I/O流
2.1. 概念
IO中的 I 是指Input(输入,指的是读取数据),O 是指Output(输出,指的是写数据);流是一种抽象概念,是对数据传输的总称,也就是说数据在设备间的传输称为流,流的本质是数据传输,常应用于文件上传、下载、复制等。
2.2. 分类
a. 根据数据流向分类:
- 输入流:读数据 将硬盘中的数据读取到内存中
- 输出流:写数据 将程序中的数据写入到硬盘中
b. 按照数据类型来分
字节流 字节输入流/字节输出流
字符流 字符输入流/字符输出流
2.3. 应用场景
1.纯文本文件,优先使用字符流
2.图片、视频、音频等二进制文件,优先使用字节流
3.不确定文件类型,优先使用字节流,字节流是万能的流
3. 具体API的使用
3.1. OutputStream 输出流
OutputStream是抽象类,表示字节输出流的所有类的超类;子类名称都是以其父类名作为子类名的后缀。
3.1.1. FileOutputStream 文件输出流
文件输出流用于将数据写入File,有三种写入方式:
方法名称 | 说明 |
void write(int b) | 将指定的字节写入此文件输出流 一次写一个字节数据 |
void write(byte[] b) | 将 b.length字节从指定的字节数组写入此文件输出流 一次写一个 字节数组数据 |
void write(byte[] b, int st, int len) | 从st下标开始,写入长度为len的数据 |
以下是代码演示:
public class OutputStreamAPI {
public static void main(String[] args) throws IOException {
//指定写入的路径
FileOutputStream fileOutputStream = new FileOutputStream("E:\\学习资料\\test.txt");
//写入到磁盘中
byte[] bytes = new byte[]{95,96,97,98};
fileOutputStream.write(bytes,2,1);
// 关闭字节输出流
fileOutputStream.close();
}
}
字节流写入数据如何换行?
windows:\r\n
linux:\n
mac: \r
3.2. InputStream 输出流
InputStream是抽象类,表示字节输入流的所有类的超类;子类名称都是以其父类名作为子类名的后缀。
3.2.1. FileInputStream 文件输入流
我们可以通过FileInputStream读取磁盘文件中的数据,使用read()方法读取,每次读取一个字节。
public class InputStreamAPI {
public static void main(String[] args) throws IOException {
File file = new File("E:\\学习资料\\test\\aaa.txt");
FileInputStream fileInputStream = new FileInputStream(file);
int a ;
while ((a = fileInputStream.read()) != -1){
System.out.print((char)a);
}
fileInputStream.close();
}
}
使用以上两个流,我们便可以实现文件的拷贝了,我们可以使用FileInputStream来读取文件中的字节,然后使用FileOutputStream来写入新的文件,实现拷贝功能,以下是代码:
public class FileCopy {
public static void main(String[] args) throws IOException {
File file1 = new File("E:\\学习资料\\test\\aaa.txt");
File file2 = new File("E:\\学习资料\\test\\bbb.txt");
FileInputStream fileInputStream = new FileInputStream(file1);
FileOutputStream fileOutputStream = new FileOutputStream(file2);
int BYTE = fileInputStream.read();
while(BYTE != -1){
fileOutputStream.write(BYTE);
BYTE = fileInputStream.read();
}
fileInputStream.close();
fileOutputStream.close();
}
}
3.3. 字节缓冲流
前面的方式是一个字节、一个字节读取或者写入数据,会频繁的发生系统内核调用(用户态→内核态切换)效率非常低。因此可以使用字节缓冲流,缓冲区是一个内存区域的概念,类似于池子以“块”的形似写入或者读取数据 减少系统调用频率。
BufferedInputStream(InputStream in):字节缓冲输入流
BufferedOutputStream(OutputStream out):字节缓冲输出流
构造函数传递字节流对象,不是文件路径,缓冲流提供了一个缓冲区 做了封装以块的形式读写数据,读写数据还是依赖于字节流对象。
注意:字节缓冲流的缓冲区大小默认是8K,即:8192字节
package JavaIO;
import java.io.*;
/**
* @Author: stukk
* @Description: 使用字节缓冲输出流于不适用的对比
* @DateTime: 2024-02-02 23:41
**/
public class BufferedStreamAPI {
public static void main(String[] args) throws IOException {
// 执行缓存程序耗时:549ms
// 执行单个字节复制的程序耗时:3300ms
long start = System.currentTimeMillis();
buffer(); //使用缓存的
long end = System.currentTimeMillis();
System.out.println("执行缓存流程序耗时:"+(end - start)+"ms");
start = System.currentTimeMillis();
single();
end = System.currentTimeMillis();
System.out.println("执行单个字节流传输程序的耗时:"+(end - start)+"ms");
}
private static void buffer() throws IOException {
// 读取
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("E:\\学习资料\\1\\test.mp4"));
// 写
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("E:\\学习资料\\2\\newTest.mp4"));
int len;
byte[] bytes = new byte[1024]; //读取的块
while((len = bufferedInputStream.read(bytes)) != -1){
bufferedOutputStream.write(bytes,0,len);
}
bufferedOutputStream.flush();
bufferedInputStream.close();
bufferedOutputStream.close();
}
private static void single() throws IOException {
// 读取
BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("E:\\学习资料\\1\\test.mp4"));
// 写
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("E:\\学习资料\\3\\newTest.mp4"));
int len;
while((len = bufferedInputStream.read()) != -1){
bufferedOutputStream.write(len);
}
bufferedOutputStream.flush();
bufferedInputStream.close();
bufferedOutputStream.close();
}
}
3.4. 字符流
3.4.1. 编码问题
1.当字节流读取存储中文数据时可能会乱码,我们可以使用字符流。
字符流=字节流+编码表
2.编码表:可以看做是一个字典,这本字典翻译是 人类的字符 和 机器语言(二进制) 之间对应关系。
3.编码表(ASCⅡ):就是人类生活的字符和计算机二进制的对照关系表。例如a 97
4.ISO8859-1:欧洲制定的码表。兼容ASCII。多了一些欧洲的语言。它用1个字节的全部来表示数据。没有未知字符的!
5.GB2312:识别数千中文
6.GBK:识别2万多中文,2个字节表示一个字符。
7.GB18030:GBK的升级
8.Unicode:世界计算机协会制定通用码表,2个字节表示一个字符。
9.UTF-8:Unicode升级版,汉字基本都是3个字节 推荐使用
3.4.2. 字符流详细介绍
字符流主要是应用在文本的输入以及输出中,指定文本的编码以及解码。
1.Writer是写入字符流的抽象类
1.1.OutputStreamWriter(输出转换流)是字符流通向字节流的桥梁,将要写入流的字符编码成字节。构造方法摘要OutputStreamWriter(OutputStream out) 创建使用默认字符编码的 OutputStreamWriter。1.2.OutputStreamWriter(OutputStream out, String charsetName) 创建使用指定字符集的 OutputStreamWriter。
字符流的底层还是用字节流进行读写,字符流仅仅是做了字节和字符的转换(编码和解码)。接收一个字节流,形成了字符流。把字节流转换为字符流!接收字符数据,首先要把字符数据变成字节数据,然后再用普通字节流,把数据写到硬盘。实现了字符数据转换为字节数据。
2.Reader是用于读取字符流的抽象类
2.2.InputStreamReader(输入转换流)是字节流通向字符流的桥梁,将要读取字节并将其解码成字符。
构造函数:InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader(InputStream in, String charsetName)创建使用指定字符集的 InputStreamReader。
字符流底层还是用的字节流,字符流仅仅做字符和字节的转换!接收一个字节流,生成的一个字符流。把字节流转换为字符流。底层会用字节流去硬盘读取字节数据,把字节数据转换为字符数据,然后返回。实现了把字节数据转换为字符数据。
字符流---读写操作:
package JavaIO;
import java.io.*;
/**
* @Author: stukk
* @Description: 字符流 读写操作
* @DateTime: 2024-02-05 17:13
**/
public class OutputStreamWriterAPI {
public static void main(String[] args) throws IOException {
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("E:\\学习资料\\test.txt"),"GBK");
outputStreamWriter.write("stukk\n坤坤");
outputStreamWriter.close();
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("E:\\学习资料\\test.txt"),"GBK");
char[] chars = new char[1024];
int len = inputStreamReader.read(chars);
System.out.println(new String(chars,0,len));
}
}
5种写入的方式:
方法名称 | 功能 |
public void write(int c) | 写出一个字符 |
public void write(char[] cbuf) | 写出字符数组 |
public void write(char[] cbuf,int off,int len) | 写出字符数组cbuf中,从off开始,共len个字符 |
public void write(String str) | 写出字符串 |
public void write(String str,int off,int len) | 写出字符串,从off开始,共len个字符 |
读取方法
方法名称 | 功能 |
int read() | 一次读一个字符数据 |
int read(char[] cbuf) | 一次读一个字符数组数据 |
3.5. 字符缓冲流
1.BufferedReader:从字符输入流读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
BufferReader:默认缓存大小为8k,但可以手动指定缓存大小,把数据读取到缓存中,减少每次转换过程,效率更高
2.BufferedWriter: 将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入
BufferWriter:默认缓存大小为8k,但可以手动指定缓存大小,把数据读取到缓存中,减少每次转换过程,效率更高
BufferedWriter-字符缓冲写入流
BufferedReader-字符缓冲输入流
Api设计思想与前面的字节缓冲流思想一样。
以下是拷贝文件的例子:
private static void buffer() throws IOException {
BufferedReader bufferedReader = new BufferedReader(new FileReader("E:\\学习资料\\test.java"));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("E:\\学习资料\\ok.java"));
String s = null;
while((s = bufferedReader.readLine()) != null){
bufferedWriter.write(s);
bufferedWriter.newLine();
}
bufferedWriter.close();
bufferedReader.close();
}
3.6. 转换流的便捷形式:FileWriter和FileReader
前面的:
new InputStreamReader(new FileInputStream("文件路径"));
可以替换成:
new FileReader("文件路径");
同理,FileWrite也是一样