IO流
★★★第十八章 IO流
文件
文件是保存数据的地方。
文件在程序中是以流的形式来操作的。
流:数据在数据源(文件)和程序(内存)之间经历的路径。

- 输入流:数据从数据源(文件)到程序(内存)的路径。
- 输出流:数据从程序(内存)到数据源(文件)的路径。
常用的文件操作
创建文件
创建文件对象的相关构造器和方法:
new File(String pathname):根据路径构建一个File对象。new File(File parent, String child):根据 “父目录文件+子路径” 构建。new File(String parent, String child):根据 “父目录+子路径” 构建。
在构建
File对象之后,还要通过createNewFile方法来创建该文件。- 之所以还要使用
createNewFile方法来创建 “new出来的文件对象” 所对应的文件,是因为new出来的文件对象是在Java程序中,即内存中,而要真正在磁盘上创建该文件,还要使用createNewFile方法,可以结合上面的图理解。
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56package com.f.chapter19.createfile;
import java.io.File;
import java.io.IOException;
/**
* @author fzy
* @date 2023/7/7 14:45
* 演示创建文件
*/
public class CreateFile {
public static void main(String[] args) {
createFile01();
createFile02();
createFile03();
}
//1.`new File(String pathname)`:根据路径构建一个 `File` 对象。
public static void createFile01() {
String filePath = "C:\\Users\\1\\Desktop\\java\\file\\1.txt"; //路径
//这里只是在Java程序中新建一个File对象,还没有在磁盘中实际创建对应的文件
File file = new File(filePath);
try {
file.createNewFile(); //将文件在磁盘中实际创建起来
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("第一种方式创建文件成功...");
}
//2.`new File(File parent, String child)`:根据 “父目录文件+子路径” 构建。
public static void createFile02() {
File parentFile = new File("C:\\Users\\1\\Desktop\\java\\file\\"); //父目录文件
String childPath = "2.txt"; //子路径
File file = new File(parentFile, childPath);
try {
file.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("第二种方式创建文件成功...");
}
//3.`new File(String parent, String child)`:根据 “父目录+子路径” 构建。
public static void createFile03() {
String parentPath = "C:\\Users\\1\\Desktop\\java\\file\\"; //父目录
String childPath = "3.txt"; //子路径
File file = new File(parentPath, childPath);
try {
file.createNewFile();
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("第三种方式创建文件成功...");
}
}
获取文件的相关信息
getName():获取文件的名称。getAbsolutePath():获取文件的绝对路径。getParent():获取文件的父级目录。length():获取文件的大小(以字节为单位)。exists():判断文件是否存在。isFile():判断是否为文件。isDirectory():判断是否为文件夹目录。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
28package com.f.chapter19.fileinformation;
import java.io.File;
/**
* @author fzy
* @date 2023/7/8 12:36
*/
public class FileInformation {
public static void main(String[] args) {
//先在Java程序中创建文件对象
File file = new File("C:\\Users\\1\\Desktop\\java\\file\\1.txt");
//1.getName():获取文件的名称
System.out.println("文件名称 = " + file.getName());
//2.getAbsolutePath():获取文件的绝对路径
System.out.println("文件的绝对路径 = " + file.getAbsolutePath());
//3.getParent():获取文件的父级目录
System.out.println("文件的父级目录 = " + file.getParent());
//4.length():获取文件的大小(以字节为单位)
System.out.println("文件的字节长度为 = " + file.length());
//5.exists():判断文件是否存在
System.out.println("文件是否存在? -> " + file.exists());
//6.isFile():判断是否为文件
System.out.println("是否是文件? -> " + file.isFile());
//7.isDirectory():判断是否为文件夹目录
System.out.println("是否是文件夹目录? -> " + file.isDirectory());
}
}
目录操作
- 首先要知道,在
Java编程中,目录也被视为是文件。
mkdir():创建一级目录。mkdirs():创建多级目录。delete():删除空目录(如果目录非空,则删除失败)或文件。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
29package com.f.chapter19.file;
import java.io.File;
/**
* @author fzy
* @date 2023/7/8 12:55
*/
public class Directoty_ {
public static void main(String[] args) {
String dirPath = "C:\\Users\\1\\Desktop\\java\\file2";
File dir = new File(dirPath);
if (dir.exists()) {
System.out.println(dirPath + " 已存在,准备删除");
//删除该目录
if (dir.delete()) {
System.out.println(dirPath + " 删除成功");
} else {
System.out.println(dirPath + " 删除失败");
}
} else {
if (dir.mkdirs()) { //创建多级目录用mkdirs()
System.out.println(dir + " 创建成功");
} else {
System.out.println(dir + " 创建失败");
}
}
}
}
★★★IO流原理及流的分类
JavaIO流原理:I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输,如读/写文件,网络通讯等。Java程序中,对于数据的输入/输出操作以 “流(stream)” 的方式进行。Java.io包下提供了各种 “流” 类和接口,用以获取不同种类的数据,并通过方法输入或输出数据。- 输入
input:读取外部数据(磁盘、光盘等存储设备)的数据到程序(内存)中。 - 输出
output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

流的分类:
- 按操作数据单位不同分为:字节流(8bit)、字符流(按字符)。
- 按数据流的流向不同分为:输入流、输出流。
- 按流的角色的不同分为:节点流、处理流/包装流。
(抽象基类) 字节流 字符流 输入流 InputStream Reader 输出流 OutputStream Writer Java的IO流共涉及 40 多个类,但实际上非常规则,都是从如上 4 个抽象基类的基础上派生出来的。由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。

★★★节点流和处理流
- 节点流:可以从一个特定的数据源读写数据。如 **
FileReader、FileWriter**。 - 处理流(也叫包装流):“连接” 在已存在的流(节点流或处理流)之上,为程序提供更为强大的读写功能,也更加灵活。如 **
BufferedReader、BufferedWriter**。


节点流和处理流的区别和联系:
- 节点流是底层流/低级流,直接跟数据源相接。
- 处理流包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
- 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连。
- 处理流的功能主要体现在以下两个方面:
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
- 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便。
- 注意:在关闭处理流时,只需要关闭外层流即可,因为底层会去自动关闭节点流。
InputStream
InputStream抽象类是所有字节输入流的父类:public abstract class InputStream implements Closeable。InputStream常用的子类:FileInputStream:文件输入流。BufferedInputStream:缓冲字节输入流。ObjectInputStream:对象字节输入流。

FileInputStream
public class FileInputStream extends InputStream
public int read():从该输入流读取一个字节的数据,如果达到文件的末尾,则返回-1。是单个字节的读取,效率较低。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
36package com.f.chapter19.inputstream_;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author fzy
* @date 2023/7/8 13:47
* 演示FileInputStream的使用(字节输入流:文件 --> 程序)
*/
public class FileInputStream01 {
public static void main(String[] args) {
String filePath = "C:\\Users\\1\\Desktop\\java\\file\\hello.txt";
int readContent = 0;
FileInputStream fileInputStream = null;
try {
//创建 FileInputStream 对象,用于读取文件内容
fileInputStream = new FileInputStream(filePath);
//从 fileInputStream 读取一个字节的数据,如果未达到文件的末尾,则不停地读取
//如果 read 方法返回 -1,则表示读取完毕
//单个字节的读取,效率较低
while ((readContent = fileInputStream.read()) != -1) {
System.out.print((char) readContent);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//在 finally 里关闭流,避免资源的浪费
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}public int read(byte[] b):从该输入流读取最多b.length字节的数据到字节数组。如果读取正常,返回的是实际读取的字节数;如果达到文件的末尾,则返回-1。比每次只读取单个字节的效率高。
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
38package com.f.chapter19.inputstream_;
import java.io.FileInputStream;
import java.io.IOException;
/**
* @author fzy
* @date 2023/7/8 14:23
*/
public class FileInputStream02 {
public static void main(String[] args) {
String filePath = "C:\\Users\\1\\Desktop\\java\\file\\hello.txt";
byte[] buf = new byte[8]; //一次读取8个字节
FileInputStream fileInputStream = null;
int readLen = 0; //读取的字节数
int cnt = 0; //读取的次数
try {
fileInputStream = new FileInputStream(filePath);
//如果读取正常,read(byte[] b) 返回的是实际读取的字节数
//如果达到文件的末尾,则返回 -1
while ((readLen = fileInputStream.read(buf)) != -1) {
cnt++;
System.out.println("这次读取的字节数 = " + readLen);
System.out.println(new String(buf, 0, readLen));
}
System.out.println("共读取了 " + cnt + " 次");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}输出为:
1
2
3
4
5这次读取的字节数 = 8
hello,wo
这次读取的字节数 = 4
rld!
共读取了 2 次
BufferedInputStream
public class BufferedInputStream extends FilterInputStream
ObjectInputStream
public class ObjectInputStream extends InputStream implements ObjectInput, ObjectStreamConstants
使用
ObjectInputStream读取data.dat并反序列化恢复数据:注意:读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致。
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
43
44
45
46
47
48package com.f.chapter19.inputstream_;
//为了让反序列化后的Dog类的对象能调用Dog类的方法,所以需要可以引用该Dog类
import com.f.chapter19.outputstream_.Dog;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* @author fzy
* @date 2023/7/11 20:51
*/
public class ObjectInputStream_ {
public static void main(String[] args) {
//指定反序列化的文件
String filePath = "C:\\Users\\1\\Code Project\\Java project\\file\\data.dat";
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream(filePath));
//注意:读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
System.out.println(objectInputStream.readInt());
System.out.println(objectInputStream.readBoolean());
System.out.println(objectInputStream.readChar());
System.out.println(objectInputStream.readDouble());
System.out.println(objectInputStream.readUTF());
Object obj = null;
try {
obj = objectInputStream.readObject();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
System.out.println(obj + " 的运行类型为 " + obj.getClass());
Dog dog = (Dog) obj;
System.out.println(dog.getName()); //调用Dog类的getName方法
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
objectInputStream.close();
System.out.println("读取成功!");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
OutputStream
OutputStream抽象类是所有字节输出流的父类:public abstract class OutputStream implements Closeable, Flushable。OutputStream常用的子类:FileOutputStream:文件输出流。BufferedOutputStream:缓冲字节输出流。ObjectOutputStream:对象字节输出流。
FileOutputStream
public class FileOutputStream extends OutputStream
public void write(int b):将指定的字节写入此文件输出流,如果文件不存在,则创建该文件,下面的方法同理。public void write(byte b[]):将b.length个字节从指定的字节数组写入此文件输出流。public void write(byte b[], int off, int len):将len个字节从位于偏移量off的指定字节数组写入此文件输出流。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
37package com.f.chapter19.outputstream_;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @author fzy
* @date 2023/7/8 14:41
* 演示FileOutputStream的使用(字节输出流:程序 --> 文件)
*/
public class FileOutputStream01 {
public static void main(String[] args) {
String filePath = "C:\\Users\\1\\Desktop\\java\\file\\hello.txt";
FileOutputStream fileOutputStream = null;
try {
//true表示在写入时是添加在文件末尾,默认为false
fileOutputStream = new FileOutputStream(filePath);
//fileOutputStream = new FileOutputStream(filePath, true);
//1.`public void write(int b)`:将指定的字节写入此文件输出流。
fileOutputStream.write('a');
//2.`public void write(byte b[])`:将 `b.length` 个字节从指定的字节数组写入此文件输出流。
String str = "hello!";
fileOutputStream.write(str.getBytes()); //getBytes():将字符串转换为字符数组
//3.`public void write(byte b[], int off, int len)`:将 `len` 个字节从位于偏移量 `off` 的指定字节数组写入此文件输出流。
fileOutputStream.write(str.getBytes(), 0, str.length());
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
fileOutputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}注意:在对磁盘中的文件创建
FileOutputStream对象时,如果FileOutputStream构造器的append参数接收到的是true,表示在写入时是添加在文件末尾,反之则是重写原来的文件。但是需要注意,在创建好了
FileOutputStream对象后,每次调用该对象的write方法时,都是添加在已经写好的文件内容的后面,不是调用write方法一次,就重写文件一次。除非又对该文件创建了FileOutputStream对象。
BufferedOutputStream
public class BufferedOutputStream extends FilterOutputStream
ObjectOutputStream
public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants
使用
ObjectOutputStream序列化基本数据类型和一个Dog对象(name, age),并
保存到data.dat文件中: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
39package com.f.chapter19.outputstream_;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/**
* @author fzy
* @date 2023/7/11 20:57
*/
public class ObjectOutputStream_ {
public static void main(String[] args) {
//序列化后,保存的文件格式不是纯文本的,而是按照他的格式来保存的
String filePath = "C:\\Users\\1\\Code Project\\Java project\\file\\data.dat";
ObjectOutputStream objectOutputStream = null;
try {
//ObjectOutputStream是处理流
objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
//序列化数据到文件中
objectOutputStream.writeInt(100); //int --自动装箱--> Integer,Integer实现了 Serializable 接口
objectOutputStream.writeBoolean(true); //原理同上 boolean -> Boolean
objectOutputStream.writeChar('a'); //原理同上 char -> Character
objectOutputStream.writeDouble(1.1); //原理同上 double -> Double
objectOutputStream.writeUTF("Java"); //写入字符串是用 writeUTF
objectOutputStream.writeObject(new Dog("小黄", 3)); //写入Dog对象
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
try {
objectOutputStream.close();
System.out.println("数据保存完成(序列化形式)");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}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
30package com.f.chapter19.outputstream_;
import java.io.Serializable;
/**
* @author fzy
* @date 2023/7/11 21:41
*/
//为了让Dog类对象能序列化地保存,让其实现Serializable接口
public class Dog implements Serializable {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
PrintStream
public class PrintStream extends FilterOutputStream implements Appendable, Closeable字节打印流:
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
34package com.f.chapter19.print_;
import java.io.IOException;
import java.io.PrintStream;
/**
* @author fzy
* @date 2023/7/12 16:37
*/
public class PrintStream_ {
public static void main(String[] args) throws IOException {
PrintStream out = System.out;
//在默认情况下,PrintStream 输出数据的位置是标准输出,即显示器
/*public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}*/
out.print("hello,world!\n");
// 因为print最后调用的是write方法,所以也可以直接调用write方法进行输出
out.write("hi!".getBytes());
//可以修改打印流输出的位置, 修改为到 hi.txt 文件中
/*public static void setOut(PrintStream out) {
checkIO();
setOut0(out); //native方法,修改了out
}*/
System.setOut(new PrintStream("C:\\Users\\1\\Code Project\\Java project\\file\\hi.txt"));
System.out.println("hi,txt!");
out.close(); //关闭流
}
}