JavaEE初階教程之Java?IO流讀寫與文件操作實(shí)戰(zhàn)

前言:
對(duì)于剛接觸 Java“文件操作”和“IO” 的小伙伴來(lái)說(shuō),“文件操作”和“IO 流”總像兩座小山峰——聽著有點(diǎn)難,實(shí)則只要找對(duì)路徑,一步一步就能輕松登頂。今天這篇文章,就帶著大家從基礎(chǔ)概念到實(shí)戰(zhàn)代碼,把 Java 文件操作和 IO 流徹底搞明白。
一、先搞懂:文件和文件系統(tǒng)的基礎(chǔ)認(rèn)知
在寫代碼之前,我們得先明白“文件”到底是什么。狹義上的文件,是硬盤這種持久化存儲(chǔ)設(shè)備中獨(dú)立的數(shù)據(jù)單位,就像辦公桌上一份份單獨(dú)的文檔,不僅有文字內(nèi)容,還有文件名、類型、大小這些“附加信息”——我們把這些附加信息叫做“文件的元信息”。

比如你在電腦上看到的“PSGet.Format.ps1xml”文件,它的元信息就包括“修改日期 2019/3/19”“類型 Windows PowerShell 數(shù)據(jù)文件”“大小 9KB”,這些信息和文件內(nèi)容是分開保存的。

而隨著文件越來(lái)越多,系統(tǒng)就用“樹形結(jié)構(gòu)”來(lái)管理它們——這就是我們熟悉的“文件夾(folder)或者目錄(directory)”。比如 Windows 里的“此電腦→Windows(C:)→Program Files(X86)”,Linux 里的“/usr/bin”,都是通過(guò)層級(jí)目錄把文件組織起來(lái),既方便查找,邏輯上也更清晰。

另外,定位文件必須用到“路徑”,這里分了兩種:
- 絕對(duì)路徑:從根目錄開始的完整路徑,比如
C:\Program Files (x86)\WindowsPowerShell,不管當(dāng)前在哪里,都能通過(guò)它找到文件;

- 相對(duì)路徑:從當(dāng)前目錄出發(fā)的路徑,比如從“WindowsPowerShell”目錄去“Windows NT”,用
..\Windows NT就行(..代表父目錄,.代表當(dāng)前目錄)。

拓展:即使是普通文件,根據(jù)其保存數(shù)據(jù)的不同,也經(jīng)常被分為不同的類型,我們一般簡(jiǎn)單的劃分為:
文本文件:保存被字符集編碼的文本。
二進(jìn)制文件:按照標(biāo)準(zhǔn)格式保存的非被字符集編碼過(guò)的文件。
在Windows操作系統(tǒng)上,還有一類文件比較特殊,就是平時(shí)我們看到的快捷方式(shortcut),這種文件只是對(duì)真實(shí)文件的一種引用而已。其他操作系統(tǒng)上也有類似的概念,例如軟鏈接(soft link)等。

