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流原理及流的分类
Java
IO
流原理: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(); //关闭流
}
}