Java I/O
Java I/O 操作
I/O 概述
I/O(Input/Output)即输入/输出,用于程序与外部设备(如文件、磁盘、网络)之间的数据传输。程序中的数据默认存储在内存中,程序终止后会丢失,通过 I/O 操作可将数据永久保存到外部存储设备,或从外部设备读取数据供程序使用。
Java 提供了丰富的 I/O 类库,核心分为两大类:
- 按数据类型:字符流(处理文本数据,如
Reader/Writer)、字节流(处理二进制数据,如InputStream/OutputStream); - 按操作方向:输入流(读取数据)、输出流(写入数据)。
File 类:文件与目录的抽象表示
java.io.File 类封装了文件或目录的路径信息,提供了文件/目录的属性操作(如创建、删除、重命名、查询属性),但不包含读写文件内容的方法。
File 类的构造方法
| 构造方法 | 说明 |
|---|---|
File(String pathname) |
根据指定路径名(文件或目录)创建 File 对象 |
File(String parent, String child) |
根据父目录路径(字符串)和子路径(文件/目录)创建 File 对象 |
File(File parent, String child) |
根据父目录 File 对象和子路径(文件/目录)创建 File 对象 |
File 类核心方法
| 方法名 | 返回值 | 说明 |
|---|---|---|
exists() |
boolean |
判断文件/目录是否存在 |
canRead() |
boolean |
判断文件/目录是否存在且可读 |
canWrite() |
boolean |
判断文件/目录是否存在且可写 |
isDirectory() |
boolean |
判断是否为目录 |
isFile() |
boolean |
判断是否为文件 |
isAbsolute() |
boolean |
判断是否为绝对路径 |
isHidden() |
boolean |
判断文件/目录是否隐藏 |
getAbsolutePath() |
String |
获取绝对路径 |
getCanonicalPath() |
String |
获取标准路径(去除冗余 ./..,盘符大写) |
getName() |
String |
获取文件名/目录名 |
getPath() |
String |
获取完整路径(构造方法传入的路径) |
getParent() |
String |
获取父目录路径 |
lastModified() |
long |
获取最后修改时间(时间戳) |
length() |
long |
获取文件大小(字节),目录返回 0 |
listFiles() |
File[] |
返回目录下所有文件/子目录的 File 数组 |
delete() |
boolean |
删除文件/空目录,成功返回 true |
renameTo(File dest) |
boolean |
重命名文件/目录为目标 File 对象的名称 |
mkdir() |
boolean |
创建目录,父目录不存在则失败 |
mkdirs() |
boolean |
创建目录,父目录不存在则一并创建(支持层级创建) |
createNewFile() |
boolean |
创建文件(需处理 IOException) |
File 类使用示例
假设存在目录 E:\Home_java 及文件 E:\Home_java\123.txt、E:\Home_java\Welcome.java,示例代码如下:
package com.exception_io;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
/**
* @author Jing61
*/
public class FileDemo {
public static void main(String[] args) {
var file = new File("E:/Home_java/Welcome.java");
var dir = new File("E:/Home_java");
if(file.exists()) {
System.out.println("文件已存在");
System.out.println("文件名:" + file.getName());
System.out.println("文件大小:" + file.length());
System.out.println("文件是否可读:" + file.canRead());
System.out.println("文件是否可写:" + file.canWrite());
System.out.println("文件是否隐藏:" + file.isHidden());
System.out.println("文件是否时目录:" + file.isDirectory());
System.out.println("文件是否是按绝对路径创建:" + file.isAbsolute());
System.out.println("文件创建时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(file.lastModified()));
System.out.println("文件父目录:" + file.getParent());
System.out.println("文件父目录是否是目录:" + file.getParentFile().isDirectory());
System.out.println("文件绝对路径:" + file.getAbsolutePath());
System.out.println("文件是否可执行:" + file.canExecute());
} else {
System.out.println("文件不存在");
}
if(dir.exists()) {
System.out.println("目录已存在");
System.out.println("目录名:" + dir.getName());
System.out.println("目录是否可读:" + dir.canRead());
System.out.println("目录是否可写:" + dir.canWrite());
System.out.println("目录是否隐藏:" + dir.isHidden());
System.out.println("目录是否是目录:" + dir.isDirectory());
System.out.println("目录是否是按绝对路径创建:" + dir.isAbsolute());
System.out.println("目录创建时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(dir.lastModified()));
System.out.println("目录父目录:" + dir.getParent());
System.out.println("目录父目录是否是目录:" + dir.getParentFile().isDirectory());
System.out.println("目录绝对路径:" + dir.getAbsolutePath());
System.out.println("目录是否可执行:" + dir.canExecute());
System.out.print("目录下的文件:");
for(File f : dir.listFiles()) {
System.out.print(f.getName() + " ");
}
System.out.println();
//删除文件
System.out.println(new File(dir, "Welcome.java").delete());
// 创建目录,父目录不存在时,创建失败
var f = new File(dir, "456");
f.mkdir();
// 层级创建目录,当父目录也不存在时,会将父目录创建
var f2 = new File("E:/Home_java/Welcome/Welcome/cc");
f2.mkdirs();
// 创建文件
var f3 = new File(dir, "Welcome.java");
try {
f3.createNewFile();
System.out.println("文件创建成功");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
Files 工具类(NIO)
java.nio.file.Files 是 Java NIO 提供的文件操作工具类,封装了更便捷的文件/目录操作方法,支持路径 Path 类型,功能比 File 类更强大。
Files 类核心功能
- 检查文件/目录是否存在及属性;
- 创建、删除、移动、复制文件/目录;
- 读写文件内容;
- 遍历目录;
- 获取文件属性。
Files 类使用示例
package com.exception_io;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
/**
* @author Jing61
*/
public class FilesDemo {
public static void main(String[] args) throws IOException {
/*
* 基本文件操作
* 1. 检查 文件/目录 是否存在,以及属性操作
* 2. 创建文件/目录
* 3. 删除文件/目录 Files.delete(path) / Files.deleteIfExists(path) / Files.deleteOnExit(path)
* 4. 读写文件
* 5. 移动文件/目录
* 6. 获取文件属性
* 7. 遍历目录 Files.list
* 8. 复制文件/目录
* 等等
*/
// 1.
var path1 = Paths.get("E:/Home_java/Welcome.java");
System.out.println("文件是否存在:" + Files.exists(path1));
System.out.println("文件是否不存在:" + Files.notExists(path1));
System.out.println("文件是否是文件:" + Files.isRegularFile(path1));
System.out.println("文件是否是目录:" + Files.isDirectory(path1));
System.out.println("文件是否可读:" + Files.isReadable(path1));
System.out.println("文件是否可写:" + Files.isWritable(path1));
System.out.println("文件是否可执行:" + Files.isExecutable(path1));
System.out.println("文件是否隐藏:" + Files.isHidden(path1));
System.out.println("文件大小:" + Files.size(path1));
// 2.
var path2 = Paths.get("E:/Home_java/Peppa.java");
if(!Files.exists(path2)) {
Files.createFile(path2);
}
var path3 = Paths.get("E:/Home_java/new");
if(Files.notExists(path3)) {
Files.createDirectory(path3);
}
var path4 = Paths.get("E:/Home_java/many_new/resume");
if(Files.notExists(path4)) {
Files.createDirectories(path4); // 创建层级目录
}
// 3.查看API即可
// 4.
// 写
Files.write(Paths.get("text.txt"), "Hello, World".getBytes(StandardCharsets.UTF_8));
List<String> con = List.of("1", "2", "3");
Files.write(Paths.get("text.txt"), con); // 会覆盖之前的文件内容
//读
List<String> lines = Files.readAllLines(Paths.get("text.txt"), StandardCharsets.UTF_8);// 读取所有行, 返回List
lines.forEach(System.out::println);
}
}
字符流:写入文本数据
字符流以字符(char)为单位处理数据,适用于文本文件(如 .txt、.java),核心抽象类为 Writer,常用实现类有FileWriter、PrintWriter等。
Writer 抽象类核心方法
write(String content):写入字符串;write(char[] cbuf):写入字符数组;flush():刷新流,将缓冲区数据写入目标(流未关闭时需手动调用);close():关闭流,释放资源(会自动刷新缓冲区)。
FileWriter :基础文本写入
FileWriter 是 Writer 的子类,直接关联文件,用于写入文本数据。注意:Writer 是资源对象,不会被垃圾回收,需手动关闭或使用 try-with-resources 自动关闭。
示例 1:手动关闭流(finally 块)
package com.exception_io;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
/**
* @author Jing61
*/
public class WriterDemo {
public static void write(String content, File file) {
Writer writer = null;
try {
writer = new FileWriter(file); // 关联目标文件
writer.write(content); // 数据写入缓冲区(未实际写入文件)
// writer.flush(); // 手动刷新(close() 会自动刷新,可省略)
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
// 确保流一定关闭,避免资源泄露
if(writer != null) {
try {
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
public static void main(String[] args) {
var file = new File("E:/Home_java/123.txt");
// 多行字符串(Java 15+ 支持)
String content = """
peppa
suzy
emily
jorge
""";
write(content, file);
System.out.println("写入成功");
}
}
示例 2:try-with-resources 自动关闭流(推荐)
try-with-resources 语法可自动关闭实现 AutoCloseable 接口的资源,简化代码且避免遗漏关闭操作:
package com.exception_io;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
/**
* @author Jing61
*/
public class WriterDemo {
// 推荐:使用 try-with-resources 自动关闭流
public static void write2(String content, File file) {
try (Writer writer = new FileWriter(file)) { // 资源声明在 try 括号内
writer.write(content); // 无需手动 flush 和 close
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
var file = new File("E:/Home_java/123.txt");
String content = """
peppa
suzy
emily
jorge
""";
write2(content, file);
System.out.println("写入成功");
}
}
PrintWriter :便捷打印写入
PrintWriter 提供了更灵活的文本写入方式,支持 println()(自动换行)、print()(不换行)等方法,可直接创建文件并写入。
示例:
package com.exception_io;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author Jing61
*/
public class WriterDemo {
/**
* PrintWriter 便捷打印输出(支持自动换行、多类型数据)
* @param file 目标文件
*/
public static void write3(File file) {
try (PrintWriter writer = new PrintWriter(file)) {
// 写入字符串(自动换行)
writer.println("Emily 5 Female");
writer.println("Peppa 5 Female");
// 拼接写入(不自动换行)
writer.print("Pedro ");
writer.print(6); // 写入数字
writer.println(" Male"); // 换行
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
var file = new File("E:/Home_java/123.txt");
write3(file);
System.out.println("写入成功");
}
}
字符流:读取文本数据
字符流读取文本数据的核心抽象类为 Reader,常用实现类有 FileReader,结合 Scanner 可实现更灵活的读取。
Reader 抽象类核心方法
read():读取单个字符,返回字符的 ASCII 码(int 类型),读取到末尾返回-1;read(char[] cbuf):读取字符到数组,返回实际读取的字符数,末尾返回-1;close():关闭流,释放资源。
FileReader :基础文本读取
FileReader 是 Reader 的子类,直接关联文件,用于读取文本数据。
示例 1:逐字符读取
package com.exception_io;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/**
* @author Jing61
*/
public class ReaderDemo {
public static void main(String[] args) {
var file = new File("E:/Home_java/123.txt");
try (var reader = new FileReader(file)) {
int ch; // 存储读取的字符 ASCII 码
// 逐字符读取,直到返回 -1(末尾)
while((ch = reader.read()) != -1) { // 读到后,会自动后移,调用一次就会后移一次
System.out.print((char)ch); // 转换为字符并打印
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
示例 2:缓冲区读取(优化性能)
逐字符读取效率较低,可通过字符数组作为缓冲区批量读取:
package com.exception_io;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
/**
* @author Jing61
*/
public class ReaderDemo {
public static void main(String[] args) {
var file = new File("E:/Home_java/123.txt");
try (var reader = new FileReader(file)) {
int len; // 实际读取的字符数
char[] buffer = new char[5]; // 缓冲区大小(5 个字符)
// 批量读取字符到缓冲区,返回实际读取的字符数
while((len = reader.read(buffer)) != -1) {
// 从缓冲区读取有效字符(0 到 len-1)
System.out.print(new String(buffer, 0, len));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Scanner :灵活读取文本
java.util.Scanner 类可简化文本读取,支持按分隔符读取、逐行读取、读取基本数据类型等。
示例:
package com.exception_io;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
/**
* @author Jing61
*/
public class ReaderDemo {
public static void input(String filename) {
var file = new File(filename);
try(var input = new Scanner(file)) { // Scanner 实现 AutoCloseable,支持 try-with-resources
// 方式 1:按默认分隔符(空白字符)读取单词
/*while(input.hasNext()) {
System.out.println("单词:\n" + input.next());
}*/
// 方式 2:逐行读取
/*while(input.hasNextLine()) {
System.out.println("行内容:\n" + input.nextLine());
}*/
// 方式 3:自定义分隔符(逗号)
input.useDelimiter(",");
while(input.hasNext()) {
System.out.println("按逗号分隔的内容:\n" + input.next());
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
public static void main(String[] args) {
String filename = "E:/Home_java/123.txt";
input(filename);
}
}
综合示例:复制文本文件
结合字符流的读取和写入,实现文本文件复制(使用 Scanner 读取、PrintStream 写入):
package com.exception_io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.util.Scanner;
/**
* 复制文本文件:将 source 内容复制到 target
* @author Jing61
*/
public class CopyFile {
public static void main(String[] args) {
var source = new File("E:/Home_java/123.txt"); // 源文件
var target = new File("E:/Home_java/456.txt"); // 目标文件
try (var input = new Scanner(source); var output = new PrintStream(target)) {
// 逐行读取源文件并写入目标文件
while(input.hasNextLine()) {
output.println(input.nextLine());
}
System.out.println("文件复制成功");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
字节流:处理二进制数据
字节流以字节(byte)为单位处理数据,适用于二进制文件(如图片、视频、音频、.class 文件),核心抽象类为 InputStream(输入)和 OutputStream(输出)。
字节流与字符流的区别
| 特性 | 字节流 | 字符流 |
|---|---|---|
| 处理单位 | 字节(1 byte = 8 bit) | 字符(1 char = 2 byte,Unicode 编码) |
| 适用场景 | 二进制文件(图片、视频、音频) | 文本文件(.txt、.java、.csv) |
| 核心类 | InputStream/OutputStream | Reader/Writer |
字节流核心方法(以 OutputStream 为例)
write(byte[] b):写入字节数组;write(byte[] b, int off, int len):写入字节数组的一部分(从 off 索引开始,共 len 个字节);flush():刷新缓冲区;close():关闭流,释放资源。
字节流编码相关方法
writeChar(char c):写入字符的 Unicode 编码(2 字节);writeChars(String s):写入字符串中每个字符的 Unicode 低字节(丢弃高字节,适用于 ASCII 字符);writeUTF(String s):将字符串转化成 UTF-8 格式的一串字节,然后将它们写入到输出流;readUTF():读取writeUTF()写入的字符串(保持编码一致性)。
UTF-8 编码说明
UTF-8 是一种可变长度编码,兼容 ASCII 码:
- ASCII 字符(编码 ≤ 0x7F):1 字节存储;
- 中等字符(0x7F < 编码 ≤ 0x7FF):2 字节存储;
- 复杂字符(编码 ≥ 0x7FF):3 字节存储。
适合存储包含大量 ASCII 字符的文本,比 Unicode 编码更节省空间。

浙公网安备 33010602011771号