最后,很多操作系統(tǒng)為了實(shí)現(xiàn)接口的統(tǒng)一性,將所有的 I/O 設(shè)備都抽象成了文件的概念,使用這一理念最為知名的就是 Unix、Linux 操作系統(tǒng)——萬(wàn)物皆文件。這種抽象設(shè)計(jì)能讓操作系統(tǒng)對(duì)不同I/O設(shè)備(如硬盤、鍵盤、打印機(jī)等)的操作,都統(tǒng)一到文件操作的接口上,簡(jiǎn)化了開發(fā)和使用邏輯,無(wú)需為不同設(shè)備單獨(dú)設(shè)計(jì)一套操作方式。
二、Java 中操作文件的“核心工具”:File 類
Java 用 java.io.File 類來(lái)抽象描述一個(gè)文件(包括目錄),但要注意:創(chuàng)建了 File 對(duì)象,不代表真實(shí)存在這個(gè)文件,它只是對(duì)文件的“描述”而已。
1. File 類的關(guān)鍵屬性、構(gòu)造和方法
屬性:
| 修飾符及類型 | 屬性 | 說(shuō)明 |
|---|---|---|
| static String | pathSeparator | 依賴于系統(tǒng)的路徑分隔符,String類型的表示 |
| static char | pathSeparator | 依賴于系統(tǒng)的路徑分隔符,char類型的表示 |
構(gòu)造方法:
| 簽名 | 說(shuō)明 |
|---|---|
| File(File parent, String child) | 根據(jù)父目錄 + 孩子文件路徑,創(chuàng)建一個(gè)新的 File 實(shí)例 |
| File(String pathname) | 根據(jù)文件路徑創(chuàng)建一個(gè)新的 File 實(shí)例,路徑可以是絕對(duì)路徑或者相對(duì)路徑 |
| File(String parent, String child) | 根據(jù)父目錄 + 孩子文件路徑,創(chuàng)建一個(gè)新的 File 實(shí)例,父目錄用路徑表示 |
File類常用方法:
| 修飾符及返回值類型 | 方法簽名 | 說(shuō)明 |
|---|---|---|
| String | getParent() | 返回 File 對(duì)象的父目錄文件路徑 |
| String | getName() | 返回 File 對(duì)象的純文件名稱 |
| String | getPath() | 返回 File 對(duì)象的文件路徑 |
| String | getAbsolutePath() | 返回 File 對(duì)象的絕對(duì)路徑 |
| String | getCanonicalPath() | 返回 File 對(duì)象的修飾過(guò)的絕對(duì)路徑 |
| boolean | exists() | 判斷 File 對(duì)象描述的文件是否真實(shí)存在 |
| boolean | isDirectory() | 判斷 File 對(duì)象代表的文件是否是一個(gè)目錄 |
| boolean | isFile() | 判斷 File 對(duì)象代表的文件是否是一個(gè)普通文件 |
| boolean | createNewFile() | 根據(jù) File 對(duì)象,自動(dòng)創(chuàng)建一個(gè)空文件。成功創(chuàng)建后返回 true |
| boolean | delete() | 根據(jù) File 對(duì)象,刪除該文件。成功刪除后返回 true |
| void | deleteOnExit() | 根據(jù) File 對(duì)象,標(biāo)注文件將被刪除,刪除動(dòng)作會(huì)到 JVM 運(yùn)行結(jié)束時(shí)才會(huì)進(jìn)行 |
| String[] | list() | 返回 File 對(duì)象代表的目錄下的所有文件名 |
| File[] | listFiles() | 返回 File 對(duì)象代表的目錄下的所有文件,以 File 對(duì)象表示 |
| boolean | mkdir() | 創(chuàng)建 File 對(duì)象代表的目錄 |
| boolean | mkdirs() | 創(chuàng)建 File 對(duì)象代表的目錄,如果必要,會(huì)創(chuàng)建中間目錄 |
| boolean | renameTo(File dest) | 進(jìn)行文件改名,也可以視為我們平時(shí)的剪切、粘貼操作 |
| boolean | canRead() | 判斷用戶是否對(duì)文件有可讀權(quán)限 |
| boolean | canWrite() | 判斷用戶是否對(duì)文件有可寫權(quán)限 |
2. File 類實(shí)操:從獲取信息到創(chuàng)建刪除
(1)搞懂 get 系列方法:獲取文件信息
比如想知道文件的父目錄、名稱、路徑,用這幾個(gè)方法就行,:
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
// 這里的文件不一定真實(shí)存在
File file = new File("..\\hello-world.txt");
System.out.println(file.getParent()); // 輸出父目錄:..
System.out.println(file.getName()); // 輸出文件名:hello-world.txt
System.out.println(file.getPath()); // 輸出路徑:..\hello-world.txt
System.out.println(file.getAbsolutePath()); // 輸出絕對(duì)路徑:D:\代碼練習(xí)\文件示例1\..\hello-world.txt
System.out.println(file.getCanonicalPath()); // 輸出簡(jiǎn)化絕對(duì)路徑:D:\代碼練習(xí)\hello-world.txt
}
}

(2)創(chuàng)建與刪除文件:createNewFile() 和 delete()
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
File file = new File("hello-world.txt"); // 確保文件初始不存在
System.out.println(file.exists()); // 初始不存在:false
System.out.println(file.createNewFile());// 創(chuàng)建成功:true
System.out.println(file.exists()); // 創(chuàng)建后存在:true
System.out.println(file.isFile()); // 是普通文件:true
System.out.println(file.delete()); // 刪除成功:true
System.out.println(file.exists()); // 刪除后不存在:false
}
}

