java IO、File、Stream

关系

在Java中,IO、File、Stream是处理输入输出、文件操作和流传输的关键概念。

  1. Java IO(输入/输出):
    • Java的IO操作是通过流(Stream)来实现的,流是一个用于读取和写入数据的序列。Java IO提供了用于读取和写入不同类型数据(如字节、字符、对象等)的接口
    • Java IO主要包括两大类流:字节流(InputStream和OutputStream)和字符流(Reader和Writer)。字节流用于处理二进制数据,而字符流用于处理文本数据。
    • Java IO类库以装饰者模式设计,允许通过装饰者类来增强基本流的功能,例如添加缓冲、数据压缩、加密等功能。
  2. Java File(文件):
    • java.io.File 类是Java IO类库中用于处理文件和目录路径的类。它提供了创建、删除、重命名文件和目录、获取文件属性等操作的方法
    • File 类本身不提供文件内容的读写操作,它只是表示文件或目录的路径信息。要读写文件内容,需要使用流(如FileInputStreamFileOutputStreamFileReaderFileWriter等)。

  3. Java Stream(流):
    • 在Java中,流是一个用于数据传输的抽象概念。流可以看作是一个数据的序列,它可以是输入流,也可以是输出流。
    • Java中的流分为两大类:
      • 字节流:处理字节(8位)数据的流,主要用于处理二进制数据 ,如图片、音频、视频等。主要的字节流类有InputStreamOutputStream
      • 字符流:处理字符(16位)数据的流,主要用于处理文本数据 。主要的字符流类有ReaderWriter
    • Java 8 引入了新的流API——java.util.stream,它是用于处理对象序列的流,不同于IO流处理字节和字符数据。新的流API主要用于集合框架,提供了一种高效且易于使用的处理数据的方法。

关系图

文件输入输出流

FileInputStream

该流用于从文件读取数据,它的对象可以用关键字 new 来创建。

有多种构造方法可用来创建对象。

可以使用字符串类型的文件名来创建一个输入流对象来读取文件:

1
InputStream f = new FileInputStream("C:/java/hello");

也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象:

1
2
File f = new File("C:/java/hello");
InputStream in = new FileInputStream(f);

FileOutputStream

该类用来创建一个文件并向文件中写数据。

如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。

有两个构造方法可以用来创建 FileOutputStream 对象。

使用字符串类型的文件名来创建一个输出流对象:

1
OutputStream f = new FileOutputStream("C:/java/hello")

也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象:

1
2
File f = new File("C:/java/hello");
OutputStream fOut = new FileOutputStream(f);

下面是一个演示 InputStream 和 OutputStream 用法的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import java.io.*;

/**
* 文件流测试类
* 该类用于演示如何使用Java的文件输入输出流(FileInputStream和FileOutputStream)来读写文件。
*/
public class fileStreamTest {
/**
* 主方法
* 该方法首先将字节数据写入文件,然后从文件中读取并打印这些数据。
* @param args 命令行参数(未使用)
*/
public static void main(String[] args) {
try {
// 要写入文件的字节数据
byte bWrite[] = { 11, 21, 3, 40, 5 };
// 创建文件输出流并打开文件用于写入
OutputStream os = new FileOutputStream("test.txt");
// 循环写入字节数据到文件
for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]);
}
// 关闭文件输出流
os.close();

// 创建文件输入流并打开文件用于读取
InputStream is = new FileInputStream("test.txt");
// 获取文件的可用字节数
int size = is.available();

// 循环读取并打印文件中的字节数据
for (int i = 0; i < size; i++) {
System.out.print((char) is.read() + " ");
}
// 关闭文件输入流
is.close();
} catch (IOException e) {
// 捕获和处理IO异常
System.out.print("Exception");
}
}
}

路径

在Java中,路径有两种形式:绝对路径相对路径

绝对路径:绝对路径是指从根目录开始的路径,如:C:\Java\test.txt。

相对路径:相对路径是指相对于当前目录的路径,如:test.txt。

从当前项目src同级目录开始.\\test.txt
从项目所在的磁盘更目录开始\\text.txt

window 下使用\\,linux下使用/

文件异常

