💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## **一、概念** Java中对文件的操作是以流的方式进行的。流是Java内存中的一组有序数据序列。Java将数据从源(文件、内存、键盘、网络)读入到内存中,形成了流,然后将这些流还可以写到另外的目的地(文件、内存、控制台、网络),之所以称为流,是因为这个数据序列在不同时刻所操作的是源的不同部分。 <br> <br> ## **二、分类** 流的分类,Java的流分类比较丰富,刚接触的人看了后会感觉很晕。流分类的方式很多: 1、按照输入的方向分,输入流和输出流,输入输出的参照对象是Java程序。 2、按照处理数据的单位不同分,字节流和字符流,字节流读取的最小单位是一个字节(1byte=8bit),而字符流一次可以读取一个字符(1char = 2byte = 16bit)。 3、按照功能的不同分,分节点流和处理流,节点流是直接从一个源读写数据的流(这个流没有经过包装和修饰),处理流是在对节点流封装的基础上的一种流,FileInputStream是一个接点流,可以直接从文件读取数据,但是BufferedInputStream可以包装FileInputStream,使得其有缓冲功能。 其实除了以上三种分类外,还有一些常常听到的一些分类比如:对象流、缓冲流、压缩流、文件流等等。其实都是节点流和处理流的子分类。当然你也可以创建新的流类型,只要你需要。 <br> <br> ## **三、流分类的关系** 不管流的分类是多么的丰富和复杂,其根源来自于四个基本的类。这个四个类的关系如下: &nbsp; :-: ![](https://img.kancloud.cn/d7/ee/d7eeb991e7782779557b6c35d4233a99_534x144.png) <br> <br> ## **四、字节流和字符流的相互转换** 1、从字节流到字符流:InputStreamReader、OutputStreamWriter类可以实现。 2、从字符流到字节流:可以从字符流中获取char\[\]数组,转换为String,然后调用String的API函数getBytes() 获取到byte\[\],然后就可以通过ByteArrayInputStream、ByteArrayOutputStream来实现到字节流的转换。 <br> <br> ## **五.输入输出流** 在Java中,把不同类型的输入输出源抽象为流,其中输入和输出的数据称为数据流(Data Stream)。数据流是Java程序发送和接收数据的一个通道,数据流中包括输入流(Input Stream)和输出流(Output Stream)。通常应用程序中使用输入流读出数据,输出流写入数据。 流式输入、输出的特点是数据的获取和发送均沿数据序列顺序进行。相对于程序来说,输出流是往存储介质或数据通道写入数据,而输入流是从存储介质或数据通道中读取数据,一般来说关于流的特性有下面几点: * 先进先出,最先写入输出流的数据最先被输入流读取到。 * 顺序存取,可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。 * 只读或只写,每个流只能是输入流或输出流的一种,不能同时具备两个功能,在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。 <br> <br> ## **六.缓冲流** 为了提高数据的传输效率,引入了缓冲流(Buffered Stream)的概念,即为一个流配备一个缓冲区(Buffer),一个缓冲区就是专门用于传送数据的一块内存。 当向一个缓冲流写入数据时,系统将数据发送到缓冲区,而不是直接发送到外部设备。缓冲区自动记录数据,当缓冲区满时,系统将数据全部发送到相应的外部设备。当从一个缓冲流中读取数据时,系统实际是从缓冲区中读取数据,当缓冲区为空时,系统就会从相关外部设备自动读取数据,并读取尽可能多的数据填满缓冲区。 使用数据流来处理输入输出的目的是使程序的输入输出操作独立于相关设备,由于程序不需关注具体设备实现的细节(具体细节由系统处理),所以对于各种输入输出设备,只要针对流做处理即可,不需修改源程序,从而增强了程序的可移植性。 <br> <br> ## **七,I/O流类概述** 为了方便流的处理,Java语言提供了java.io包,在该包中的每一个类都代表了一种特定的输入或输出流。为了使用这些流类,编程时需要引入这个包。Java提供了两种类型的输入输出流:一种是面向字节的流,数据的处理以字节为基本单位;另一种是面向字符的流,用于字符数据的处理。字节流(Byte Stream)每次读写8位二进制数,也称为二进制字节流或位流。字符流一次读写16位二进制数,并将其做一个字符而不是二进制位来处理。需要注意的是,为满足字符的国际化表示,Java语言的字符编码采用的是16位的Unicode码,而普通文本文件中采用的是8位ASCⅡ码。 <br> <br> 针对一些频繁的设备交互,Java语言系统预定了3个可以直接使用的流对象,分别是: * System.in(标准输入),通常代表键盘输入。 * System.out(标准输出):通常写往显示器。 * System.err(标准错误输出):通常写往显示器。 在Java语言中使用字节流和字符流的步骤基本相同,以输入流为例,首先创建一个与数据源相关的流对象,然后利用流对象的方法从流输入数据,最后执行close()方法关闭流。 <br> <br> ## 读取控制台输入 java 的控制台输入由 System.in 完成。 为了获得一个绑定到控制台的字符流,你可以把 System.in 包装在一个 BufferedReader 对象中来创建一个字符流。 下面是创建 BufferedReader 的基本语法: ``` BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); ``` BufferedReader 对象创建后,我们便可以使用 **read()** 方法从控制台读取一个字符,或者用 **readLine()** 方法读取一个字符串。 <br> <br> ## **从控制台读取多字符输入** 从 BufferedReader 对象读取一个字符要使用 read() 方法,它的语法如下: ``` int read() throws IOException ``` 每次调用 read() 方法,它从输入流读取一个字符并把该字符作为整数值返回。 当流结束的时候返回 -1。该方法抛出 IOException。 下面的程序示范了用 read() 方法从控制台不断读取字符直到用户输入 "e"。 ``` package day03file; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class day01Stringin { public static void main(String args[]) throws IOException{ char num; // 使用System.in创建 BuffereadReader BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("输入字符:退出请输入e"); // 读取字符 do { num = (char) br.read(); System.out.println(num); }while (num != 'e'); } } ``` <br> <br> ## **从控制台读取字符串** 从标准输入读取一个字符串需要使用 BufferedReader 的 readLine() 方法。 它的一般格式是: ``` String readLine() throws IOException ``` 下面的程序读取和显示字符行直到你输入了单词"end"。 ``` package day03file; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class day02Stringin { public static void main(String args[])throws IOException{ // 使用System.in 创建 BufferdReader BufferedReader num = new BufferedReader(new InputStreamReader(System.in)); String str; System.out.println("Enter lines of text."); System.out.println("Enter end to quit."); do { str = num.readLine(); System.out.println(str); }while(!str.equals("end")); } } ``` <br> <br> ## **读写文件** 如前所述,一个流被定义为一个数据序列。输入流用于从源读取数据,输出流用于向目标写数据。 下图是一个描述输入流和输出流的类层次图。 :-: ![](https://img.kancloud.cn/e7/6e/e76e989ff1cf0b7610f7bc56a806e2f6_687x763.png) <br> <br> ## **FileInputStream** 该流用于从文件读取数据,它的对象可以用关键字 new 来创建。 有多种构造方法可用来创建对象。 可以使用字符串类型的文件名来创建一个输入流对象来读取文件: ``` InputStream f = new FileInputStream("src/web.log"); ``` 也可以使用一个文件对象来创建一个输入流对象来读取文件。我们首先得使用 File() 方法来创建一个文件对象: ``` File f = new File("src/web.log"); InputStream out = new FileInputStream(f); ``` 创建了InputStream对象,就可以使用下面的方法来读取流或者进行其他的流操作。 :-: ![](https://img.kancloud.cn/10/37/10376b64c93e8d0a261f26b591603bb9_1081x476.png) <br> <br> ## **FileOutputStream** 该类用来创建一个文件并向文件中写数据。 如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。 有两个构造方法可以用来创建 FileOutputStream 对象。 使用字符串类型的文件名来创建一个输出流对象: ``` OutputStream f = new FileOutputStream("src/web.log") ``` 也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象: ``` File f = new File("src/web.log"); OutputStream f = new FileOutputStream(f); ``` 创建OutputStream 对象完成后,就可以使用下面的方法来写入流或者进行其他的流操作。 创建OutputStream 对象完成后,就可以使用下面的方法来写入流或者进行其他的流操作。 | **序号** | **方法及描述** | | --- | --- | | **public void close() throws IOException{}** | 关闭此文件输入流并释放与此流有关的所有系统资源。抛出IOException异常。 | | **protected void finalize()throws IOException {}** | 这个方法清除与该文件的连接。确保在不再引用文件输入流时调用其 close 方法。抛出IOException异常。 | | **public void write(int w)throws IOException{}** | 这个方法把指定的字节写到输出流中。 | | **public void write(byte\[\] w)** | 把指定数组中w.length长度的字节写到OutputStream中。 | ``` package day03file; import java.io.FileOutputStream; import java.io.OutputStream; public class day03Stringin { 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]); // writes the bytes } 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) { System.out.print("Exception"); } } } ``` 上面的程序首先创建文件test.txt,并把给定的数字以二进制形式写进该文件,同时输出到控制台上。 以上代码由于是二进制写入,可能存在乱码,你可以使用以下代码实例来解决乱码问题: ``` import java.io.*; public class fileStreamTest2 { public static void main(String[] args) throws IOException { File f = new File("a.txt"); FileOutputStream fop = new FileOutputStream(f); // 构建FileOutputStream对象,文件不存在会自动新建 OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8"); // 构建OutputStreamWriter对象,参数可以指定编码,默认为操作系统默认编码,windows上是gbk writer.append("中文输入"); // 写入到缓冲区 writer.append("\r\n"); // 换行 writer.append("English"); // 刷新缓存冲,写入到文件,如果下面已经没有写入的内容了,直接close也会写入 writer.close(); // 关闭写入流,同时会把缓冲区内容写入文件,所以上面的注释掉 fop.close(); // 关闭输出流,释放系统资源 FileInputStream fip = new FileInputStream(f); // 构建FileInputStream对象 InputStreamReader reader = new InputStreamReader(fip, "UTF-8"); // 构建InputStreamReader对象,编码与写入相同 StringBuffer sb = new StringBuffer(); while (reader.ready()) { sb.append((char) reader.read()); // 转成char加到StringBuffer对象中 } System.out.println(sb.toString()); reader.close(); // 关闭读取流 fip.close(); // 关闭输入流,释放系统资源 } } ```