這里要注意:createNewFile() 只能創(chuàng)建普通文件,不能創(chuàng)建目錄;delete() 直接刪除文件,不會(huì)進(jìn)回收站,操作要謹(jǐn)慎。
(3)創(chuàng)建目錄:mkdir() 和 mkdirs() 的區(qū)別
新手最容易踩的坑就是這兩個(gè)方法的區(qū)別!
mkdir():只能創(chuàng)建單層目錄,如果父目錄不存在,創(chuàng)建失?。?/li>mkdirs():能創(chuàng)建多層目錄(包括不存在的父目錄)。
比如要?jiǎng)?chuàng)建“some-parent\some-dir”這個(gè)多層目錄,用 mkdir() 會(huì)失敗,用 mkdirs() 才能成功:
package IO;
import java.io.File;
public class demo3 {
public static void main(String[] args) {
File dir=new File("some-parent\\some-dir");
System.out.println(dir.mkdir());
System.out.println(dir.isDirectory());
System.out.println(dir.mkdirs());
System.out.println(dir.isDirectory());
}
}

這個(gè)區(qū)別一定要記牢,不然創(chuàng)建多層目錄時(shí)會(huì)卡很久。
(4)文件重命名:renameTo()
import java.io.File;
import java.io.IOException;
public class Main {
public static void main(String[] args) throws IOException {
File oldFile = new File("some-file.txt"); // 確保該文件存在
File newFile = new File("dest.txt"); // 確保該文件不存在
System.out.println(oldFile.exists());
System.out.println(newFile.exists());
System.out.println(oldFile.renameTo(newFile)); // 重命名成功:true
System.out.println(oldFile.exists()); // 原文件不存在:false
System.out.println(newFile.exists()); // 新文件存在:true
}
}


注意:如果目標(biāo)文件(dest.txt)已經(jīng)存在,renameTo() 會(huì)返回 false,所以先判斷目標(biāo)文件是否存在很重要。
三、Java IO 流:文件內(nèi)容讀寫的核心
搞懂了文件操作,接下來(lái)就是“讀寫文件內(nèi)容”——這就需要 IO 流了。可以把 IO 流比喻得很形象:讀文件像“接水”(輸入流 InputStream),寫文件像“灌水”(輸出流 OutputStream),我們就順著這個(gè)邏輯來(lái)學(xué)。

1. 字節(jié)流:InputStream 和 OutputStream
字節(jié)流是最基礎(chǔ)的 IO 流,以“字節(jié)”為單位讀寫數(shù)據(jù),適合所有文件(比如文本、圖片、視頻)。
(1)InputStream:讀文件內(nèi)容
InputStream 是抽象類,我們常用它的子類 FileInputStream 來(lái)讀文件。它的核心方法是 read(),有三種用法:
read():讀1個(gè)字節(jié),返回字節(jié)值(-1 表示讀完);read(byte[] b):讀多個(gè)字節(jié)到數(shù)組 b 中,返回實(shí)際讀的字節(jié)數(shù);read(byte[] b, int off, int len):從 off 位置開始,讀 len 個(gè)字節(jié)到數(shù)組 b 中。close():關(guān)閉字節(jié)流。
FileInputStream類構(gòu)造方法
| 簽名 | 說(shuō)明 |
|---|---|
| FileInputStream(File file) | 利用 File 構(gòu)造文件輸入流 |
| FileInputStream(String name) | 利用文件路徑構(gòu)造文件輸入流 |
強(qiáng)調(diào):用數(shù)組讀比單個(gè)字節(jié)讀效率高,因?yàn)闇p少了 IO 次數(shù)。比如讀“hello.txt”里的“Hello”:
單個(gè)字節(jié)讀:
package IO;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class demo5 {
//需要在項(xiàng)目目錄下創(chuàng)建hello.txt文件
public static void main(String[] args) throws IOException {
try(InputStream is=new FileInputStream("hello.txt")){
while(true){
int b=is.read();
if(b==-1){
break;
}
System.out.printf("%c",b);
}
}
}
}
數(shù)組讀:
package IO;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class demo6 {
public static void main(String[] args) throws IOException {
try(InputStream is=new FileInputStream("hello.txt")){
byte[] buf=new byte[1024];
int len;
while(true){
len=is.read(buf);
if(len==-1){
break;
}
for(int i=0;i<len;i++){
System.out.printf("%c",buf[i]);
}
}
}
}
}
運(yùn)行后會(huì)輸出“Hello”,這里用了 try-with-resources 語(yǔ)法,能自動(dòng)關(guān)閉流,避免資源泄漏,新手一定要養(yǎng)成這個(gè)習(xí)慣。
如果讀中文文件(比如“你好中國(guó)”),要注意編碼,用 UTF-8 解碼,因?yàn)?UTF-8 中一個(gè)中文字符占 3 個(gè)字節(jié):
package IO;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class demo7 {
public static void main(String[] args) throws IOException {
try(InputStream is=new FileInputStream("hello.txt")){
byte[] buf=new byte[1024];
int len;
while (true){
len=is.read(buf);
if(len==-1){
break;
}
for(int i=0;i<len;i+=3){
String s=new String(buf,i,3,"UTF-8");
System.out.printf("%s",s);
}
}
}
}
}