以字节流的形式向文件写入数据,当test.txt文件不存在的时候,是会自动创建test.txt。
但是,如果是写入数据到d:/xyz/test.txt,而目录xyz又不存在的话,就会抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test01 {
public static void main(String[] args) {
/**
* 以字节流的形式向文件写入数据,当test.txt文件不存在的时候,是会自动创建test.txt。
* 但是,如果是写入数据到d:/xyz/test.txt,而目录xyz又不存在的话,就会抛出异常。
*/
try {
// 新建一个相对路径,是从src同级目录开始
File dir = new File(".\\src\\main\\java\\top\\icewolf\\medium\\IoFileStream\\aaa\\test"); // 在src同级目录下开始
// 判断目录是否存在。不存在就创建目录
if (!dir.exists()) {
dir.mkdirs();
}
// 创建一个名为test.txt的文件对象,用路径加文件名拼合
File file = new File(dir,"test.txt");
// 创建一个输出流,file是目录路径dir和文件名test.txt的组合
FileOutputStream fileOutputStream = new FileOutputStream(file);
// 写入数据
fileOutputStream.write("I Love You!".getBytes());
// 关闭数据流
fileOutputStream.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
}

使用try-with-resources,可以不用关闭流。
File对象不用关闭,FileOutputStream需要关闭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

public class tryFile {
public static void run() {
File DIR = new File(".\\src\\main\\java\\top\\icewolf\\medium\\IoFileStream\\aaa\\test");
File file = new File(DIR, "test.txt");
try (
FileOutputStream fileOutputStream = new FileOutputStream(file);
) {
// 判断目录是否存在。不存在就创建目录
if (!DIR.exists()) {
DIR.mkdirs();
}
// 写入数据
fileOutputStream.write("Hello nodaoli!".getBytes());
} catch (IOException exception) {
exception.printStackTrace();
}
}
}

缓存流

以介质是硬盘为例,字节流和字符流的弊端:
在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。

为了解决以上弊端,采用缓存流。
缓存流在读取的时候,yellow 会一次性读较多的数据到缓存中,以后每一次的读取,都是在缓存中访问,直到缓存中的数据读取完毕,再到硬盘中读取。

就好比吃饭,yellow 不用缓存就是每次都要到锅里去铲。用缓存就是先把饭盛到碗里,碗里的吃完了,再到锅里去铲

缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到yellow 一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作

缓存流必须建立在一个现有的流上

1
2
3
4
5
6
7
8
9
// 输入缓存流建立在文件输入流之上
// FileReader fr = new FileReader("input.txt");
FileInputStream fis = new FileInputStream("input.txt");
BufferedInputStream bis = new BufferedInputStream(fis);

// 输出缓存流建立在文件输出流之上
// FileWriter fw = new FileWriter("output.txt");
FileOutputStream fos = new FileOutputStream("output.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos)

BufferedReader

BufferedReader 是一个带缓冲的字符输入流,它从字符输入流中读取文本,并缓冲字符以减少读取操作的次数。它提供了读取一行文本的方法 readLine(),这对于读取文本文件特别有用。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;

public class TestStream {

public static void main(String[] args) {
// 准备文件lol.txt其中的内容是
// garen kill teemo
// teemo revive after 1 minutes
// teemo try to garen, but killed again
File f = new File("d:/lol.txt");
// 创建文件字符流
// 缓存流必须建立在一个存在的流的基础上
try (
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
)
{
while (true) {
// 一次读一行
String line = br.readLine();
if (null == line)
break;
System.out.println(line);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

BufferedWriter

BufferedWriter 是一个带缓冲的字符输出流,它向字符输出流中写入文本,并缓冲字符以减少写入操作的次数。它提供了 newLine() 方法来写入一个新行,这对于跨平台写入文本文件很有用。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedWriterExample {
public static void main(String[] args) {
try (BufferedWriter bw = new BufferedWriter(new FileWriter("example.txt"))) {
bw.write("Hello, World!");
bw.newLine();
bw.write("This is a buffered writer example.");
} catch (IOException e) {
e.printStackTrace();
}
}
}

BufferedInputStream

BufferedInputStream 是一个带缓冲的字节输入流,它从字节输入流中读取数据,并缓冲字节以减少读取操作的次数。它适用于读取任何类型的字节流,包括文件、网络数据等。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class BufferedInputStreamExample {
public static void main(String[] args) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("example.bin"))) {
int data;
while ((data = bis.read()) != -1) {
System.out.print((char) data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

BufferedOutputStream

BufferedOutputStream 是一个带缓冲的字节输出流,它向字节输出流中写入数据,并缓冲字节以减少写入操作的次数。它适用于写入任何类型的字节流,包括文件、网络数据等。

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.FileOutputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;

public class BufferedOutputStreamExample {
public static void main(String[] args) {
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("example.txt"))) {
String content = "Hello, World!";
bos.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
}
}

总结

  • BufferedReader 和 BufferedWriter 用于缓冲字符流,适用于文本数据的读写。
  • BufferedInputStream 和 BufferedOutputStream 用于缓冲字节流,适用于任何类型的字节数据的读写。

使用这些缓冲流可以显著提高 IO 操作的性能,特别是在处理大量数据或频繁的读写操作时。

PrintWriter

缓存字符输出流, 可以一次写出一行数据

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class TestStream {

public static void main(String[] args) {
// 向文件lol2.txt中写入三行语句
File f = new File("d:/lol2.txt");

try (
// 创建文件字符流
FileWriter fw = new FileWriter(f);
// 缓存流必须建立在一个存在的流的基础上
PrintWriter pw = new PrintWriter(fw);
) {
pw.println("garen kill teemo");
pw.println("teemo revive after 1 minutes");
pw.println("teemo try to garen, but killed again");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}

PrintWriter和BufferedOutputStream的区别

  • PrintWriter 是一个字符输出流,用于输出文本数据,并提供自动刷新缓冲区的功能。
  • BufferedOutputStream 是一个字节输出流,用于提供缓冲功能,以提高输出性能。

如果你需要输出文本数据,并且希望简化格式化过程,使用 PrintWriter 是一个不错的选择。如果你需要输出字节数据,并且希望提高输出性能,使用 BufferedOutputStream 来包装一个字节输出流是合适的。

flush

有的时候,需要yellow 立即把数据写入到硬盘,而不是等缓存满了才写出去。 这时候就需要用到flush

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class TestStream {
public static void main(String[] args) {
//向文件lol2.txt中写入三行语句
File f =new File("d:/lol2.txt");
//创建文件字符流
//缓存流必须建立在一个存在的流的基础上
try(FileWriter fr = new FileWriter(f);PrintWriter pw = new PrintWriter(fr);) {
pw.println("garen kill teemo");
//强制把缓存中的数据写入硬盘,无论缓存是否已满
pw.flush();
pw.println("teemo revive after 1 minutes");
pw.flush();
pw.println("teemo try to garen, but killed again");
pw.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}