Java IO操作主要是指使用Java程序完成输入(Input)、输出(Output)的功能。所谓输入是指将文件以数据流的形式读取到Java程序中,输出是指通过Java程序将数据流写入到文件中。
File类
Java 提供了 java.io.File 类,使用该类的构造函数就可以创建文件对象以表示一个物理资源。
| 方法 | 描述 |
|---|---|
| public File(String pathname) | 根据路径创建对象 |
| public String getName() | 获取文件名 |
| public String getParent() | 获取文件所在的目录 |
| public File getParentFile() | 获取文件所在的目录对应的 File 对象 |
| public String getPath() | 获取文件路径 |
| public boolean exists() | 判断对象是否存在 |
| public boolean isDirectory() | 判断对象是否为目录 |
| public boolean isFile() | 判断对象是否为文件 |
| public long length() | 获取文件的大小 |
| public boolean createNewFile() | 根据当前对象创建新文件 |
| public boolean delete() | 删除对象 |
| public boolean mkdir() | 根据当前对象创建新目录 |
| public boolean renameTo(File dest) | 为已存在的对象重命名 |
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("D:\\java\\test\\src\\com\\test\\a.txt");
System.out.println("文件是否存在:" + file.exists());
System.out.println("文件路径:" + file.getPath());
System.out.println("文件名:" + file.getName());
System.out.println("文件大小:" + file.length());
System.out.println("文件所在的目录:" + file.getParent());
System.out.println("文件的父级对象是否为路径:" + file.getParentFile().isDirectory());
System.out.println("文件的父级对象是否为文件:" + file.getParentFile().isFile());
// 创建新的文件
File file1 = new File("D:\\java\\test\\src\\com\\test\\b.txt");
System.out.println("新文件创建是否成功:" + file1.createNewFile());
System.out.println("文件删除是否成功:" + file1.delete());
}
}
// 输出
文件是否存在:true
文件路径:D:\java\test\src\com\test\a.txt
文件名:a.txt
文件大小:89
文件所在的目录:D:\java\test\src\com\test
文件的父级对象是否为路径:true
文件的父级对象是否为文件:false
新文件创建是否成功:true
文件删除是否成功:true
字节流
用Java程序把D盘的文件复制到F盘,可以通过IO流来实现。I是指input,表示输入的意思;О是指Output,表示输出的意思。将D盘文件读取到程序中,再由程序将文件的数据流写到F盘中。所以读取目标文件就是输入流,写入F盘就是输出流。
流是一种以先进先出的方式传输数据的序列。Java中的流有很多种的分类。
- 按照方向分,可以分为输入流和输出流
- 按照单位分,可以分为字节流和字符流,字节流指每次处理数据是以字节为单位的,字符流是指每次处理数据是以字符为单位的。
- 按照功能分,可以分为节点流和处理流。
字节流按照方向又可以分为输入字节流(InputStream)和输出字节流(OutputStream),InputStream是java.io包中的顶层父类,并且是一个抽象类。
InputStream 实现了 Closeable接口,该接口的作用是每次操作结束之后完成资源释放。
| 方法 | 描述 |
|---|---|
| public abstract int read() throws IOException | 以字节为单位读取数据 |
| public int read(byte b[]) throws IOException | 将数据存入byte 类型数组中,并返回数据长度 |
| public int read(byte b[], int off, int len) throws IOException | 将数据存入byte类型数组的指定区间中,并返回数据长度 |
| public byte[] readAllBytes() throws IOException | 将数据存入byte类型数组中返回 |
| public int available() throws IOException | 返回当前数据流中未读取的数据个数 |
| public void close() throws IOException | 关闭数据流 |
实际开发中不能直接实例化 InputStream 类的对象,应该实例化其实现了抽象方法的子类 FileInputStream。
public class Main {
public static void main(String[] args) throws IOException {
InputStream file = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
System.out.println("当前未读取的数据个数:" + file.available());
int dataNum = 0;
while ((dataNum = file.read()) != -1){
System.out.println("当前字符的byte数值为:" + dataNum + ",当前未读取数据的数据个数:" + file.available());
}
file.close();
System.out.println("-------------------------------------------");
file = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
byte[] bytes = new byte[6];
int len = file.read(bytes);
System.out.println("数据流长度:" + len);
System.out.print("遍历 bytes 数组:");
for(byte b : bytes){
System.out.print(b + " ");
}
file.close();
System.out.println("");
System.out.println("-------------------------------------------");
file = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
bytes = new byte[6];
len = file.read(bytes, 2,2);
System.out.println("数据流长度:" + len);
System.out.print("遍历 bytes 数组:");
for(byte b : bytes){
System.out.print(b + " ");
}
file.close();
System.out.println("");
System.out.println("-------------------------------------------");
file = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
}
}
// 输出
当前未读取的数据个数:2
当前字符的byte数值为:105,当前未读取数据的数据个数:1
当前字符的byte数值为:116,当前未读取数据的数据个数:0
-------------------------------------------
数据流长度:2
遍历 bytes 数组:105 116 0 0 0 0
-------------------------------------------
数据流长度:1
遍历 bytes 数组:0 0 105 0 0 0
-------------------------------------------
OutputStream 跟 InputStream 类似,也是一个抽象父类。OutputStream 除了实现 Closeable 接口之外,还实现了 Flushable接口。Flushable接口可以将缓冲区的数据同步到输出流中,保证数据的完整性。OutputStream的常用方法如下:
| 方法 | 描述 |
|---|---|
| public abstract void write(int b) throws IOException | 以字节为单位写数据 |
| public void write(byte b[]) throws IOException | 将byte类型数组中的数据写出 |
| public void write(byte b[], int off, int len) throws IOException | 将byte类型数组中指定区间的数据写出 |
| public void flush() throws IOException | 可以强制将缓冲区的数据同步到输出流中 |
| public void close() throws IOException | 关闭数据流 |
在使用OutputStream进行写操作时,不能直接实例化 OutputStream 类的对象,应该实例化其抽象方法的子类 FileOutputStream。
public class Main {
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("D:\\java\\test\\src\\com\\test\\a.txt");
outputStream.write(99); // byte类型数据,99 表示为 'c'
outputStream.flush();
outputStream.close();
}
}
public class Main {
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("D:\\java\\test\\src\\com\\test\\a.txt");
byte[] bytes = new byte[]{97,98,99};
outputStream.write(bytes);
outputStream.flush();
outputStream.close();
}
}
public class Main {
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("D:\\java\\test\\src\\com\\test\\a.txt");
byte[] bytes = new byte[]{97,98,99};
outputStream.write(bytes, 0, 2);
outputStream.flush();
outputStream.close();
}
}
字符流
字符流是有别于字节流的另外一种数据流,两者的区别在于每次处理的数据单位不同,一个是以字节为单位,一个是以字符为单位。
字符流也分为输入字符流(Reader)和输出字符流(Writer)。Reader 是一个抽象类,实现了 Readable 和 Closeable 接口。
Readable 接口的作用是可以将数据以字符的形式读入缓冲区,Reader常用方法如下。
| 方法 | 描述 |
|---|---|
| public int read() throws IOException | 以字符为单位读数据 |
| public int read(char c[]) throws IOException | 将数据读入char类型数组,并返回数据长度 |
| public abstract int read(char c[], int off, int len) throws IOException | 将数据读入char类型数组的指定区间,并返回数据长度 |
| public abstract void close() throws IOException | 关闭数据流 |
| public long transferTo(Writer out) | 将数据直接读入字符输出流 |
使用Reader进行读入操作时,不能直接实例化Reader对象,应该实例化其实现了抽象方法的子类FileReader。
查看源码可以发现 FileReader 继承自 InputStreamReader,这个类才是 Reader 的直接子类,它的作用是将字节流转为字符流,属于处理流。
public class Main {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("D:\\java\\test\\src\\com\\test\\a.txt");
int temp = 0;
while((temp = reader.read()) != -1){
System.out.print(temp + " ");
}
reader.close();
}
}
// 输出
97 98 99
可以看到使用字符流和字节流读取的结果是一样的。其原因是,txt文件中保存的数据是“abc”,在UTF-8编码下,一个英文字符占用一个字节的空间(一个中文字符占用三个字节),即每读取一个字符就等于读取了一个字节,所以两种流的结果是一样的。换成中文字符就可看出区别。
将txt文件的内容改为“a好”,分别使用字符流和字节流同时读取文件。
public class Main {
public static void main(String[] args) throws IOException {
System.out.println("用字符流读取数据:");
Reader reader = new FileReader("D:\\java\\test\\src\\com\\test\\a.txt");
int temp = 0;
while((temp = reader.read()) != -1){
System.out.print(temp + " ");
}
reader.close();
System.out.println("");
System.out.println("用字节流读取数据:");
InputStream inputStream = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
while ((temp = inputStream.read()) != -1){
System.out.print(temp + " ");
}
inputStream.close();
}
}
// 输出
用字符流读取数据:
97 22909
用字节流读取数据:
97 229 165 189
通过结果可以看到,使用字符流读取数据和字节流读取数据的不一样。在字符流中,22909表示汉字“好”。在字节流中,229 165 189 三个字节表示汉字“好”。
public class Main {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("D:\\java\\test\\src\\com\\test\\a.txt");
char[] chars = new char[5];
int len = reader.read(chars);
System.out.println("数据流长度:" + len);
System.out.println("遍历 chars 数组");
for (char c : chars){
System.out.print(c + " ");
}
reader.close();
System.out.println("");
System.out.println("---------------------------------");
reader = new FileReader("D:\\java\\test\\src\\com\\test\\a.txt");
chars = new char[5];
len = reader.read(chars,1,2);
System.out.println("数据流长度:" + len);
System.out.println("遍历 chars 数组");
for (char c : chars){
System.out.print(c + " ");
}
reader.close();
}
}
// 输出
数据流长度:2
遍历 chars 数组
a 好
---------------------------------
数据流长度:2
遍历 chars 数组
a 好
字符输出流 Writer 继承 Appendable 接口,可以将char类型的数据读入数据缓冲区。Writer的常用方法。
| 方法 | 描述 |
|---|---|
| public void write(int c) throws IOException | 以字符为单位写数据 |
| public void write(char c[]) throws IOException | 将char类型数组中的数据写出 |
| public abstract void write(char c[], int off, int len) throws IOException | 将char类型数组中指定区间的数据写出 |
| public void write(String str) throws IOException | 将String类型的数据写出 |
| public void write(String str, int off, int len) throws IOException | 将String类型指定区间的数据写出 |
| public abstract void flush() throws IOException | 可以强制将缓冲区的数据同步到输出流中 |
| public abstract void close() throws IOException | 关闭数据流 |
使用Writer进行写出操作时,不能直接实例化Writer对象,应该实例化其实现了抽象方法的子类 FileWriter。
FileWriter 继承自 OutputStreamWriter,和 InputStreamReader一样,OutputStreamWriter 也属于处理流。
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
writer.write(20320);
writer.write(22909);
writer.flush();
writer.close();
}
}
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
char[] chars = new char[]{'你', '好', '世', '界'};
writer.write(chars);
writer.flush();
writer.close();
}
}
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
char[] chars = new char[]{'你', '好', '世', '界'};
writer.write(chars, 2, 2);
writer.flush();
writer.close();
}
}
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
String str = "你好世界!";
writer.write(str);
writer.flush();
writer.close();
}
}
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
String str = "你好世界!";
writer.write(str, 2, 3);
writer.flush();
writer.close();
}
}
处理流
字符流是在字节流的基础上转换来的,Java提供了完成转换功能的类,按照输入和输出两个方向分为输入转换流(InputStreamReader)和输出转换流(OutputStreamWriter)。
首先看字符流的类定义,FileReader的定义是public class FileReader extends InputStreamReader{},可以看到,FileReader继承自InputStreamReader,即输入转换流。通过这个类将字节流转为字符流,字节是基本单位,所以是基础管道。同理,FileWriter 继承自 OutputStreamWriter。
InputStreamReader 和 OutputStreamWriter 分别是 Reader 和 Writer的子类。
public class InputStreamReader extends Reader{}
public class OutputStreamWriter extends Writer{}
InputStreamReader 的使用如下:
public class Main {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
Reader reader = inputStreamReader;
char[] chars = new char[10];
int len = reader.read(chars);
reader.close();
String str = new String(chars, 0 ,len);
System.out.println(str);
}
}
OutputStreamWriter 的使用如下:
public class Main {
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("D:\\java\\test\\src\\com\\test\\a.txt");
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
Writer writer = outputStreamWriter;
String str = "Hello Java";
writer.write(str);
writer.flush();
writer.close();
}
}
缓冲流
前面介绍了字节流和字符流,在实际真实的运行环境中,无论使用那种流读取数据都需要频繁地访问磁盘,这样对硬盘地损伤比较大,而且效率不高。为了减少访问硬盘时的资源开销,引入了缓冲流。缓冲流自带缓冲区,可以一次性从硬盘读取部分数据存入缓冲区,再写入程序内存中。
缓冲流也属于处理流,即不能直接关联文件进行操作,只能对已存在的节点流进行包装,如何区分节点流和处理流?如果该流可以直接关联了文件,即提供了一个参数为 File 对象或文件路径的构造函数,该流就是节点流。如果该流不能直接关联文件,即没有提供参数为 File 对象或文件路径的构造函数,而必须依赖其他流来实例化的就是处理流。缓冲流又可以分为字节缓冲流和字符缓冲流,再细分又可分为字节输入缓冲流和字节输出缓冲流,以及字符输入缓冲流和字符输出缓冲流。
输入缓冲流
字节输入缓冲流 BufferedInputStream,其定义为public class BufferedInputStream extends FilterInputStream{}
BufferedInputStream 的常用方法与 OutputStream 类似,并且必须依赖于已存在的流才能创建实例化对象。
// read()方法
public class Main {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
int temp = 0;
while ((temp = bufferedInputStream.read()) != -1){
System.out.print(temp + " ");
}
bufferedInputStream.close();
inputStream.close();
}
}
// read(byte b[]) 方法
public class Main {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
int temp = 0;
byte[] bytes = new byte[20];
int len = bufferedInputStream.read(bytes);
for (byte b : bytes){
System.out.print(b + " ");
}
bufferedInputStream.close();
inputStream.close();
}
}
// read(byte b[], int off, int length) 方法
public class Main {
public static void main(String[] args) throws IOException {
InputStream inputStream = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
int temp = 0;
byte[] bytes = new byte[20];
int len = bufferedInputStream.read(bytes,2, 10);
for (byte b : bytes){
System.out.print(b + " ");
}
bufferedInputStream.close();
inputStream.close();
}
}
字符输入缓冲流 BufferedReader,其定义为public class BufferedReader extends Reader{},具体使用方法如下。
// readLine() 方法可以直接读取一整行数据
public class Main {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
String str = "";
while ((str = bufferedReader.readLine()) != null){
System.out.println(str);
}
bufferedReader.close();
reader.close();
}
}
// read() 方法
public class Main {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
int temp = 0;
while ((temp = bufferedReader.read()) != -1){
System.out.print(temp + " ");
}
bufferedReader.close();
reader.close();
}
}
// read(char c[]) 方法
public class Main {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
char[] chars = new char[50];
int len = bufferedReader.read(chars);
System.out.println(len);
for (char c : chars){
System.out.println(c + " "); // 使用 print 只能显示最后一行
}
bufferedReader.close();
reader.close();
}
}
// read(char c[], int off, int length) 方法
public class Main {
public static void main(String[] args) throws IOException {
Reader reader = new FileReader("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedReader bufferedReader = new BufferedReader(reader);
char[] chars = new char[50];
int len = bufferedReader.read(chars,2, 32);
System.out.println(len);
for (char c : chars){
System.out.println(c + " ");
}
bufferedReader.close();
reader.close();
}
}
输出缓冲流
字节输出缓冲流 BufferedOutputStream,其定义为public class BufferedOutputStream extends FilterOutputStream{} 输出缓冲流在关闭之前需要先调用flush()方法,目的是将缓冲区的数据全部写入到目前文件中,再关闭流。
// write(int b) 方法
public class Main {
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
String str = "java是一门高级编程语言,\r\n可以使用Java编写程序。";
byte[] bytes = str.getBytes();
for (byte b : bytes){
bufferedOutputStream.write(b);
}
bufferedOutputStream.flush();
bufferedOutputStream.close();
outputStream.close();
}
}
// write(byte b[]) 方法
public class Main {
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
String str = "java是一门高级编程语言,\r\n可以使用Java编写程序。";
byte[] bytes = str.getBytes();
bufferedOutputStream.write(bytes);
bufferedOutputStream.flush();
bufferedOutputStream.close();
outputStream.close();
}
}
// write(byte b[], int off, int length) 方法
public class Main {
public static void main(String[] args) throws IOException {
OutputStream outputStream = new FileOutputStream("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
String str = "java是一门高级编程语言,\r\n可以使用Java编写程序。";
byte[] bytes = str.getBytes();
int len = bytes.length;
System.out.println(len);
bufferedOutputStream.write(bytes, 2, len - 2);
bufferedOutputStream.flush();
bufferedOutputStream.close();
outputStream.close();
}
}
字符输出缓冲流(BufferedWriter),其定义为public class BufferedWriter extends Writer{}。BufferedWriter提供了一个直接将字符串输出的方法 write(String str)。
// write(String str)方法
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
String str = "java是一门高级编程语言,\r\n可以使用Java编写程序。";
bufferedWriter.write(str);
bufferedWriter.flush();
bufferedWriter.close();
writer.close();
}
}
// write(String str, int off, int length)方法
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
String str = "java是一门高级编程语言,\r\n可以使用Java编写程序。";
bufferedWriter.write(str,3, str.length() - 3);
bufferedWriter.flush();
bufferedWriter.close();
writer.close();
}
}
// write(char c[])方法
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
String str = "java是一门高级编程语言,\r\n可以使用Java编写程序。";
char[] chars = new char[]{'j','a','v','a','你','好'};
bufferedWriter.write(chars);
bufferedWriter.flush();
bufferedWriter.close();
writer.close();
}
}
// write(char c[], int off, int length)方法
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
String str = "java是一门高级编程语言,\r\n可以使用Java编写程序。";
char[] chars = new char[]{'j','a','v','a','你','好'};
bufferedWriter.write(chars, 1, 5);
bufferedWriter.flush();
bufferedWriter.close();
writer.close();
}
}
// write(int t) 方法
public class Main {
public static void main(String[] args) throws IOException {
Writer writer = new FileWriter("D:\\java\\test\\src\\com\\test\\a.txt");
BufferedWriter bufferedWriter = new BufferedWriter(writer);
String str = "java是一门高级编程语言,\r\n可以使用Java编写程序。";
bufferedWriter.write(22909);
bufferedWriter.flush();
bufferedWriter.close();
writer.close();
}
}
序列化和反序列化
序列化就是指将内存中的对象输出到硬盘文件中进行保存,反序列化是从文件中读取数据并还原成内存中的对象。
序列化
需要将序列化的对象的类实现 java.io.Serializable 接口。
public class People implements Serializable {
private int id;
private String name;
private int age;
private String gender;
}
将 People 的实例化对象进行序列化处理,通过数据流写入到文件中,需要用到 ObjectOutputStream,该类是专门将对象进行输出处理的。
public class Main {
public static void main(String[] args) throws IOException {
People people = new People();
people.setId(1);
people.setName("bob");
people.setAge(18);
people.setGender("男");
OutputStream outputStream = new FileOutputStream("D:\\java\\test\\src\\com\\test\\a.txt");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(people);
objectOutputStream.flush();
objectOutputStream.close();
outputStream.close();
}
}
反序列化
反序列化操作即从文件中读取数据并还原成内存中的对象,需要用到 ObjectInputStream 类。
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
InputStream inputStream = new FileInputStream("D:\\java\\test\\src\\com\\test\\a.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
People people = (People)objectInputStream.readObject();
objectInputStream.close();
inputStream.close();
System.out.println(people.getId());
System.out.println(people.getName());
System.out.println(people.getAge());
System.out.println(people.getGender());
}
}
// 输出
1
bob
18
男