我們看到了對(duì)字符類型直接使用 InputStream 進(jìn)行讀取是非常麻煩且困難的,所以,我們使用一種我們之前比較熟悉的類來(lái)完成該工作,就是Scanner 類。
| 構(gòu)造方法 | 說(shuō)明 |
|---|---|
| Scanner(InputStream is, String charset) | 使用 charset 字符集進(jìn)行 is 的掃描讀取 |
package IO;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Scanner;
public class demo8 {
public static void main(String[] args) throws IOException {
try(InputStream is=new FileInputStream("hello.txt")){
try(Scanner sc=new Scanner(System.in)){
while(sc.hasNext()){
String s=sc.next();
System.out.print(s);
}
}
}
}
}
(2)OutputStream:寫文件內(nèi)容
OutputStream 也是抽象類,常用子類 FileOutputStream 寫文件。核心方法是 write(),同樣有三種用法,還有一個(gè)關(guān)鍵方法 flush()——因?yàn)?OutputStream 有緩沖區(qū),數(shù)據(jù)會(huì)先存在內(nèi)存,必須調(diào)用 flush() 才能把數(shù)據(jù)刷到硬盤。
比如寫字符串“你好中國(guó)”到“output.txt”:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
try (OutputStream os = new FileOutputStream("output.txt")) {
String s = "你好中國(guó)";
byte[] b = s.getBytes("UTF-8"); // 轉(zhuǎn)成 UTF-8 字節(jié)數(shù)組
os.write(b); // 寫入字節(jié)數(shù)組
os.flush(); // 必須刷新,否則數(shù)據(jù)可能留在緩沖區(qū)
}
}
}
運(yùn)行后打開“output.txt”,就能看到“你好中國(guó)”。如果想追加內(nèi)容,把 FileOutputStream 構(gòu)造方法改成 new FileOutputStream("output.txt", true) 即可(第二個(gè)參數(shù) true 表示追加)。

2. 字符流:更方便的文本讀寫
字節(jié)流讀中文需要處理編碼,很麻煩,這時(shí)候就需要“字符流”——按“字符”為單位讀寫,自動(dòng)處理編碼問(wèn)題。用 Scanner 讀字符,用 PrintWriter 寫字符。
(1)用 Scanner 讀文本文件
Scanner 能按行讀文本,還能指定編碼(比如 UTF-8),避免亂碼。比如讀“hello.txt”里的“你好中國(guó)”:
import java.io.*;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws IOException {
try (InputStream is = new FileInputStream("hello.txt")) {
// 指定 UTF-8 編碼,避免中文亂碼
try (Scanner scanner = new Scanner(is, "UTF-8")) {
while (scanner.hasNextLine()) { // 按行讀
String line = scanner.nextLine();
System.out.println(line); // 輸出:你好中國(guó)
}
}
}
}
}

