版本基线
| 类别 | 版本 |
|---|---|
| Java | 17 (LTS) |
| C# | 12 (.NET 8) |
1、Java 文件系统操作对照
1.1 类型对比
| 维度 | Java | C# |
|---|---|---|
| 文件路径 | java.nio.file.Path |
System.IO.Path(静态类) |
| 文件操作 | java.nio.file.Files |
System.IO.File(静态类) |
| 目录操作 | java.nio.file.DirectoryStream |
System.IO.Directory(静态类) |
| 文件监听 | WatchService |
FileSystemWatcher |
| 路径拼接 | Paths.get("a", "b", "c.txt") |
Path.Combine("a", "b", "c.txt") |
| 文件存在 | Files.exists(path) |
File.Exists(path) |
| 创建目录 | Files.createDirectories(path) |
Directory.CreateDirectory(path) |
| 复制文件 | Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING) |
File.Copy(src, dst, overwrite: true)(.NET Core 3.0+ / .NET 5+) |
| 移动文件 | Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING) |
File.Move(src, dst, overwrite: true)(.NET Core 3.0+ / .NET 5+) |
| 删除文件 | Files.delete(path) |
File.Delete(path) |
| 读取字节 | Files.readAllBytes(path) |
File.ReadAllBytes(path) |
| 写入字节 | Files.write(path, bytes) |
File.WriteAllBytes(path, bytes) |
| 读取行 | Files.readAllLines(path, StandardCharsets.UTF_8) |
File.ReadAllLines(path) / File.ReadLines(path)(流式) |
| 写入行 | Files.write(path, lines) |
File.WriteAllLines(path, lines) |
| 遍历目录 | Files.list(path) / Files.walk(path) |
Directory.EnumerateFiles(path) |
| 文件属性 | Files.getLastModifiedTime(path) |
File.GetLastWriteTime(path) |
| 文件大小 | Files.size(path) |
new FileInfo(path).Length |
| 临时文件 | Files.createTempFile("prefix", ".tmp") / Files.createTempDirectory(prefix) |
Path.GetTempFileName() / Path.Combine(Path.GetTempPath(), ...) |
1.2 数据流图
#mermaid-svg-9uTVCydxwpMmsVoH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9uTVCydxwpMmsVoH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9uTVCydxwpMmsVoH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9uTVCydxwpMmsVoH .error-icon{fill:#552222;}#mermaid-svg-9uTVCydxwpMmsVoH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9uTVCydxwpMmsVoH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9uTVCydxwpMmsVoH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9uTVCydxwpMmsVoH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9uTVCydxwpMmsVoH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9uTVCydxwpMmsVoH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9uTVCydxwpMmsVoH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9uTVCydxwpMmsVoH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9uTVCydxwpMmsVoH .marker.cross{stroke:#333333;}#mermaid-svg-9uTVCydxwpMmsVoH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9uTVCydxwpMmsVoH p{margin:0;}#mermaid-svg-9uTVCydxwpMmsVoH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9uTVCydxwpMmsVoH .cluster-label text{fill:#333;}#mermaid-svg-9uTVCydxwpMmsVoH .cluster-label span{color:#333;}#mermaid-svg-9uTVCydxwpMmsVoH .cluster-label span p{background-color:transparent;}#mermaid-svg-9uTVCydxwpMmsVoH .label text,#mermaid-svg-9uTVCydxwpMmsVoH span{fill:#333;color:#333;}#mermaid-svg-9uTVCydxwpMmsVoH .node rect,#mermaid-svg-9uTVCydxwpMmsVoH .node circle,#mermaid-svg-9uTVCydxwpMmsVoH .node ellipse,#mermaid-svg-9uTVCydxwpMmsVoH .node polygon,#mermaid-svg-9uTVCydxwpMmsVoH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9uTVCydxwpMmsVoH .rough-node .label text,#mermaid-svg-9uTVCydxwpMmsVoH .node .label text,#mermaid-svg-9uTVCydxwpMmsVoH .image-shape .label,#mermaid-svg-9uTVCydxwpMmsVoH .icon-shape .label{text-anchor:middle;}#mermaid-svg-9uTVCydxwpMmsVoH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9uTVCydxwpMmsVoH .rough-node .label,#mermaid-svg-9uTVCydxwpMmsVoH .node .label,#mermaid-svg-9uTVCydxwpMmsVoH .image-shape .label,#mermaid-svg-9uTVCydxwpMmsVoH .icon-shape .label{text-align:center;}#mermaid-svg-9uTVCydxwpMmsVoH .node.clickable{cursor:pointer;}#mermaid-svg-9uTVCydxwpMmsVoH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9uTVCydxwpMmsVoH .arrowheadPath{fill:#333333;}#mermaid-svg-9uTVCydxwpMmsVoH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9uTVCydxwpMmsVoH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9uTVCydxwpMmsVoH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9uTVCydxwpMmsVoH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9uTVCydxwpMmsVoH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9uTVCydxwpMmsVoH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9uTVCydxwpMmsVoH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9uTVCydxwpMmsVoH .cluster text{fill:#333;}#mermaid-svg-9uTVCydxwpMmsVoH .cluster span{color:#333;}#mermaid-svg-9uTVCydxwpMmsVoH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9uTVCydxwpMmsVoH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9uTVCydxwpMmsVoH rect.text{fill:none;stroke-width:0;}#mermaid-svg-9uTVCydxwpMmsVoH .icon-shape,#mermaid-svg-9uTVCydxwpMmsVoH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9uTVCydxwpMmsVoH .icon-shape p,#mermaid-svg-9uTVCydxwpMmsVoH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9uTVCydxwpMmsVoH .icon-shape .label rect,#mermaid-svg-9uTVCydxwpMmsVoH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9uTVCydxwpMmsVoH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9uTVCydxwpMmsVoH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9uTVCydxwpMmsVoH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}#mermaid-svg-9uTVCydxwpMmsVoH .scene>*{fill:#fef3c7!important;stroke:#d97706!important;}#mermaid-svg-9uTVCydxwpMmsVoH .scene span{fill:#fef3c7!important;stroke:#d97706!important;} Path 接口
代表路径
Files 工具类
静态方法
DirectoryStream
目录迭代
Read 读
Write 写
Delete 删
List 列
Create 建
WatchService
事件队列
park 场景
1.3 代码对照
java
// Java NIO.2 文件系统
package com.aspnet2java.demo.lang;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FileService {
private static final Path BASE_DIR = Paths.get("/data/parking");
// 1. 创建目录
public void createParkDir(Long parkId) throws IOException {
Path parkDir = BASE_DIR.resolve(String.valueOf(parkId));
Files.createDirectories(parkDir);
}
// 2. 写文件
public void writeFile(Long parkId, String filename, String content) throws IOException {
Path file = BASE_DIR.resolve(String.valueOf(parkId)).resolve(filename);
// 关键:用 TRUNCATE_EXISTING 截断旧文件,否则二次写入会变成追加。
// REPLACE_EXISTING / TRUNCATE_EXISTING 与 ATOMIC_MOVE 不可同用(后者只在 Files.move 里)。
Files.writeString(file, content,
StandardOpenOption.CREATE,
StandardOpenOption.TRUNCATE_EXISTING);
}
// 3. 读文件
public String readFile(Long parkId, String filename) throws IOException {
Path file = BASE_DIR.resolve(String.valueOf(parkId)).resolve(filename);
return Files.readString(file);
}
// 4. 遍历目录
public List<Path> listFiles(Long parkId) throws IOException {
Path parkDir = BASE_DIR.resolve(String.valueOf(parkId));
try (Stream<Path> stream = Files.list(parkDir)) {
return stream.filter(Files::isRegularFile).collect(Collectors.toList());
}
}
// 5. 文件监听(注意:WatchService 不递归,子目录需单独 register;
// 必须处理 OVERFLOW 事件,否则高频写入会丢事件)
public void watchParkDir(Long parkId) throws IOException, InterruptedException {
Path parkDir = BASE_DIR.resolve(String.valueOf(parkId));
try (WatchService watchService = FileSystems.getDefault().newWatchService()) {
parkDir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
while (!Thread.currentThread().isInterrupted()) {
WatchKey key = watchService.take();
for (WatchEvent<?> event : key.pollEvents()) {
if (event.kind() == StandardWatchEventKinds.OVERFLOW) {
System.err.println("WatchService overflow, events lost");
continue;
}
System.out.println("Event: " + event.kind() + " - " + event.context());
}
if (!key.reset()) break;
}
}
}
// 6. 临时文件
public Path createTempFile() throws IOException {
return Files.createTempFile("iot-", ".tmp");
}
}
csharp
// C# System.IO 文件系统
using System;
using System.Collections.Generic;
using System.IO;
namespace AspNet2Java.Demo.Lang {
public class FileService {
private static readonly string BaseDir = @"D:\data\parking";
// 1. 创建目录
public void CreateParkDir(long parkId) {
var parkDir = Path.Combine(BaseDir, parkId.ToString());
Directory.CreateDirectory(parkDir); // 自动递归创建
}
// 2. 写文件(默认 UTF-8 无 BOM;.NET Framework 上需 Encoding.UTF8)
public void WriteFile(long parkId, string filename, string content) {
var file = Path.Combine(BaseDir, parkId.ToString(), filename);
File.WriteAllText(file, content, System.Text.Encoding.UTF8); // 显式 UTF-8
}
// 3. 读文件
public string ReadFile(long parkId, string filename) {
var file = Path.Combine(BaseDir, parkId.ToString(), filename);
return File.ReadAllText(file);
}
// 4. 遍历目录
public IEnumerable<string> ListFiles(long parkId) {
var parkDir = Path.Combine(BaseDir, parkId.ToString());
return Directory.EnumerateFiles(parkDir); // 流式
}
// 5. 文件监听(生产建议:using 释放 + InternalBufferSize 调大 + 处理 Renamed + Error)
public void WatchParkDir(long parkId) {
var parkDir = Path.Combine(BaseDir, parkId.ToString());
using var watcher = new FileSystemWatcher(parkDir) {
InternalBufferSize = 65536, // 默认 8192,停车场 OTA 高频写入会丢
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Size
};
watcher.Created += (s, e) => Console.WriteLine($"Created: {e.Name}");
watcher.Changed += (s, e) => Console.WriteLine($"Changed: {e.Name}");
watcher.Deleted += (s, e) => Console.WriteLine($"Deleted: {e.Name}");
watcher.Renamed += (s, e) => Console.WriteLine($"Renamed: {e.OldName} -> {e.Name}");
watcher.Error += (s, e) => Console.Error.WriteLine($"Watcher error: {e.GetException().Message}");
watcher.EnableRaisingEvents = true;
}
// 6. 临时文件
public string CreateTempFile() {
return Path.GetTempFileName();
}
}
}
重点 :
.NET
System.IO全部是静态类 (File/Directory/Path),调用更简洁。Java
java.nio.file.Files/Paths是工具类(也静态)。路径处理 :Java
Path是逻辑路径,按FileSystem.getDefault().getSeparator()处理,跨平台一致;.NETPath.Combine在 .NET Core+ 上同样按平台分隔符拼接,Windows 上输出\、Linux/macOS 输出/。文件监听 :.NET
FileSystemWatcher与 JavaWatchService都依赖底层文件系统通知机制,实际差异更多来自平台与事件缓冲配置,而非"事件回调 vs 轮询"这种简单二分;.NET 8 上FileSystemWatcher用 inotify(Linux)/ ReadDirectoryChangesW(Windows)/ FSEvents(macOS),均有内部缓冲区(默认 8KB),高频写入会丢事件,生产建议显式设置InternalBufferSize = 65536并处理Error事件。
1.4 FAQ
Q1:Files 与 File 区别?
Java 旧版
java.io.File(Java 1.0)只表示路径,操作能力有限 ;新版本java.nio.file.Files(Java 7+,NIO.2)提供完整静态方法。生产推荐 Files (File 仅作为遗留兼容)。.NETFile/Directory全部是静态类,对应 Files 工具类。
Q2:Path 与 Paths 区别?Java
Path是接口 (代表路径),Paths是工厂类 (Paths.get()创建 Path 实例)。C#Path是静态类(提供 Combine / GetFileName 等静态方法)。
Q3:WatchService vs FileSystemWatcher?Java
WatchService通过事件队列通知文件变化,take()会阻塞等待事件;C#FileSystemWatcher通过事件回调通知变化。两者都依赖操作系统或运行时的文件系统通知机制,实际可靠性和性能取决于平台、监听范围、事件数量以及缓冲区配置。跨平台场景下都需要处理事件重复、事件丢失和文件尚未写完等问题。