I/O 流是什么
I:Input、O:Output,即输入输出。
一个文件,从磁盘到内存,叫输入。从内存到磁盘,叫输出。通过 IO 可以完成磁盘文件的读和写。
以内存为参照物就好理解了。
流是什么,流可以认为是一种数据流向。从磁盘到内存,从内存到磁盘,这就是流向。
I/O 流的分类
按照流向,分为输入流和输出流。
按照类别分,分为字节流和字符流。
Java I/O 的四大家族
java.io.InputStream
字节输入流、java.io.OutputStream
字节输出流
java.io.Reader
字符输入流、java.io.InputStream
字符输出流
该四大家族都是抽象类。其中,所有的流都实现了 java.io.Closeable
接口,都是可关闭的,都有 close() 方法。流毕竟是一个管道,这个是内存和硬盘之间的通道,用完之后一定要关闭,不然会耗费(占用)很多资源。养成好习惯,用完流一定要关闭。
所有的输出流都实现了:java.io.Flushable
接口,都是可刷新的,都有 flush() 方法。养成一个好习惯,输出流在最终输出之后,一定要记得 flush() 刷新一下。这个刷新表示将管道当中剩余未输出的数据强行输出完,刷新的作用就是清空管道。
一些 I/O 类
文件专属:
java.io.FileInputStream
java.io.FileOutputStream
java.io.FileReader
java.io.FileWriter
转换流:(将字节流转换成字符流)
java.io.InputStreanReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedReader
java.io BufferedWriter
java.io.BufferedInputStream
java.io.BufferedOutputStream
数据流专属:
java.io.DataInputStream
java.io.DataOutputStream
标准输出流:
java.io.PrintWriter
java.io.PrintStream
对象专属流:
java.io.ObjectInputStream
java.io.ObjectOutputStream
字节流
利用字节流读取文件和写入文件,这里以 test.txt 文件为例。test.txt 的内容如下。
my name is van
i am a artist
i am a performance artist
the deep dark fantasy
读取文件,磁盘 → 内存
创建的步骤大致都一样,先创建对象 → 读取文件 → 关闭流
1、先创建一个输入流
FileInputStream fis = new FileInputStream("e:\\test.txt");
2、创建文件完之后,就可以调用 read() 直接读数据了。数每读取一次,文件指针就会移动一位。当移动到文件末尾时,此时 read() 的返回值为 -1,可以用 while 循环让它一直读
int read;
while((read = fis.read()) != -1) {
System.out.println(read);
}
3、最后关闭文件流
fis.close();
代码运行完之后,控制台输出并不是字符,而是每个字符所对应的 ascii 码,可以强制类型转换成 char 类型,输出的就都是字符了。
109
121
32
110
97
109
101
32
105
115
......
但是,上面这种方式读取文件效率太低了,因为是一个一个读。Java 还可以通过字节数组的方式读取数据,这样效率就会高很多。
@Test
public void test2() {
FileInputStream fis = null;
try {
fis = new FileInputStream(path);
byte[] bytes = new byte[12];
int i; // 拿到的是读取的字节数,当读取不到数据的时候,直接返回 -1
while ((i = fis.read(bytes)) != -1) {
// 不能全部读取数组中的元素,读到哪里指针就指到哪里
// System.out.println(new String(bytes));
System.out.print(new String(bytes, 0, i));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
写入到文件,内存 → 磁盘
步骤:创建输出流 → 写入 → 刷新 → 关闭
1、创建文件输出流
// 默认是直接覆盖,要在文件结尾追加数据,在构造方法中另加一个参数 true,表示可追加。
FileOutputStream fos = new FileOutputStream(path, true);
2、写入数据。由于是字节,所以只能一个一个按字节写或者通过字节数组的方式来写。
byte[] bytes = new byte[]{65, 66, 67, 68};
fos.write(bytes);
fos.write(51);
3、刷新流
fos.flush();
4、关闭
fos.close();
如果不加 true 的话,那么文件中的内容是 ABCD ,加了 true 的话,那么就是文件结尾 ABCD。
复制一个视频文件
通过字节输入流和输出流的配合,可以实现一个简单的文件复制功能。
@Test
public void copyFileTest() {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
System.out.println("开始复制......");
long start = System.currentTimeMillis();
// 读文件
fis = new FileInputStream("D:\\fakepath\\星愿(茶理理版).mov");
// 写文件
fos = new FileOutputStream("E:\\星愿(茶理理版).mov");
// 每次读取 10kb 数据
byte[] bytes = new byte[1024 * 10];
int read;
while ((read = fis.read(bytes)) != -1) {
fos.write(bytes);
}
// 刷新输出流
fos.flush();
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end - start) + " ms");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符流
字符流是专门操作纯文本文件的,如果要操作图片,音频,word 文档就不可以了。它的使用方式和字节流一样的
读取文件中的数据,通过字符数组的方式。
@Test
public void test1() {
FileReader reader = null;
try {
reader = new FileReader("e:\\test.txt");
char[] chars = new char[5];
int read = chars.length;
while ((read = reader.read(chars)) != -1) {
System.out.println(new String(chars, 0, read));
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
将字符串写入到文件中。
@Test
public void test2() {
FileWriter writer = null;
try {
writer = new FileWriter("e:\\test2.txt");
writer.write("On a sultry evening in August, Zhang Yuwei, " +
"who works in sales in Shanghai, " +
"donned a chiffon shirt and hotpants to attend one of " +
"the first performances of the show in Mandarin.");
writer.write("\n");
writer.write("八月的一个闷热的晚上,在上海从事销售工作的张雨薇(音译)穿着雪纺纱T恤和热裤去观看" + "普通话版本的最早的几场演出。");
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
复制纯文本文件。
@Test
public void testCopyFile() {
FileReader fileReader = null;
FileWriter fileWriter = null;
try {
fileReader = new FileReader("D:\\fakepath\\forge-1.14.4-28.2.23-installer.jar.log");
fileWriter = new FileWriter("e:\\forge-1.14.4-28.2.23-installer.jar.log");
char[] chars = new char[100];
int read;
while((read = fileReader.read(chars)) != -1) {
fileWriter.write(chars, 0, read);
}
fileWriter.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fileReader != null) {
try {
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fileWriter != null) {
try {
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
利用缓冲流读取文本中的一行数据。
@Test
public void test1() {
BufferedReader bufferedReader = null;
try {
// 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做节点流,比如 FileReader
FileReader fr = new FileReader("e:\\forge-1.14.4-28.2.23-installer.jar.log");
// 外部负责包装的这个流,叫做包装流,还有一个名字叫做处理流,比如 BufferedReader
bufferedReader = new BufferedReader(fr);
String s;
while((s = bufferedReader.readLine()) != null) {
System.out.println(s);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
标准输入输出流
什么是标准输入输出流,看似没听过,其实经常会使用,而且很频繁。System.in
就是标准输入流,System.out
就是标准输出流。
默认的标准输入流是通过键盘读取的,标准输出流是输出到控制台的。而最常用的 Scanner 类也只是对标准输入流进行了封装而已。
默认的标准输入输出流其实是这样的。
// 输入
InputStream in = System.in;
in.read();
// 输出
PrintStream out = System.out;
out.println("fmisfsdfoidk");
既然输入可以用键盘、输出可以打印到控制台上。那肯定是修改成可以通过文件来进行输入、输出结果打印到文件上。
这是将结果输出到控制台上。
@Test
public void test2() {
PrintStream ps = null;
try {
ps = new PrintStream(new FileOutputStream("log.txt"));
System.setOut(ps);
System.out.println("Hello BestGuo!");
System.out.println("世界全由你谱写\n" +
"相处的时间你我已命运相连");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
这是通过文件的方式读取数据。
/**
* 标准输入流,通过文件的方式
*
* @throws Exception
*/
@Test
public void testIn() throws Exception {
System.setIn(new FileInputStream("log.txt"));
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
System.out.println(scanner.nextInt());
}
}
请勿发布违反中国大陆地区法律的言论,请勿人身攻击、谩骂、侮辱和煽动式的语言。