這種方式比字節(jié)流簡(jiǎn)單多了,新手讀文本文件優(yōu)先用這個(gè)。
(2)用 PrintWriter 寫文本文件
PrintWriter 有我們熟悉的 print()、println()、printf() 方法,寫文本很方便,還能指定編碼。
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
try (OutputStream os = new FileOutputStream("output.txt")) {
// 先轉(zhuǎn)成 OutputStreamWriter,指定 UTF-8 編碼
try (OutputStreamWriter osWriter = new OutputStreamWriter(os, "UTF-8")) {
// 用 PrintWriter 寫內(nèi)容
try (PrintWriter writer = new PrintWriter(osWriter)) {
writer.println("我是第一行"); // 換行
writer.print("我是第二行"); // 不換行
writer.printf("%d: 我是第三行\(zhòng)n", 3); // 格式化輸出
writer.flush(); // 刷新到硬盤
}
}
}
}
}

運(yùn)行后“output.txt”里會(huì)有三行內(nèi)容,格式清晰,比直接用 OutputStream 方便太多。
四、實(shí)戰(zhàn)案例:把知識(shí)點(diǎn)串起來(lái)用
學(xué)完基礎(chǔ),必須通過(guò)實(shí)戰(zhàn)鞏固。通過(guò)一些經(jīng)典案例,我們逐個(gè)拆解,新手跟著寫一遍就能掌握。
案例 1:掃描目錄,找到指定文件并刪除
需求:輸入根目錄和關(guān)鍵詞,找到文件名包含關(guān)鍵詞的普通文件,詢問(wèn)用戶是否刪除。
核心思路:用“遞歸”遍歷樹形目錄(因?yàn)槲募到y(tǒng)是樹形結(jié)構(gòu)),找到符合條件的文件后處理。
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
// 輸入根目錄
System.out.print("請(qǐng)輸入要掃描的根目錄(絕對(duì)路徑/相對(duì)路徑):");
String rootDirPath = scanner.next();
File rootDir = new File(rootDirPath);
if (!rootDir.isDirectory()) {
System.out.println("根目錄不存在或不是目錄,退出!");
return;
}
// 輸入關(guān)鍵詞
System.out.print("請(qǐng)輸入文件名包含的字符:");
String token = scanner.next();
List<File> result = new ArrayList<>();
// 遞歸掃描目錄
scanDir(rootDir, token, result);
// 處理結(jié)果
System.out.println("共找到 " + result.size() + " 個(gè)符合條件的文件:");
for (File file : result) {
System.out.print(file.getCanonicalPath() + ",是否刪除?(y/n)");
String choice = scanner.next();
if (choice.toLowerCase().equals("y")) {
file.delete();
System.out.println("已刪除!");
}
}
}
// 遞歸掃描目錄的方法
private static void scanDir(File rootDir, String token, List<File> result) {
File[] files = rootDir.listFiles();
if (files == null || files.length == 0) {
return; // 目錄為空,返回
}
for (File file : files) {
if (file.isDirectory()) {
scanDir(file, token, result); // 是目錄,遞歸掃描
} else {
// 是普通文件,判斷文件名是否包含關(guān)鍵詞
if (file.getName().contains(token)) {
result.add(file.getAbsoluteFile());
}
}
}
}
}


這個(gè)案例用到了 File 類的 isDirectory()、listFiles(),還有遞歸遍歷,新手要理解遞歸的邏輯——“自己調(diào)用自己,處理子目錄”。
案例 2:文件復(fù)制工具
需求:輸入源文件路徑和目標(biāo)路徑,實(shí)現(xiàn)文件復(fù)制(支持所有文件類型,比如文本、圖片)。
核心思路:用 InputStream 讀源文件,用 OutputStream 寫目標(biāo)文件,用字節(jié)數(shù)組做緩沖區(qū)提高效率。
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
// 輸入源文件
System.out.print("請(qǐng)輸入要復(fù)制的文件路徑:");
String sourcePath = scanner.next();
File sourceFile = new File(sourcePath);
if (!sourceFile.exists()) {
System.out.println("源文件不存在!");
return;
}
if (!sourceFile.isFile()) {
System.out.println("不是普通文件,無(wú)法復(fù)制!");
return;
}
// 輸入目標(biāo)路徑
System.out.print("請(qǐng)輸入目標(biāo)路徑:");
String destPath = scanner.next();
File destFile = new File(destPath);
// 處理目標(biāo)文件已存在的情況
if (destFile.exists()) {
if (destFile.isDirectory()) {
System.out.println("目標(biāo)是目錄,無(wú)法覆蓋!");
return;
}
System.out.print("目標(biāo)文件已存在,是否覆蓋?(y/n)");
String choice = scanner.next();
if (!choice.toLowerCase().equals("y")) {
System.out.println("停止復(fù)制!");
return;
}
}
// 開始復(fù)制:讀源文件,寫目標(biāo)文件
try (InputStream is = new FileInputStream(sourceFile);
OutputStream os = new FileOutputStream(destFile)) {
byte[] buf = new byte[1024]; // 1KB 緩沖區(qū)
int len;
while ((len = is.read(buf)) != -1) {
os.write(buf, 0, len); // 寫讀到的字節(jié)
}
os.flush(); // 刷新緩沖區(qū)
}
System.out.println("復(fù)制完成!");
}
}

