1. IO流概述
1.1 什么是IO流
在计算机中,input/output(I/O、i/o 或非正式的 io 或 IO)是信息处理系统(例如计算机)与外界(可能是人类或其他信息处理系统)之间的通信。 输入是系统接收到的信号或数据,输出是系统发送的信号或数据。
Java将数据的输入/输出(I/O)操作当作流处理,流是一组有序的数据序列,也可称为数据流。
数据流分为两种形式:输入流和输出流。站在当前系统的角度,数据流入系统的是输入流,数据流出系统的是输出流。如下图所示:
1.2 I/O的流分类
为支持Java程序的I/O操作,Java在java.io包下提供了丰富的I/O相关API(80余个类和接口)。为了快速掌握Java I/O的核心API,需要先了解I/O流的分类。
可以按照不同的角度对流进行分类:
1、按照数据流的方向不同可以分为输入流和输出流
2、按处理数据单位不同可以分为字节流和字符流
3、按使用方式不同可分为节点流与处理流,也称为基础流和高级流
- 节点流:真实连接数据源与程序之间的"管道",负责实际搬运数据的流,读写一定是建立在节点流的基础上进行的
- 处理流:不能独立存在,必须连接在其它流上,使得在读写数据的过程中,当数据流经当前处理流时对其做某些加工处理,简化我们对数据的相关操作
实际应用中我们会串联一组高级流并最终连接到基础流上,使得对数据的读写以流水线加工的方式实现。这个过程称为流的连接,也是IO的精髓所在。
2. 字节流
2.1 字节流概述
字节流,顾名思义,是指数据流中的数据以字节为单位进行操作,主要用于处理二进制数据。
InputStream和OutputStream是字节流的核心类,是2个抽象类,定义了基础的数据流读写方法,字节流中的其他类均为两个类的子类。
FileInputStream和FileOutputStream是字节流中最为常用的类,分别继承自InputStream和OutputStream,属于基础流。
BufferedInputStream和BufferedOutputStream是字节流中较为常用的高级流,间接继承自InputStream和OutputStream,主要提供了缓冲区功能。
2.2 创建 FOS 对象
FileOutputStream,是文件的字节输出流,可以以字节为单位将数据写入文件。
其构造方法有:
- FileOutputStream(File file):创建一个向指定 File 对象表示的文件中写数据的文件输出流
- FileOutputStream(String filename):创建一个向具有指定名称的文件中写数据的文件输出流
这里需要注意:若指定的文件已经包含内容,那么当使用FOS对其写入数据时,会将该文件中原有数据全部清除。
若想在文件的原有数据之后追加新数据则需要以下构造方法创建FOS:
- FileOutputStream(File file,boolean append):创建一个向指定 File 对象表示的文件中写数据的文件输出流
- FileOutputStream(String filename,boolean append):创建一个向具有指定名称的文件中写数据的文件输出流
以上两个构造方法中,第二个参数若为true,那么通过该FOS写出的数据都是在文件末尾追加的。
2.3 FileOutputStream示例
编写代码,向文件写入数据:分别测试覆盖写操作和追加写操作。代码示意如下:
package api_03;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputDemo {
public static void main(String[] args) throws IOException {
/*
* 文件输出流有两种创建方式,分别表示的是覆盖写操作和追加写操作
* 构造方法如下:
* FileOutputStream(File file)
* FileOutputStream(String path)
* 以上形式创建的文件流是覆盖写模式,当创建时指定的文件已经存在,则会将该
* 文件数据全部清除,然后通过当前流写出的内容作为该文件的数据
*
* FileOutputStream(File file,boolean append)
* FileOutputStream(String path,boolean append)
* 当构造方法第二个参数为true时,当前文件流为追加写模式,
* 即:若文件已经存在,原有数据保留,通过当前流写出的内容都被追加到文件中。
*/
FileOutputStream fos = new FileOutputStream("./src/api_03/fos.txt", true);
// String str = "这是第一次写出的内容\n";
String str = "这是第二次写出的内容\n";
byte[] data = str.getBytes("utf-8");
fos.write(data);
System.out.println("写出完毕!");
fos.close();
}
}
2.4 创建 FIS 对象
FileInputStream(常简称为 FIS对象),作为文件的字节输入流,使用该流可以以字节为单位从文件中读取数据。
FileInputStream有两个常用的构造方法:
- FileInputStream(File file):创建一个从指定 File 对象表示的文件中读取数据的文件输入流
- FileInputStream(String name):创建用于读取给定的文件系统中的路径名name所指定的文件的文件输入流
2.5 读和写
FileInputStream继承自InputStream,其提供了以字节为单位读取文件数据的方法read:
- int read():从此输入流中读取一个数据字节,若返回-1则表示EOF(End Of File)
- int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入到字节数组b中
FileOutputStream继承自OutputStream,其提供了以字节为单位向文件写数据的方法write:
- void write(int d):将指定字节写入此文件输出流,这里只写给定的int值的”低八位”
- void write(byte[] d):将 b.length 个字节从指定 byte 数组写入此文件输出流中
- void write(byte[] d,int offset,int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流
2.6 FileInputStream示例
编写代码,读取文件内容。代码示意如下:
package api_03;
import java.io.FileInputStream;
import java.io.IOException;
public class FileInputDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis
= new FileInputStream("./src/api_03/fos.txt");
// 存放读取到的数据的容器
byte[] data = new byte[1024];
// 执行一次读取,将读到的数据存入data中
int len = fis.read(data);
System.out.println("实际读取到了"+len+"个字节");
String str = new String(data,0,len,"utf-8");
System.out.println(str);
fis.close();
}
}
2.7 文件复制示例
编写代码,实现文件复制。代码示意如下:
package api_03;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileCopyDemo1 {
public static void main(String[] args) throws IOException {
/*
* 1:创建文件输入流,用于读取原文件
* 2:创建文件输出流,用于写复制文件
* 3:循环从原文件读取一组字节并写入
* 到复制文件中,完成复制工作
* 4:关闭两个流
*/
FileInputStream fis
= new FileInputStream("./src/api_03/fos.txt");
FileOutputStream fos
= new FileOutputStream("./src/api_03/fos_cp.txt");
byte[] data = new byte[1024*10];
int len = -1;
// 当读取到流的末尾时,会返回-1
while((len = fis.read(data))!=-1) {
// 注意规避数组中的冗余数据
fos.write(data,0,len);
}
System.out.println("复制完毕!");
fis.close();
fos.close();
}
}
在上述操作中,需要特别注意读取到流的末尾时可能遇到的数组中数据冗余问题。