目录
[二、 树型结构组织和目录](#二、 树型结构组织和目录)
[1. 绝对路径](#1. 绝对路径)
[2. 相对路径](#2. 相对路径)
[1. 构造方法](#1. 构造方法)
[2. 方法](#2. 方法)
[1. InputStream概述](#1. InputStream概述)
[2. FileInputStream概述](#2. FileInputStream概述)
[2.1 构造方法](#2.1 构造方法)
[2.2 示例](#2.2 示例)
[3. OutputStream概述](#3. OutputStream概述)
[3.1 方法](#3.1 方法)
[3.2 示例](#3.2 示例)
[4. Reader 和 Writer](#4. Reader 和 Writer)
一、认识文件
文件是数据的集合,以特定格式存储在计算机或其他设备上。文件可以包含文本、图像、音频、视频等各种类型的信息。
- 狭义上的文件:保存在硬盘上的文件
- 广义上的文件:操作系统进行资源管理的一种机制(软件/硬件资源抽象成"文件")
硬盘:
用来存储数据,可以存储数据的还有CPU寄存器和内存。
(注:MySQL存储数据的时候,也是保存到硬盘上的)
| | 存储空间 | 访问速度 | 读写速度 | 成本 | 持久化存储 |
| 硬盘 | 很大(几TB) | 慢 | 慢 | 便宜 | 可持久 |
| 内存 | 小(16GB/32GB/...) | 快 | 快 | 贵 | 不可持久 |
CPU寄存器 非常小(<1KB) 非常快 非常快 不单卖 不可持久 [硬盘、内存和CPU寄存器的区别]
二、 树型结构组织和目录
同时,随着文件越来越多,对文件的系统管理也被提上了日程,如何进行文件的组织呢,⼀种合乎自然的想法出现了,就是按照层级结构进行组织⸺也就是我们数据结构中学习过的树形结构。这样, ⼀种专门用来存放管理信息的特殊文件诞生了,也就是我们平时所谓文件夹(folder)或者目录
(directory)的概念。
文件夹也是文件(目录文件)。
三、文件路径
文件路径是指文件在计算机系统中存储的具体位置。在Windows和Mac OS系统中,文件路径通常由文件夹(目录)的层级结构表示,通过一系列文件夹名称和分隔符组成,以便于定位和访问文件。
绝对路径和相对路径是用于描述文件或文件夹在文件系统中位置的两种不同方式。
1. 绝对路径
绝对路径是从文件系统的根目录开始,沿着路径一直到文件或文件夹的准确位置的完整路径。
在Windows系统中,文件路径通常以盘符(如C:\)开始,然后依次列出每个文件夹的名称,使用反斜杠(\)或斜杠(/)作为分隔符,例如:C:\Users\Username\Documents\File.txt。
注:写代码时,建议"/","\"需要用转义字符。
2. 相对路径
相对路径是相对于当前工作目录(当前位置)的路径。它描述文件或文件夹相对于当前位置的位置关系。
相对路径不以根目录开始,而是相对于当前位置的文件路径。
例如,假设当前工作目录是C:\Users\Username,在该位置下的Documents文件夹中的文件File.txt的相对路径可以表示为Documents\File.txt。
相对路径的优点是更简洁、易于移植,并且可以根据当前位置灵活地定位文件。
例:
java
String path = "./test_4_10";
基准路径是谁?
- 1.在IDEA中直接运行:基准路径就是项目的目录(D:\code\java\bite_class_code)
- 打一个jar包,单独运行jar包:当前在哪个目录下执行运行命令,基准目录就是哪个目录
- 打成一个war包,放到tomcat中去执行:基准路径就是tomcat的bin目录
四、文件类型
即使是普通文件,根据其保存数据的不同,夜京城被分为不同的类型,我们一般简单的划分为文本文件(如图片、音频、视频、可执行程序......)和二进制文件(如txt纯文本文件、.java......),分别指代保存杯被字符集编码的文本和按照标准格式保存的非被字符集编码过的文件。
所有的文件都是二进制的,但是有一些事特殊的,二进制数据的编码方式刚好能构成字符。
区分方法:用记事本打开,出现乱码的是二进制文件,不是乱码的是文本文件。
五、文件操作
Java中通过 java.io.File 类来对一个文件(包括目录)进行抽象的描述。注意,有File对象,并不代表真实存在该文件。
1. 构造方法
方法签名 | 说明 |
---|---|
File(File parent, String child) | 根据父目录 + 孩子文件路径,创建⼀个新的 File 实例 |
File(String pathname) | 根据文件路径创建⼀个新的 File 实例,路径可以是绝对路径或者相对路径 |
File(String parent, String child) | 根据父目录 + 孩子文件路径,创建⼀个新的 File 实例,父目录用路径表示 |
2. 方法
修饰符及返回值类型 | 方法签名 | 说明 |
---|---|---|
String | getParent() | 返回 File 对象的父目录文件路径 |
String | getName() | 返回 FIle 对象的纯文件名称 |
String | getPath() | 返回 File 对象的文件路径 |
String | getAbsolutePath() | 返回 File 对象的绝对路径 |
String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径 |
boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
boolean | isDirectory() | 判断 File 对象代表的文件是否是⼀个目录 |
boolean | isFile() | 判断 File 对象代表的文件是否是⼀个普通文件 |
boolean | createNewFile() | 根据 File 对象,自动创建⼀个空文件,成功创建后返回 true |
boolean | delete() | 根据 File 对象,删除该文件,成功删除后返回 true |
void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行 |
String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示 |
boolean | mkdir() | 创建 File 对象代表的目录 |
boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录 |
boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |
boolean | canRead() | 判断用户是否对文件有可读权限 |
boolean | canWrite() | 判断用户是否对文件有可写权限 |
示例:
java
import java.io.File;
import java.io.IOException;
public class FileOperationandIO {
public static void main(String[] args) throws IOException {
File file = new File("./text.txt");
System.out.println(file.getParent());
System.out.println(file.getName());
System.out.println(file.getPath());
System.out.println(file.getAbsolutePath());
System.out.println(file.getCanonicalPath()); //绝对路径的简化版本
file.createNewFile();
System.out.println(file.exists());
System.out.println(file.isFile());
System.out.println(file.isDirectory());
}
}
运行结果:
.
text.txt
.\text.txt
D:\code\java\bite_class_code\.\text.txt
D:\code\java\bite_class_code\text.txt
true
true
false
并且在源路径下创建了一个新文件"text.txt":
java
import java.io.File;
import java.io.IOException;
public class FileOperationandIO {
public static void main(String[] args) throws IOException, InterruptedException {
File file = new File("./test.txt");
Boolean result = file.delete();
System.out.println(result);
}
}
运行结果:
且目录中的"text.txt"消失。
java
import java.io.File;
import java.io.IOException;
public class FileOperationandIO {
public static void main(String[] args) throws IOException, InterruptedException {
File file = new File("./text.txt");
file.deleteOnExit();
Thread.sleep(10000);
}
}
运行结果:
过10秒钟之后,进程结束时,文件被删除。
deleteOnExit 应用场景:
office 系列产品会在编辑文芳过程中产生一个隐藏的临时文件,用来保存正在编辑的内容。
如果 office 正常关闭了,此时这个临时文件会自动删除; =>deleteOnExit
如果 office 异常关闭了,这个临时文件仍然存在,下次启东时就可以恢复上次编辑的内容。
(类似于这样的功能,服务器端也会涉及到)
java
import java.io.File;
import java.io.IOException;
public class FileOperationandIO {
public static void main(String[] args) {
File file = new File("./test");
Boolean result = file.mkdir();
System.out.println(result);
}
}
运行结果:
并且在源目录下创建了一个"test"文件夹:
java
import java.io.File;
import java.io.IOException;
public class FileOperationandIO {
public static void main(String[] args) {
File file = new File("./test/111/222");
Boolean result = file.mkdirs();
System.out.println(result);
}
}
运行结果:
并且创建了多级目录:
java
import java.io.File;
public class FileOperationandIO {
public static void main(String[] args) {
File file = new File("./test.txt");
File newfile = new File("./test2.txt");
System.out.println(file.renameTo(newfile));
}
}
运行结果:
且文件"test.txt"更名成"test2.txt":
注:此方法也可以用于移动文件。
六、文件内容的读写------数据流
- 字节流:读写文件,以字节为单位(针对二进制文件)
如:InputStream(输入),OutputStream(输出)
- 字符流:读写文件,以字符为单位(针对文本文件)
如:Reader(输入),Writer (输出)
1. InputStream概述
InputStream(输入流)是Java中用于从输入源读取数据的抽象类。InputStream类是所有输入流类的超类,它定义了一系列读取字节的方法,可以从不同的数据源(文件、网络、内存等)读取数据。
InputStream类的常见子类包括:
-
FileInputStream:从文件中读取数据的输入流。
-
BufferedInputStream:带缓冲功能的输入流,可以提高数据读取的效率。
-
DataInputStream:用于读取基本数据类型(如int、float、double等)的输入流。
-
ObjectInputStream:用于读取Java对象的输入流,主要用于对象的序列化和反序列化。
通过使用InputStream及其子类,可以方便地处理不同输入源的数据读取操作,同时提供了丰富的方法来满足不同数据读取需求。在使用InputStream时,需要注意及时关闭流以释放资源,可以通过try-with-resources语句或手动关闭流来实现。
方法:
修饰符及返回值类型 | 方法签名 | 说明 |
---|---|---|
int | read() | 读取⼀个字节的数据,返回 -1 代表已经完全读完了 |
int | read(byte[] b) | 最多读取 b.length 字节的数据到 b中,返回实际读到的数量;-1 代表以及读完了 |
int | read(byte[] b, int off, int len) | 最多读取 len - off 字节的数据到 b中,放在从 off 开始,返回实际读到的数量;-1 代表以及读完了 |
void | close() | 关闭字节流 |
2. FileInputStream概述
2.1 构造方法
签名 | 说明 |
---|---|
FileInputStream(File file) | 利用File构造文件输入流 |
FileInputStream(String name) | 利用文件路径构造文件输入流 |
2.2 示例
java
import java.io.*;
public class FileOperationandIO {
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try {
inputStream = new FileInputStream("./text.txt");
// 相当于C语言中的fopen
} finally {
inputStream.close(); // 关闭文件
}
}
}
读文件操作。
但是这样写有点丑,并且有忘记"close"的风险。
所以我们一般用 "try with resource" 方法:
javatry (InputStream inputStream = new FileInputStream("./text.txt")) { }// 出了try自动关闭文件
我们在text.txt中写入"你好"字符:

java
import java.io.*;
public class FileOperationandIO {
public static void main(String[] args) throws IOException {
try (InputStream inputStream = new FileInputStream("./text.txt")) {
//读文件操作
while (true) {
// 一次读一个字节
int data = inputStream.read();
if (data == -1) {
// 文件读完
break;
}
System.out.printf("0x%x\n", data); //打印utf-8的十六进制编码
}
}
}
}
运行结果:
前三行是"你"的十六进制utf-8编码,后三行是"好"的utf-8编码。
java
import java.io.*;
public class FileOperationandIO {
public static void main(String[] args) throws IOException {
try (InputStream inputStream = new FileInputStream("./text.txt")) {
while (true) {
// 一次读多个字节
byte[] data = new byte[3];
// 读操作,就会尽可能的把字节数组给填满
// 填不满的话能填几个就是几个
// 此处的n就表示实际读了几个字节
int n = inputStream.read(data);
System.out.println("n = " + n);
if (n == -1) {
break;
}
for (int i = 0; i < n; i++) {
System.out.printf("0x%x\n", data[i]);
}
System.out.println("==================");
}
}
}
}
运行结果:
3. OutputStream概述
3.1 方法
修饰符及返回值类型 | 方法签名 | 说明 |
---|---|---|
void | write(int b) | 写入要给字节的数据 |
void | write(byte[] b) | 将 b 这个字符数组中的数据全部写入 os 中 |
int | write(byte[] b, int off, int len) | 将 b 这个字符数组中从 off 开始的 数据写入 os 中,⼀共写 len 个 |
void | close() | 关闭字节流 |
void | flush() | 重要:我们知道 I/O 的速度是很慢的,所以,大多的 OutputStream 为了减少设备操作的次数,在写数据的时候都会将数据先暂时写入内 存的⼀个指定区域里,直到该区域满了或者其他指定条件时才真正将数据写入设备中,这个区域⼀般称为缓冲区。但造成⼀个结果,就是我们写的数据,很可能会遗留⼀部分在缓冲区中。需要在最后或者合适的位置,调用 flush(刷新)操作,将数据刷到设备中。 |
说明:
- 默认情况下会创建不存在的文件
- 打开文件的时候会默认清除上次的文件内容,否则要添加append参数
3.2 示例
java
import java.io.*;
public class FileOperationandIO {
public static void main(String[] args) throws IOException {
try (OutputStream outputStream = new FileOutputStream("./output.txt", true)) {
// 97在ascii码中对应'a'
outputStream.write(97);
outputStream.write(98);
outputStream.write(99);
} catch (IOException e) {
// 此处需要处理两个异常,但因为针对两个异常的处理方法是一样的,所以直接合并了
throw new RuntimeException(e);
}
}
}
运行结果:
代码运行结束之后,创建了一个新文件"output"文件,并写入了"abc"三个字符。
java
import java.io.*;
public class FileOperationandIO {
public static void main(String[] args) throws IOException {
try (OutputStream outputStream = new FileOutputStream("./output.txt", true)) {
// 一次写入多个字符
byte[] bytes = {100, 101, 102};
outputStream.write(bytes);
} catch (IOException e) {
// 此处需要处理两个异常,但因为针对两个异常的处理方法是一样的,所以直接合并了
throw new RuntimeException(e);
}
}
}
运行结果:
在文件中追加了"def"三个字符:
4. Reader 和 Writer
用法和InputStream/OutputStream很像,而Reader/Writer是处理字符流的抽象类,用于读写字符数据。在处理文本文件等字符数据时,通常使用Reader/Writer。
java
import java.io.*;
public class FileOperationandIO {
public static void main(String[] args) throws IOException {
try (Reader reader = new FileReader("./text.txt")) {
while (true) {
int c = reader.read();
if (c == -1) {
break;
}
System.out.println((char)c);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
运行结果:
java
import java.io.*;
public class FileOperationandIO {
public static void main(String[] args) throws IOException {
try (Writer writer = new FileWriter("./text.txt")) {
writer.write("Hello World!");
}
}
}
运行结果:
七、练习
扫描指定目录,并找到名称中包含指定字符的所有普通文件(不包含目录),并且后续询问用户是否要删除该文件。
java
import java.io.File;
import java.util.Scanner;
public class Exercise_deleteFile {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要扫描的根目录:");
String rootDirpath = scanner.next();
File rootDir = new File(rootDirpath);
if (!rootDir.isDirectory()) {
System.out.println("输入的不是目录!");
return;
}
System.out.println("请输入要删除的关键字:");
String keyword = scanner.next();
scanDir(rootDir, keyword);
}
private static void scanDir(File rootDir, String keyword) {
// 1.列出当前目录中包含的所有文件
File[] files = rootDir.listFiles();
if (files == null) {
return;
}
for (File file: files) {
// 2.遍历files
if (file.isFile()) {
// 3.如果是文件,则判断是否含有关键字
dealFile(file, keyword);
} else {
// 4.如果是目录,则递归调用该方法
scanDir(file, keyword);
}
}
}
private static void dealFile(File file, String keyword) {
if (file.getName().contains(keyword)) {
System.out.println("发现文件:" + file.getAbsolutePath() + "\n是否删除(y/n)");
Scanner scanner = new Scanner(System.in);
String result = scanner.next();
if (result.equals("y")) {
file.delete();
System.out.println("文件已删除!");
}
}
}
}
运行结果:
且文件已经删除。
进行普通文件的复制。
java
import java.io.*;
import java.util.Scanner;
public class Exercise_copyFile {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入源文件路径:");
String srcRoot = scanner.next();
System.out.println("请输入目标文件路径:");
String destRoot = scanner.next();
File srcFile = new File(srcRoot);
if (!srcFile.isFile()) {
System.out.println("源文件不存在!");
return;
}
File destFile = new File(destRoot);
//目标文件不一定存在,但是其根目录必须要求存在
if (!destFile.getParentFile().isDirectory()) {
System.out.println("目标文件的根目录不存在!");
return;
}
// 复制文件
try (InputStream inputStream = new FileInputStream(srcFile);
OutputStream outputStream = new FileOutputStream(destFile)) {
while (true) {
byte[] bytes = new byte[1024];
int data = inputStream.read();
if (data == -1) {
break;
}
// 此处的write不应该写整个bytes数组
// bytes数组不一定能被填满,要按照data这个长度来写入
outputStream.write(bytes, 0, data);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
运行结果:
text.txt文件:
text1.txt文件:
扫描指定目录,并找到名称或者内容中包含指定字符的所有普通文件(不包含目录)。
java
import java.io.*;
import java.util.Scanner;
// 扫描指定目录, 并找到名称或者内容中包含制定字符的所有普通文件
public class Exercise_seekFile {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要扫描的目录:");
String rootPath = scanner.next();
File rootFile = new File(rootPath);
if (!rootFile.isDirectory()) {
System.out.println("输入的不是目录!");
return;
}
System.out.println("请输入要搜索的关键词:");
String keyword = scanner.next();
scanDir(rootFile, keyword);
}
private static void scanDir(File rootFile, String keyword) {
File[] files = rootFile.listFiles();
if (files == null) {
return;
}
for (File file: files) {
if (file.isFile()) {
seekFile(file, keyword);
} else {
scanDir(file, keyword);
}
}
}
private static void seekFile(File file, String keyword) {
if (file.getName().contains(keyword)) {
System.out.println("找到名称中含Keyword的文件:" + file.getAbsolutePath());
return;
}
StringBuilder stringBuilder = new StringBuilder();
try (Reader reader = new FileReader(file)) {
while (true) {
char[] chars = new char[1024];
int data = reader.read(chars);
if (data == -1) {
break;
}
stringBuilder.append(chars, 0, data);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
if (stringBuilder.indexOf(keyword) >= 0) {
System.out.println("找到内容中包含keyword的文件:" + file.getAbsolutePath());
}
}
}
运行结果:
创作不易,麻烦给个三连呗