執(zhí)行完成后ouput文件復(fù)制了hello文件里面的內(nèi)容。

這個(gè)案例是 IO 流的經(jīng)典應(yīng)用,不管復(fù)制什么文件(文本、圖片、視頻)都能用,因?yàn)樽止?jié)流不區(qū)分文件類型。
案例 3:掃描目錄,找到內(nèi)容包含關(guān)鍵詞的文件
需求:輸入根目錄和關(guān)鍵詞,找到文件名或內(nèi)容包含關(guān)鍵詞的普通文件。
核心思路:在案例 1 的基礎(chǔ)上,增加“讀文件內(nèi)容判斷”的邏輯,用 Scanner 讀文件內(nèi)容,判斷是否包含關(guān)鍵詞。
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
System.out.print("請(qǐng)輸入要掃描的根目錄:");
String rootDirPath = scanner.next();
File rootDir = new File(rootDirPath);
if (!rootDir.isDirectory()) {
System.out.println("根目錄無(wú)效,退出!");
return;
}
System.out.print("請(qǐng)輸入要查找的關(guān)鍵詞:");
String token = scanner.next();
List<File> result = new ArrayList<>();
scanDirWithContent(rootDir, token, result);
System.out.println("共找到 " + result.size() + " 個(gè)文件:");
for (File file : result) {
System.out.println(file.getCanonicalPath());
}
}
// 遞歸掃描目錄,判斷文件名或內(nèi)容
private static void scanDirWithContent(File rootDir, String token, List<File> result) throws IOException {
File[] files = rootDir.listFiles();
if (files == null || files.length == 0) {
return;
}
for (File file : files) {
if (file.isDirectory()) {
scanDirWithContent(file, token, result);
} else {
// 文件名包含,或內(nèi)容包含,都加入結(jié)果
if (file.getName().contains(token) || isContentContains(file, token)) {
result.add(file);
}
}
}
}
// 判斷文件內(nèi)容是否包含關(guān)鍵詞(按 UTF-8 處理)
private static boolean isContentContains(File file, String token) throws IOException {
StringBuilder sb = new StringBuilder();
try (InputStream is = new FileInputStream(file);
Scanner scanner = new Scanner(is, "UTF-8")) {
while (scanner.hasNextLine()) {
sb.append(scanner.nextLine()).append("\n"); // 讀所有行
}
}
return sb.indexOf(token) != -1; // 判斷是否包含關(guān)鍵詞
}
}

