Java IO API實(shí)現(xiàn)文件和目錄移動(dòng)、復(fù)制和刪除操作
Java IO API - 移動(dòng)文件或目錄
移動(dòng)文件或目錄是文件系統(tǒng)操作中的常見(jiàn)需求,Java 提供了非常方便的方法來(lái)實(shí)現(xiàn)這一操作:Files.move()。
1. 基本用法
Files.move(sourcePath, targetPath);
這行代碼會(huì)嘗試將 sourcePath 移動(dòng)到 targetPath,但如果目標(biāo)路徑已存在,move() 方法將拋出 FileAlreadyExistsException。
2. 常用選項(xiàng)(CopyOption)
你可以傳遞多個(gè)選項(xiàng)來(lái)定制移動(dòng)操作,這些選項(xiàng)以可變參數(shù)(varargs)的形式傳入:
Files.move(sourcePath, targetPath, REPLACE_EXISTING, ATOMIC_MOVE);
| 枚舉常量 | 含義說(shuō)明 |
|---|---|
REPLACE_EXISTING | 如果目標(biāo)已經(jīng)存在,則替換目標(biāo)文件或目錄。對(duì)于符號(hào)鏈接,會(huì)替換鏈接本身。 |
ATOMIC_MOVE | 確保以原子方式移動(dòng)文件,意味著文件在移動(dòng)過(guò)程中不會(huì)出現(xiàn)部分完成的狀態(tài)。 |
3. 移動(dòng)目錄
- 空目錄可以直接移動(dòng)。
- 非空目錄的移動(dòng)操作會(huì)依賴操作系統(tǒng)的支持。在 UNIX 系統(tǒng)中,如果目錄在同一分區(qū)內(nèi)進(jìn)行移動(dòng),實(shí)際上是通過(guò)重命名來(lái)實(shí)現(xiàn)的,甚至包含子文件的目錄也能被移動(dòng)。
Path sourceDir = Paths.get("/home/user/dir");
Path targetDir = Paths.get("/home/user/dir_backup");
Files.move(sourceDir, targetDir, REPLACE_EXISTING);
示例:使用ATOMIC_MOVE保證原子操作
import java.nio.file.*;
import static java.nio.file.StandardCopyOption.*;
public class FileMoveDemo {
public static void main(String[] args) {
Path source = Paths.get("example.txt");
Path target = Paths.get("backup/example.txt");
try {
Files.move(source, target, REPLACE_EXISTING, ATOMIC_MOVE);
System.out.println("? 文件成功移動(dòng)!");
} catch (IOException e) {
System.err.println("? 文件移動(dòng)失敗:" + e.getMessage());
}
}
}
這里的 ATOMIC_MOVE 保證了文件在移動(dòng)過(guò)程中不會(huì)被其他進(jìn)程看到半成品。如果操作系統(tǒng)或文件系統(tǒng)不支持原子操作,會(huì)拋出異常。
4. 使用 Varargs 傳遞多個(gè)選項(xiàng)
你可以傳入多個(gè) CopyOption 枚舉值來(lái)定制操作。例如:
Files.move(source, target, REPLACE_EXISTING, ATOMIC_MOVE);
這會(huì)執(zhí)行一個(gè)替換目標(biāo)文件且確保原子性的移動(dòng)操作。
5. 錯(cuò)誤處理
如果目標(biāo)路徑已存在并且你沒(méi)有使用 REPLACE_EXISTING,move() 會(huì)拋出 FileAlreadyExistsException。如果操作系統(tǒng)不支持原子操作(尤其是 ATOMIC_MOVE),會(huì)拋出 IOException。
try {
Files.move(sourcePath, targetPath, REPLACE_EXISTING);
} catch (FileAlreadyExistsException e) {
System.err.println("? 目標(biāo)文件已存在,未能移動(dòng)!");
} catch (IOException e) {
System.err.println("? 文件移動(dòng)失敗:" + e.getMessage());
}
6. 原子操作(Atomic Operations)
原子操作意味著整個(gè)操作要么完成,要么完全失敗。對(duì)于多進(jìn)程環(huán)境中的文件操作,原子操作非常關(guān)鍵。舉個(gè)例子,如果你在復(fù)制文件時(shí)發(fā)生了中斷,原子操作可以保證文件要么完全復(fù)制,要么根本不進(jìn)行任何復(fù)制操作,避免部分完成的狀態(tài)。
小結(jié)
| 操作類別 | 方法 |
|---|---|
| 文件/目錄移動(dòng) | Files.move(Path, Path, ...) |
| 移動(dòng)選項(xiàng) | REPLACE_EXISTING, ATOMIC_MOVE |
Java IO API - 復(fù)制文件和目錄
在日常開(kāi)發(fā)中,復(fù)制文件或目錄是非常常見(jiàn)的需求。Java 提供了靈活且強(qiáng)大的 Files.copy() 方法來(lái)處理這些操作。
1. 基本用法
Files.copy(sourcePath, targetPath);
這行代碼會(huì)嘗試將 sourcePath 復(fù)制到 targetPath,但如果目標(biāo)已存在,會(huì)拋出 FileAlreadyExistsException。
2. 常用復(fù)制選項(xiàng)(CopyOption)
你可以使用可變參數(shù)(varargs)形式傳入多個(gè)選項(xiàng):
Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
| 枚舉常量 | 含義說(shuō)明 |
|---|---|
REPLACE_EXISTING | 如果目標(biāo)已存在則替換。對(duì)于符號(hào)鏈接,會(huì)復(fù)制“鏈接本身”,而非其指向目標(biāo)。 |
COPY_ATTRIBUTES | 盡量復(fù)制原文件的屬性(如修改時(shí)間等)。平臺(tái)支持情況可能不同。 |
NOFOLLOW_LINKS | 如果源是符號(hào)鏈接,僅復(fù)制鏈接本身,而不跟蹤其目標(biāo)。 |
3. 注意:目錄復(fù)制不包含內(nèi)容!
雖然你可以復(fù)制一個(gè)目錄:
Files.copy(Paths.get("docs"), Paths.get("docs_copy"));
但是這樣復(fù)制的是空目錄,原始目錄中的子文件和子目錄不會(huì)自動(dòng)復(fù)制。
如果你想復(fù)制整個(gè)目錄結(jié)構(gòu),需要使用遞歸或第三方庫(kù)(如 Apache Commons IO)。
4. 關(guān)于符號(hào)鏈接的行為
默認(rèn)情況下,復(fù)制符號(hào)鏈接會(huì)復(fù)制它所指向的目標(biāo)內(nèi)容:
Files.copy(linkPath, copyPath); // 默認(rèn)會(huì)“追蹤”鏈接并復(fù)制目標(biāo)
但如果你只想復(fù)制“鏈接本身”:
Files.copy(linkPath, copyPath, NOFOLLOW_LINKS);
示例:完整文件復(fù)制流程
import java.nio.file.*;
import java.io.IOException;
import static java.nio.file.StandardCopyOption.*;
public class FileCopyDemo {
public static void main(String[] args) {
Path source = Paths.get("example.txt");
Path target = Paths.get("backup/example.txt");
try {
Files.copy(source, target, REPLACE_EXISTING, COPY_ATTRIBUTES);
System.out.println("? 文件復(fù)制成功!");
} catch (IOException e) {
System.err.println("? 復(fù)制失?。? + e.getMessage());
}
}
}
5. 從流中復(fù)制 / 到流中復(fù)制
除了 Path-to-Path 的復(fù)制,Files 類還支持以下操作:
InputStream → File
try (InputStream in = new FileInputStream("source.txt")) {
Files.copy(in, Paths.get("target.txt"), REPLACE_EXISTING);
}
File → OutputStream
try (OutputStream out = new FileOutputStream("output.txt")) {
Files.copy(Paths.get("source.txt"), out);
}
適用于文件上傳、下載等場(chǎng)景!
常見(jiàn)錯(cuò)誤與建議
| 問(wèn)題場(chǎng)景 | 建議或說(shuō)明 |
|---|---|
| 目標(biāo)文件存在導(dǎo)致異常 | 使用 REPLACE_EXISTING |
| 復(fù)制目錄卻發(fā)現(xiàn)是空的 | 遞歸復(fù)制子文件或使用第三方庫(kù) |
| 權(quán)限不足導(dǎo)致 IOException | 檢查文件讀寫(xiě)權(quán)限 |
| 想保留時(shí)間戳等屬性但沒(méi)生效 | 盡量使用 COPY_ATTRIBUTES,并測(cè)試平臺(tái)兼容性 |
小結(jié)
| 操作類別 | 方法 |
|---|---|
| 文件復(fù)制 | Files.copy(Path, Path, ...) |
| 輸入流 → 文件復(fù)制 | Files.copy(InputStream, Path) |
| 文件 → 輸出流復(fù)制 | Files.copy(Path, OutputStream) |
Java IO API - 刪除文件和目錄
在文件操作中,刪除是一個(gè)非常常見(jiàn)但需要小心處理的操作,尤其在涉及目錄和符號(hào)鏈接時(shí)更是如此。
1. 刪除的基本規(guī)則
- 文件(file):可以直接刪除。
- 目錄(directory):必須為空,否則刪除會(huì)失敗。
- 符號(hào)鏈接(symbolic link):刪除的是鏈接本身,不是它指向的目標(biāo)。
2. 刪除文件的兩種方式
Java 提供了兩種方式來(lái)刪除文件或目錄:
方法一:Files.delete(Path path)
此方法用于強(qiáng)制刪除,如果刪除失敗會(huì)拋出異常,適合你希望清楚了解失敗原因的場(chǎng)景。
示例代碼:
try {
Files.delete(Paths.get("data.txt"));
System.out.println("? 文件刪除成功!");
} catch (NoSuchFileException e) {
System.err.println("? 文件不存在:" + e.getMessage());
} catch (DirectoryNotEmptyException e) {
System.err.println("?? 目錄不為空,無(wú)法刪除:" + e.getMessage());
} catch (IOException e) {
System.err.println("?? 刪除失敗,可能是權(quán)限問(wèn)題:" + e.getMessage());
}
方法二:Files.deleteIfExists(Path path)
此方法刪除文件,但如果文件不存在也不會(huì)拋出異常,返回值為 true 表示刪除成功,false 表示文件本來(lái)就不存在。
示例代碼:
Path path = Paths.get("temp.log");
try {
boolean deleted = Files.deleteIfExists(path);
if (deleted) {
System.out.println("? 文件已刪除");
} else {
System.out.println("?? 文件原本就不存在");
}
} catch (IOException e) {
System.err.println("?? 刪除失?。? + e.getMessage());
}
應(yīng)用場(chǎng)景建議:
| 場(chǎng)景 | 建議方法 |
|---|---|
| 你希望嚴(yán)格處理刪除失敗并報(bào)告原因 | Files.delete(path) |
| 你只是想安靜地嘗試刪除,不管是否存在 | Files.deleteIfExists(path) |
| 多線程環(huán)境,避免競(jìng)爭(zhēng)刪除時(shí)拋異常 | deleteIfExists 更合適 |
補(bǔ)充提示:遞歸刪除目錄
標(biāo)準(zhǔn) API 沒(méi)有提供遞歸刪除目錄的方法,如果你想要刪除非空目錄,需要自己遍歷子文件并逐個(gè)刪除。例如:
void deleteRecursively(Path path) throws IOException {
if (Files.isDirectory(path)) {
try (DirectoryStream<Path> entries = Files.newDirectoryStream(path)) {
for (Path entry : entries) {
deleteRecursively(entry);
}
}
}
Files.delete(path);
}
安全注意事項(xiàng)
- 刪除操作不可恢復(fù),請(qǐng)務(wù)必確認(rèn)路徑。
- 刪除目錄前應(yīng)檢查是否為空,或考慮是否應(yīng)該遞歸刪除。
- 文件可能正在被其他進(jìn)程使用,導(dǎo)致刪除失敗。
- 有些系統(tǒng)目錄(如 Windows 的
C:\Windows)根本不允許刪除,嘗試會(huì)拋異常。
總結(jié)小表
| 方法 | 行為 | 是否拋出異常 |
|---|---|---|
Files.delete(path) | 刪除文件/空目錄 | 是(失敗即拋) |
Files.deleteIfExists() | 刪除,文件不存在也不報(bào)錯(cuò) | 是(但文件不存在不拋) |
| 遞歸刪除目錄 | 自定義實(shí)現(xiàn)遍歷目錄后逐個(gè)刪除 | 是(建議封裝) |
到此這篇關(guān)于Java IO API實(shí)現(xiàn)文件和目錄移動(dòng)、復(fù)制和刪除操作的文章就介紹到這了,更多相關(guān)Java文件和目錄操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA插件之彩虹括號(hào)Rainbow?Brackets使用介紹
這篇文章主要為大家介紹了IDEA插件之彩虹括號(hào)Rainbow?Brackets使用介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
SpringBoot整合阿里云開(kāi)通短信服務(wù)詳解
這篇文章主要介紹了如何利用SpringBoot整合阿里云實(shí)現(xiàn)短信服務(wù)的開(kāi)通,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)有一定幫助,需要的可以參考一下2022-03-03
java開(kāi)發(fā)分布式服務(wù)框架Dubbo原理機(jī)制詳解
這篇文章主要為大家介紹了java開(kāi)發(fā)分布式服務(wù)框架Dubbo的原理機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11
關(guān)于Filter中獲取請(qǐng)求體body后再次讀取的問(wèn)題
這篇文章主要介紹了關(guān)于Filter中獲取請(qǐng)求體body后再次讀取的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03
Spring實(shí)現(xiàn)HikariCP連接池的示例代碼
在SpringBoot 2.0中,我們使用默認(rèn)連接池是HikariCP,本文講一下HikariCP的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下2021-08-08
簡(jiǎn)單講解java中throws與throw的區(qū)別
這篇文章主要介紹了簡(jiǎn)單講解java中throws與throw的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07
一次因Java應(yīng)用造成CPU過(guò)高的排查實(shí)踐過(guò)程
一個(gè)應(yīng)用占用CPU很高,除了確實(shí)是計(jì)算密集型應(yīng)用之外,通常原因都是出現(xiàn)了死循環(huán)。下面這篇文章主要給大家介紹了一次因Java應(yīng)用造成CPU過(guò)高的排查實(shí)踐過(guò)程,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-11-11

