Appearance
IO
Java IO 是 Java 核心库的重要组成部分,用于处理输入输出操作。
IO 流概述
流的分类
- 按方向:输入流(InputStream/Reader)、输出流(OutputStream/Writer)
- 按类型:字节流(处理二进制数据)、字符流(处理文本数据)
- 按功能:节点流(直接操作数据源)、处理流(包装其他流)
流的继承体系
字节输入流: InputStream
├── FileInputStream
├── ByteArrayInputStream
├── BufferedInputStream
├── DataInputStream
└── ObjectInputStream
字节输出流: OutputStream
├── FileOutputStream
├── ByteArrayOutputStream
├── BufferedOutputStream
├── DataOutputStream
└── ObjectOutputStream
字符输入流: Reader
├── FileReader
├── CharArrayReader
├── BufferedReader
└── InputStreamReader
字符输出流: Writer
├── FileWriter
├── CharArrayWriter
├── BufferedWriter
└── OutputStreamWriter字节流
FileInputStream / FileOutputStream
java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
// 写入文件
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
String content = "Hello, Java IO!";
fos.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
// 读取文件
try (FileInputStream fis = new FileInputStream("output.txt")) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
}
// 复制文件
try (FileInputStream fis = new FileInputStream("source.txt");
FileOutputStream fos = new FileOutputStream("dest.txt")) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}ByteArrayInputStream / ByteArrayOutputStream
java
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
// 内存中的字节流
byte[] data = "Hello".getBytes();
// 从字节数组读取
try (ByteArrayInputStream bais = new ByteArrayInputStream(data)) {
int b;
while ((b = bais.read()) != -1) {
System.out.print((char) b);
}
}
// 写入到字节数组
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
baos.write("Hello".getBytes());
baos.write(" World".getBytes());
byte[] result = baos.toByteArray();
System.out.println(new String(result));
}BufferedInputStream / BufferedOutputStream
java
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
// 使用缓冲流提高性能
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("large.txt"));
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream("copy.txt"))) {
byte[] buffer = new byte[8192];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
}DataInputStream / DataOutputStream
java
import java.io.DataInputStream;
import java.io.DataOutputStream;
// 写入基本数据类型
try (DataOutputStream dos = new DataOutputStream(
new FileOutputStream("data.bin"))) {
dos.writeInt(100);
dos.writeDouble(3.14);
dos.writeUTF("Hello");
dos.writeBoolean(true);
}
// 读取基本数据类型
try (DataInputStream dis = new DataInputStream(
new FileInputStream("data.bin"))) {
int i = dis.readInt(); // 100
double d = dis.readDouble(); // 3.14
String s = dis.readUTF(); // "Hello"
boolean b = dis.readBoolean();// true
}字符流
FileReader / FileWriter
java
import java.io.FileReader;
import java.io.FileWriter;
// 写入文件
try (FileWriter fw = new FileWriter("output.txt")) {
fw.write("Hello, Java IO!");
fw.write("\n第二行");
} catch (IOException e) {
e.printStackTrace();
}
// 追加写入
try (FileWriter fw = new FileWriter("output.txt", true)) {
fw.write("\n追加的内容");
}
// 读取文件
try (FileReader fr = new FileReader("output.txt")) {
char[] buffer = new char[1024];
int len;
while ((len = fr.read(buffer)) != -1) {
System.out.print(new String(buffer, 0, len));
}
}BufferedReader / BufferedWriter
java
import java.io.BufferedReader;
import java.io.BufferedWriter;
// 写入文件
try (BufferedWriter bw = new BufferedWriter(
new FileWriter("output.txt"))) {
bw.write("第一行");
bw.newLine(); // 写入换行符
bw.write("第二行");
}
// 读取文件(按行)
try (BufferedReader br = new BufferedReader(
new FileReader("output.txt"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}InputStreamReader / OutputStreamWriter
java
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
// 字节流转字符流(指定编码)
try (BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream("utf8.txt"), StandardCharsets.UTF_8))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
// 写入时指定编码
try (BufferedWriter bw = new BufferedWriter(
new OutputStreamWriter(
new FileOutputStream("gbk.txt"), "GBK"))) {
bw.write("中文内容");
}文件操作
File 类
java
import java.io.File;
// 创建 File 对象
File file = new File("test.txt");
File dir = new File("D:/projects");
File file2 = new File(dir, "test.txt");
// 获取文件信息
String name = file.getName(); // 文件名
String path = file.getPath(); // 路径
String absPath = file.getAbsolutePath();// 绝对路径
String parent = file.getParent(); // 父目录
long length = file.length(); // 文件大小(字节)
long lastModified = file.lastModified();// 最后修改时间
// 判断
boolean exists = file.exists(); // 是否存在
boolean isFile = file.isFile(); // 是否为文件
boolean isDir = file.isDirectory(); // 是否为目录
boolean canRead = file.canRead(); // 是否可读
boolean canWrite = file.canWrite(); // 是否可写
boolean isHidden = file.isHidden(); // 是否隐藏
// 创建
boolean created = file.createNewFile(); // 创建文件
boolean mkdir = dir.mkdir(); // 创建目录(单级)
boolean mkdirs = dir.mkdirs(); // 创建目录(多级)
// 删除
boolean deleted = file.delete(); // 删除文件或空目录
// 重命名
boolean renamed = file.renameTo(new File("newname.txt"));
// 列出目录内容
String[] list = dir.list(); // 文件名数组
File[] files = dir.listFiles(); // File 对象数组
// 过滤器
File[] txtFiles = dir.listFiles((d, name) -> name.endsWith(".txt"));文件遍历
java
import java.io.File;
public class FileTraversal {
public static void main(String[] args) {
File dir = new File("D:/projects");
listFiles(dir, 0);
}
public static void listFiles(File file, int level) {
if (file.exists()) {
String indent = " ".repeat(level);
if (file.isDirectory()) {
System.out.println(indent + "📁 " + file.getName());
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
listFiles(f, level + 1);
}
}
} else {
System.out.println(indent + "📄 " + file.getName() +
" (" + file.length() + " bytes)");
}
}
}
}NIO
Path 和 Files
java
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
// Path 对象
Path path = Paths.get("D:/projects/test.txt");
Path path2 = Path.of("D:/projects", "subdir", "file.txt");
// 路径信息
Path fileName = path.getFileName();
Path parent = path.getParent();
Path root = path.getRoot();
int count = path.getNameCount();
// 路径操作
Path resolved = path.resolve("subdir/file.txt"); // 解析路径
Path normalized = path.normalize(); // 规范化
Path relativized = path.relativize(path2); // 相对路径
// Files 工具类
// 创建
Files.createFile(path);
Files.createDirectory(Paths.get("newdir"));
Files.createDirectories(Paths.get("a/b/c"));
// 复制
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
// 移动
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
// 删除
Files.delete(path);
Files.deleteIfExists(path);
// 读取文件
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
byte[] bytes = Files.readAllBytes(path);
// 写入文件
Files.write(path, "内容".getBytes(StandardCharsets.UTF_8));
Files.write(path, lines, StandardCharsets.UTF_8);
// 追加写入
Files.write(path, "追加内容".getBytes(), StandardOpenOption.APPEND);
// 判断
boolean exists = Files.exists(path);
boolean isRegularFile = Files.isRegularFile(path);
boolean isDirectory = Files.isDirectory(path);
boolean isReadable = Files.isReadable(path);
boolean isWritable = Files.isWritable(path);
// 文件属性
long size = Files.size(path);
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);文件遍历
java
import java.nio.file.*;
import java.io.IOException;
// 使用 Files.walk
try (Stream<Path> stream = Files.walk(Paths.get("D:/projects"))) {
stream.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.forEach(System.out::println);
}
// 使用 Files.list(只列出直接子项)
try (Stream<Path> stream = Files.list(Paths.get("D:/projects"))) {
stream.forEach(System.out::println);
}
// 使用 Files.find
try (Stream<Path> stream = Files.find(
Paths.get("D:/projects"),
10, // 最大深度
(path, attrs) -> attrs.isRegularFile() &&
path.toString().endsWith(".java"))) {
stream.forEach(System.out::println);
}
// 使用 FileVisitor
Files.walkFileTree(Paths.get("D:/projects"), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
System.out.println("访问文件: " + file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
System.out.println("进入目录: " + dir);
return FileVisitResult.CONTINUE;
}
});文件监视
java
import java.nio.file.*;
WatchService watchService = FileSystems.getDefault().newWatchService();
Path dir = Paths.get("D:/projects");
dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path fileName = (Path) event.context();
if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
System.out.println("创建: " + fileName);
} else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
System.out.println("删除: " + fileName);
} else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
System.out.println("修改: " + fileName);
}
}
boolean valid = key.reset();
if (!valid) break;
}对象序列化
序列化与反序列化
java
import java.io.*;
// 实现 Serializable 接口
class Person implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String password; // 不序列化
public Person(String name, int age, String password) {
this.name = name;
this.age = age;
this.password = password;
}
@Override
public String toString() {
return "Person{name='" + name + "', age=" + age + "}";
}
}
// 序列化
Person person = new Person("张三", 25, "123456");
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("person.obj"))) {
oos.writeObject(person);
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("person.obj"))) {
Person p = (Person) ois.readObject();
System.out.println(p); // Person{name='张三', age=25}
}自定义序列化
java
class User implements Serializable {
private String username;
private String password;
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
// 自定义加密
oos.writeObject(Base64.getEncoder().encodeToString(password.getBytes()));
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
// 自定义解密
String encoded = (String) ois.readObject();
password = new String(Base64.getDecoder().decode(encoded));
}
}实践示例
文件复制工具
java
import java.io.*;
import java.nio.file.*;
public class FileCopyUtil {
public static void copyFile(String source, String target) throws IOException {
try (InputStream is = new FileInputStream(source);
OutputStream os = new FileOutputStream(target)) {
byte[] buffer = new byte[8192];
int len;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
}
}
public static void copyFileNIO(String source, String target) throws IOException {
Files.copy(Paths.get(source), Paths.get(target),
StandardCopyOption.REPLACE_EXISTING);
}
public static void copyDirectory(String source, String target) throws IOException {
Path sourcePath = Paths.get(source);
Path targetPath = Paths.get(target);
Files.walk(sourcePath).forEach(sourceFile -> {
Path targetFile = targetPath.resolve(sourcePath.relativize(sourceFile));
try {
if (Files.isDirectory(sourceFile)) {
Files.createDirectories(targetFile);
} else {
Files.copy(sourceFile, targetFile,
StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
public static void main(String[] args) throws IOException {
copyFile("source.txt", "target.txt");
copyDirectory("D:/source", "D:/target");
}
}配置文件读取器
java
import java.io.*;
import java.util.*;
public class ConfigReader {
private Properties properties = new Properties();
public void load(String filePath) throws IOException {
try (FileInputStream fis = new FileInputStream(filePath)) {
properties.load(fis);
}
}
public void loadFromXML(String filePath) throws IOException {
try (FileInputStream fis = new FileInputStream(filePath)) {
properties.loadFromXML(fis);
}
}
public String get(String key) {
return properties.getProperty(key);
}
public String get(String key, String defaultValue) {
return properties.getProperty(key, defaultValue);
}
public void set(String key, String value) {
properties.setProperty(key, value);
}
public void save(String filePath, String comment) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filePath)) {
properties.store(fos, comment);
}
}
public void saveToXML(String filePath, String comment) throws IOException {
try (FileOutputStream fos = new FileOutputStream(filePath)) {
properties.storeToXML(fos, comment);
}
}
public static void main(String[] args) throws IOException {
ConfigReader config = new ConfigReader();
config.load("config.properties");
System.out.println("数据库: " + config.get("db.url"));
System.out.println("用户名: " + config.get("db.username"));
config.set("new.key", "new value");
config.save("config.properties", "Updated configuration");
}
}日志文件分析器
java
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.regex.*;
import java.util.stream.*;
public class LogAnalyzer {
public static void analyzeLogFile(String filePath) throws IOException {
Map<String, Long> levelCounts = new HashMap<>();
Map<String, Long> hourCounts = new HashMap<>();
List<String> errors = new ArrayList<>();
Pattern pattern = Pattern.compile(
"\\[(\\d{4}-\\d{2}-\\d{2} (\\d{2}):\\d{2}:\\d{2})\\] \\[(\\w+)\\] (.+)"
);
try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
String hour = matcher.group(2);
String level = matcher.group(3);
String message = matcher.group(4);
levelCounts.merge(level, 1L, Long::sum);
hourCounts.merge(hour, 1L, Long::sum);
if ("ERROR".equals(level)) {
errors.add(line);
}
}
}
}
System.out.println("=== 日志级别统计 ===");
levelCounts.forEach((k, v) -> System.out.println(k + ": " + v));
System.out.println("\n=== 每小时日志量 ===");
hourCounts.entrySet().stream()
.sorted(Map.Entry.comparingByKey())
.forEach(e -> System.out.println(e.getKey() + "时: " + e.getValue()));
System.out.println("\n=== 错误日志 ===");
errors.forEach(System.out::println);
}
public static void main(String[] args) throws IOException {
analyzeLogFile("application.log");
}
}