這個(gè)案例增加了 isContentContains() 方法,讀文件內(nèi)容并判斷,適合查找文本文件中的關(guān)鍵詞(注意:大文件會(huì)影響性能,文檔里也提到了這一點(diǎn))。
五、新手避坑總結(jié)
看到這里,你已經(jīng)掌握了 Java 文件操作和 IO 流的核心內(nèi)容,最后再總結(jié)幾個(gè)新手常踩的坑,幫你少走彎路:
- File 類不代表真實(shí)文件:創(chuàng)建 File 對(duì)象只是“描述”文件,不代表文件存在,必須調(diào)用
createNewFile()或mkdirs()才會(huì)真實(shí)創(chuàng)建; - mkdir() 和 mkdirs() 別用混:創(chuàng)建多層目錄一定要用
mkdirs(); - IO 流必須關(guān)閉:用
try-with-resources語(yǔ)法,自動(dòng)關(guān)閉流,避免資源泄漏; - OutputStream 要 flush():寫完數(shù)據(jù)必須調(diào)用
flush(),否則數(shù)據(jù)可能留在緩沖區(qū),沒(méi)寫到硬盤; - 讀中文要指定編碼:用 Scanner 或 OutputStreamWriter 時(shí),明確指定 UTF-8,避免亂碼。
至此,Java 文件操作和 IO 流的核心知識(shí)和實(shí)戰(zhàn)就講完了。其實(shí)這些內(nèi)容并不難,關(guān)鍵是多寫代碼、多跑案例——把文中的代碼逐個(gè)復(fù)制到 IDE 里運(yùn)行,改改參數(shù)(比如換個(gè)文件路徑、關(guān)鍵詞),很快就能熟練掌握。告別小白,從搞定文件操作和 IO 流開始吧!
到此這篇關(guān)于JavaEE初階教程之Java IO流讀寫與文件操作實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)Java IO流讀寫與文件操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring中的@RestController注解詳細(xì)解析
這篇文章主要介紹了Spring中的@RestController注解詳細(xì)解析,@RestController 是 Spring Framework 中的一個(gè)注解,用于標(biāo)識(shí)一個(gè)類為 RESTful Web 服務(wù)的控制器(Controller),處理 HTTP 請(qǐng)求并返回相應(yīng)的數(shù)據(jù),2024-01-01
JVM的垃圾回收機(jī)制詳解和調(diào)優(yōu)
JVM的垃圾回收機(jī)制詳解和調(diào)優(yōu)...2006-12-12
SpringBoot整合SSE接口實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送
SSEServer-Sent?Events)是一種基于HTTP的服務(wù)器向客戶端單向?qū)崟r(shí)推送數(shù)據(jù)的技術(shù),本文主要介紹了SpringBoot整合SSE接口實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送的方法,需要的可以參考下2025-05-05
詳解Spring Boot 項(xiàng)目部署到heroku爬坑
這篇文章主要介紹了詳解Spring Boot 項(xiàng)目部署到heroku爬坑,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
SpringBoot啟動(dòng)多數(shù)據(jù)源找不到合適的驅(qū)動(dòng)類問(wèn)題
這篇文章主要介紹了SpringBoot啟動(dòng)多數(shù)據(jù)源找不到合適的驅(qū)動(dòng)類問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01
springboot使用@Slf4j進(jìn)行日志的記錄步驟詳解
這篇文章主要介紹了springboot使用@Slf4j進(jìn)行日志的記錄,使用@Slf4j的注解進(jìn)行日志記錄非常方便,本文給大家分享操作步驟,需要的朋友可以參考下2023-08-08
MybatisPlus自動(dòng)填充創(chuàng)建(更新)時(shí)間問(wèn)題
在開發(fā)數(shù)據(jù)庫(kù)相關(guān)應(yīng)用時(shí),手動(dòng)設(shè)置創(chuàng)建和更新時(shí)間會(huì)導(dǎo)致代碼冗余,MybatisPlus提供了自動(dòng)填充功能,通過(guò)實(shí)現(xiàn)MetaObjectHandler接口并重寫insertFill、updateFill方法,可以自動(dòng)維護(hù)創(chuàng)建時(shí)間、更新時(shí)間等字段,極大簡(jiǎn)化了代碼,這不僅提高了開發(fā)效率,也保證了數(shù)據(jù)的可追溯性2024-09-09
SpringMvc框架的簡(jiǎn)介與執(zhí)行流程詳解
MVC是一種軟件設(shè)計(jì)典范,用一種業(yè)務(wù)邏輯、數(shù)據(jù)、界面顯示分離的方法組織代碼,將業(yè)務(wù)邏輯聚集到一個(gè)組件里面,在改進(jìn)和個(gè)性化定制界面及用戶交互的同時(shí),不需要重新編寫業(yè)務(wù)邏輯,MVC分層有助于管理和架構(gòu)復(fù)雜的應(yīng)用程序2021-06-06

