一.文件操作
1.核心概念
① 文件 : 存储数据 ; 目录 : 存储文件/子目录的容器 ; Java 中均通过抽象类/接口表示
② 绝对路径 : 从根目录触发(如 D:/test/file.txt) ; 相对路径 : 从当前项目出发(如果再 IDEA 中直接运行 , 那么基准路径就是项目目录 ; 如果打了 jar 包 , 单独运行 jar 包 , 当前再哪个目录下执行运行命令的就是基准目录)
③ 流: 传统 IO 的核心 , 分为字节流(操作任意文件) 和 字符流(仅操作文本文件)
④ 文本文件 : 数据以字符为基本 , 通过字符编码将字符映射为字节存储(能被记事本直接打开读懂) ; 二进制文件 : 以字节为单位 , 按一定格式存储(计算机可直接解析的原始字节流 , 用记事本打开为乱码)
2.Java 中操作系统文件
Java 中通过 java.io.file 类来对一个文件(包括目录)进行抽象描述 ;
注意 : 有 file 对象并不代表有真实文件存在
① 属性
|--------------------------|---------------------------------|--------------------------|
| 静态常量 | 作用 | 示例(Windows/Linux) |
| File.separator | 系统默认的「文件路径分隔符」 | Windows:\ Linux:/ |
| File.pathSeparator | 系统默认的「路径列表分隔符」(如环境变量 PATH) | Windows:; Linux:: |
| File.separatorChar | 字符类型的路径分隔符(同 separator) | Windows:'\'Linux:'/' |
| File.pathSeparatorChar | 字符类型的路径列表分隔符(同 pathSeparator) | Windows:';'Linux:':' |
java
// 避免硬编码 "\\" 或 "/",用 separator 跨平台兼容
String path = "data" + File.separator + "test.txt";
System.out.println(path);
// Windows 输出:data\test.txt;Linux 输出:data/test.txt
② 构造方法
|-------------------------------------|----------------------------------------|
| 方法名 | 说明 |
| File(File parent, String child) | 根据父目录 + 孩子文件路径,创建一个新的 File 实例 |
| File(String pathname) | 根据文件路径创建一个新的 File 实例,路径可以是绝对路径或者相对路径 |
| File(String parent, String child) | 根据父目录 + 孩子文件路径,创建一个新的 File 实例,父目录用路径表示 |
③ 常用方法 (基础)
|---------|--------------------|-------------------------------------------------|
| 返回值 | 方法签名 | 说明 |
| String | getParent() | 返回 File 对象的父目录文件路径 |
| String | getName() | 返回 File 对象的纯文件名称 |
| String | getPath() | 返回 File 对象的文件路径(构造方法传入绝对 / 相对路径,此方法就返回对应路径) |
| String | getAbsolutePath() | 返回 File 对象的绝对路径(无论构造方法传相对 / 绝对路径,此方法都返回绝对路径) |
| String | getCanonicalPath() | 返回 File 对象的修饰过的绝对路径(简化路径,把.//../等符号替代为实际路径) |
| boolean | exists() | 判断 File 对象描述的文件是否真实存在 |
| boolean | isDirectory() | 判断 File 对象代表的文件是否是一个目录 |
| boolean | isFile() | 判断 File 对象代表的文件是否是一个普通文件 |
java
import java.io.File;
import java.io.IOException;
public class demo1 {
public static void main(String[] args) throws IOException {
//File file = new File("D:/JavaCode/src/FIle/demo.txt");//绝对路径
//File file1 = new File("D:/JavaCode/src/FIle", "demo.txt");
//File parent = new File("D/JavaCode/src/FIle");new File(parent, "demo.txt");
File file2 = new File("./demo.txt");//相对路径
System.out.println(file2.getParent());
System.out.println(file2.getName());
System.out.println(file2.getPath());
System.out.println(file2.getAbsoluteFile());
System.out.println(file2.getCanonicalFile());
System.out.println(file2.exists());
System.out.println(file2.isDirectory());
System.out.println(file2.isFile());
}
}

④ 常用方法
|---------------|-------------------------|------------------------------------------------------|
| 修饰符及返回值类型 | 方法签名 | 说明(含手写补充) |
| boolean | createNewFile() | 根据 File 对象,自动创建一个空文件。成功创建后返回 true(注:需处理 IO 异常) |
| boolean | delete() | 根据 File 对象,删除该文件。成功删除后返回 true |
| void | deleteOnExit() | 根据 File 对象,标注文件将被删除,删除动作会到 JVM 运行结束时才会进行 |
| String[] | list() | 返回 File 对象代表的目录下的所有文件名 |
| File[] | listFiles() | 返回 File 对象代表的目录下的所有文件,以 File 对象表示(可以对 File 数组继续进行操作) |
| boolean | mkdir() | 创建 File 对象代表的目录(make directory) |
| boolean | mkdirs() | 创建 File 对象代表的目录,如果必要,会创建中间目录(创建多级目录) |
| boolean | renameTo(File dest) | 进行文件改名,也可以视为我们平时的剪切、粘贴操作 |
| boolean | canRead() | 判断用户是否对文件有可读权限 |
| boolean | canWrite() | 判断用户是否对文件有可写权限 |
示例 1 : file1.createNewFile();
java
import java.io.File;
import java.io.IOException;
public class demo2 {
public static void main(String[] args) throws InterruptedException {
File file = new File("./demo3.java");
file.deleteOnExit();
Thread.sleep(1000);
}
public static void main1(String[] args) throws IOException {
File file1 = new File("./text.txt");
file1.createNewFile();
System.out.println(file1.exists());
System.out.println(file1.isFile());
System.out.println(file1.isDirectory());
//System.out.println(file1.delete());//若执行这行代码则文件消失
}

示例 2 : 返回文件名
java
import java.io.File;
import java.util.Arrays;
public class demo4 {
public static void main(String[] args) {
File file = new File("d:/");
String[] list = file.list();
System.out.println(Arrays.toString(list));
File[] files = file.listFiles();
System.out.println(Arrays.toString(files));
}
}
示例 3 : 创建目录
java
import java.io.File;
public class demo5 {
public static void main(String[] args) {
File file = new File("./test/1/2/3");
Boolean a = file.mkdirs();
System.out.println(a);//true,mkdirs() 能同时创建多级目录
Boolean b = file.mkdir();
System.out.println(b);//false,mkdir() 只能创建单级目录
}
}
示例 4 : 剪切操作(需要执行上一个代码块)
java
import java.io.File;
public class demo6 {
public static void main(String[] args) {
File file = new File("./test");
File file2 = new File("./src/test");
boolean a = file.renameTo(file2);
System.out.println(a);
}
}
3.文件内容操作 -- 数据流
Java 文件内容操作 , 核心基类是四个抽象类**(InputStream/OutputStream/Reader/Writer)**
基类 ,对应 4 个常用文件实现类(FileInputStream**/** FileOutputStream**/** FileReader**/** FileWriter) , 共八个核心类 , 覆盖二进制文件读写和文本文件读写的所有场景
|--------|-------------------|--------------------|----------------------|------------|
| 体系 | 抽象基类(2 个) | 文件实现类(2 个) | 核心用途 | 操作对象 |
| 字节流 | InputStream(读) | FileInputStream | 读取二进制文件(图片、视频、压缩包等) | 字节(byte) |
| 字节流 | OutputStream(写) | FileOutputStream | 写入二进制文件 | 字节(byte) |
| 字符流 | Reader(读) | FileReader | 读取文本文件(.txt/.java 等) | 字符(char) |
| 字符流 | Writer(写) | FileWriter | 写入文本文件 | 字符(char) |
4. 字节流 -- 操作二进制文件
4.1 抽象基类 InputStream (字节输入流)
InputStream 是所有字节输入流的父类 , 抽象类
核心方法 :
|-------------------------------------------|-----------------------------------|-----------------------------------|
| 方法签名 | 作用 | 备注 |
| int read() | 读 1 个字节,返回字节值(0~255);读到末尾返回 -1 | 单字节读取,效率低 |
| int read(byte[] buffer) | 读多个字节到缓冲区,返回实际读取的字节数;末尾返回 -1 | 推荐!批量读取,效率高 |
| int read(byte[] buffer,int off,int len) | 读 len个字节到缓冲区,从 off 下标开始存储 | 精细控制读取范围 |
| void close() | 关闭流,释放资源 | 必须调用(建议用 try-with-resources 自动关闭) |
| long skip(long n) | 跳过 n个字节不读 | 少用,适用于大文件跳转 |
4.2 文件实现类 FIleInputStream(文件字节输入流)
InputStream 的子类 , 专门用于从文件读取字节
构造方法 :
java
// 1. 传入文件路径(字符串)
InputStream in = new FileInputStream("D:/test.jpg");
// 2. 传入 File 对象
File file = new File("D:/test.jpg");
InputStream in = new FileInputStream(file);
示例 1 : 将文件完全读的两种方式
java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class demo7 {
public static void main(String[] args) throws IOException {
InputStream inputStream = null;
try {
inputStream = new FileInputStream("./test.txt");
while(true){
int data = inputStream.read();
if(data == -1){
break;
}
System.out.printf("0x%x ",data);
}
}
finally {
inputStream.close();
}
}
}

java
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
public class demo8 {
public static void main(String[] args) throws IOException {
try(InputStream inputStream = new FileInputStream("./test.txt")){
//此处资源要求在try括号内直接声明并初始化, 并且使用括号将资源包裹起来, 避免出现异常时资源未关闭的情况
while(true){
byte[] data = new byte[1024];
int n = inputStream.read(data);//返回读取的字节数
System.out.println("n="+n);
if(n == -1){
break;
}
for(int i = 0;i<n;i++){
System.out.print(data[i]+" ");
}
System.out.println();
}
}
}
}

4.3 抽象基类 : OutputStream(字节输出流)
OutputStream 是所有字节输出流的父类 , 抽象类
核心方法 :
|-----------|-------------------------------------|---------------------------------------------------------------------------------|
| 返回值类型 | 方法签名 | 说明 |
| void | write(int b) | 写入要给字节的数据 |
| void | write(byte[] b) | 将 b 这个字节数组中的数据全部写入输出流中 |
| int | write(byte[] b, int off, int len) | 将 b 这个字节数组中从 off 开始的数据写入输出流中,一共写 len 个 |
| void | close() | 关闭字节流 |
| void | flush() | 重要:I/O 速度较慢,多数 OutputStream 会先将数据写入缓冲区以减少设备操作次数;调用此方法可将缓冲区中残留的数据刷入设备,避免数据丢失 |
4.4 文件实现类 : FileOutputStream(文件字节输出流)
FileOutputStream 是 OutputStream 子类 , 用于向文件写入字节
构造方法 :
java
// 1. 覆盖写入(文件已存在则清空原有内容)
OutputStream out = new FileOutputStream("D:/test_copy.jpg");
// 2. 追加写入(文件已存在则在末尾添加内容)
OutputStream out = new FileOutputStream("D:/test_copy.jpg", true);
// 3. 传入 File 对象
File file = new File("D:/test_copy.jpg");
OutputStream out = new FileOutputStream(file, true);
示例 1 : 写入字节
java
import java.io.*;
public class demo9 {
public static void main(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./test.txt")){//如果不加true将会覆盖原文件内容
//此处需要处理两个异常
byte[] data = {88,66,85};
outputStream.write(data);
}
}
public static void main1(String[] args) throws IOException {
try(OutputStream outputStream = new FileOutputStream("./test.txt",true)){
//此处需要处理两个异常
outputStream.write(78);
outputStream.write(99);
outputStream.write(79);
}
}
}
5. 字符流 -- 操作文本文件
5.1 抽象基类 : Reader(字符输入流)
Reader 是所有字符输入流的父类 , 抽象类
核心方法 :
|-------------------------------------------|-----------------------------------|-----------|
| 方法签名 | 作用 | 备注 |
| int read() | 读 1 个字符,返回字符的 Unicode 码;末尾返回 -1 | 单字符读取,效率低 |
| int read(char[] cbuf) | 读多个字符到字符缓冲区,返回实际读取的字符数;末尾返回 -1 | 推荐!批量读取 |
| int read(char[] cbuf, int off, int len) | 读 len个字符到缓冲区,从 off 开始存储 | 精细控制读取范围 |
| void close() | 关闭流,释放资源 | 必须调用 |
5.2 文件实现类 : FileReader(文件字符输入流)
FileReader 是 Reader 的子类 , 用于文本文件读取字符(自动按系统默认编码)
构造方法 :
java
// 1. 传入文件路径
Reader reader = new FileReader("D:/test.txt");
// 2. 传入 File 对象
File file = new File("D:/test.txt");
Reader reader = new FileReader(file);
示例 1 : 将文件完全读的两种方式
java
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
public class demo10 {
public static void main(String[] args) throws IOException {
try(Reader reader = new FileReader("./test.txt")){
while(true){
char[] data = new char[1024];//缓冲区,也可以创建在循环外面重复使用
int len = reader.read(data);//输出型参数
//返回实际读取的字符个数,如果为-1表示已经读取完毕
if(len == -1){
break;
}
for (int i = 0; i < len; i++) {
System.out.print(data[i]+" ");
}
}
}
}
public static void main1(String[] args) throws IOException {
try(Reader reader = new FileReader("./test.txt")){//这里要处理两个异常其中一个是IOException子类,另一个是IOException
while(true){
int data = reader.read();
if(data == -1){
break;
}
System.out.printf("0x%x ",data);
}
}
}
}

5.3 抽象基类 : Writer(字符输出流)
所有字符输出流的父类 , 是抽象类
核心方法 :
|---------------------------------------------|----------------------------|--------------|
| 方法签名 | 作用 | 备注 |
| void write(int c) | 写 1 个字符(c是 Unicode 码) | 单字符写入,效率低 |
| void write(char[] cbuf) | 把字符缓冲区的所有字符写入 | 批量写入,效率高 |
| void write(char[] cbuf, int off, int len) | 写入缓冲区从 off开始的 len个字符 | 精细控制写入范围 |
| void write(String str) | 直接写字符串(无需手动转字符数组) | 最常用!文本文件写入首选 |
| void write(String str, int off, int len) | 写字符串从 off开始的 len个字符 | 截取字符串写入 |
| void flush() | 刷新缓冲区(字符流必须调用,否则数据可能未写入磁盘) | 关键!避免数据丢失 |
| void close() | 关闭流(关闭前会自动刷新) | 必须调用 |
5.4 文件实现类 : FileWriter(文件字符输出流)
FileWriter 是 Writer 的子类 , 用于向文本文件写入字符
构造方法 :
java
// 1. 覆盖写入
Writer writer = new FileWriter("D:/test.txt");
// 2. 追加写入
Writer writer = new FileWriter("D:/test.txt", true);
// 3. 传入 File 对象
File file = new File("D:/test.txt");
Writer writer = new FileWriter(file, true);
示例 1 : 写入字符
java
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
public class demo11 {
public static void main(String[] args) throws IOException {
try(Writer writer = new FileWriter("./test.txt",true)){
char[] data = {'*','[',']','{'};
writer.write(data);
}
}
}
6.字节流 vs 字符流:关键差异
|-------|---------------------------------|--------------------------------|
| 对比维度 | 字节流(InputStream/OutputStream) | 字符流(Reader/Writer) |
| 操作单位 | 字节(byte) | 字符(char) |
| 编码依赖 | 无(直接操作原始字节) | 有(自动编码 / 解码,需统一编码) |
| 适用文件 | 所有文件(尤其是二进制文件:图片、视频、压缩包、.class) | 仅文本文件(.txt/.java/.md/.csv 等) |
| 核心方法 | read(byte[])/write(byte[]) | read(char[])/write(String) |
| 缓冲区刷新 | 可选(flush()可省略) | 必须(flush()否则数据可能滞留缓冲区) |