一文詳解如何使用IO流實現(xiàn)文件數(shù)據(jù)的讀寫及文件復(fù)制
一. 什么是IO流?
IO流本質(zhì)上是 Java 中用于處理設(shè)備間數(shù)據(jù)傳輸的 API(不止限于文件,還包括網(wǎng)絡(luò)、內(nèi)存、鍵盤等),可以實現(xiàn)對文件數(shù)據(jù)的讀寫,區(qū)別于File類只能操作文件本身
- 輸入流(Input):數(shù)據(jù)從外部設(shè)備(如文件、網(wǎng)絡(luò))進入程序(內(nèi)存);
- 輸出流(Output):數(shù)據(jù)從程序(內(nèi)存)發(fā)送到外部設(shè)備。File 類僅能操作文件的 “元數(shù)據(jù)”(如創(chuàng)建、刪除、判斷存在性),而 IO 流負責(zé)文件內(nèi)容的讀寫,這是兩者的核心區(qū)別。
二. IO流的分類
1. 字節(jié)IO流:
以字節(jié)(8bit)為單位讀寫數(shù)據(jù),可處理所有類型文件(文本、圖片、視頻等)
輸入:FileInputStream() 對應(yīng)的緩沖流: BufferedInputStream()
輸出:FileOutputStream() 對應(yīng)的緩沖流: BufferedOutputStream()
2.字符IO流:
以字符(16bit,Java 中char)為單位讀寫數(shù)據(jù),僅適合處理文本文件
輸入:FileReader() 對應(yīng)的緩沖流: BufferedReader()
輸出:FileWriter() 對應(yīng)的緩沖流: BufferedWriter()
注意:為什么要記對應(yīng)的緩沖流?
緩沖流(BufferedXXX)通過內(nèi)置8KB 緩沖區(qū)(字節(jié)緩沖流)或字符緩沖區(qū)(字符緩沖流),減少直接與磁盤的 IO 次數(shù)(磁盤 IO 效率遠低于內(nèi)存操作),從而提升性能。
- 例如:讀取文件時,緩沖流會一次性從磁盤讀取 8KB 數(shù)據(jù)到緩沖區(qū),程序從緩沖區(qū)獲取數(shù)據(jù);寫入時先攢滿緩沖區(qū)再一次性寫入磁盤。
使用規(guī)范
處理流需 “包裹” 節(jié)點流,關(guān)閉時只需關(guān)閉外層處理流(會自動關(guān)閉內(nèi)層節(jié)點流):
// 示例:緩沖流包裹節(jié)點流
try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
String line;
while ((line = br.readLine()) != null) {
// 讀取數(shù)據(jù)
}
} catch (IOException e) {
e.printStackTrace();
}其他重要補充
流的繼承關(guān)系:
- 所有字節(jié)輸入流繼承
InputStream,字節(jié)輸出流繼承OutputStream; - 所有字符輸入流繼承
Reader,字符輸出流繼承Writer(這四個是抽象基類,不能直接實例化)。
- 所有字節(jié)輸入流繼承
編碼問題:字符流涉及編碼轉(zhuǎn)換,
FileReader/FileWriter默認使用系統(tǒng)編碼(可能導(dǎo)致亂碼),建議用InputStreamReader/OutputStreamWriter指定編碼:// 指定UTF-8編碼讀取文本 Reader reader = new InputStreamReader(new FileInputStream("test.txt"), "UTF-8");JDK7 + 的 try-with-resources:流實現(xiàn)了
AutoCloseable接口,可在 try 后自動關(guān)閉,無需手動調(diào)用close(),推薦優(yōu)先使用(如上述緩沖流示例)。
思維導(dǎo)圖

練習(xí)一: 統(tǒng)計單一文件目錄大小
package com.itheima.homework;
import java.io.File;
/**
* @author Administrator
**需求:**
假設(shè)在`D:\itheima\`目錄下有若干個文件**(只有文件沒有目錄)**,請編寫程序統(tǒng)計`D:\itheima`目錄的大小。
**提示:**
1. 如果沒有`D:\itheima`目錄,就在任意盤下創(chuàng)建一個`itheima`目錄
2. 統(tǒng)計目錄的大小就是統(tǒng)計目錄中所有文件的大小之和
*/
public class Work1 {
public static void main(String[] args) {
File file = new File("C:\\Users\\Administrator\\IDEA\\java-upgrade\\day06-file-recursion-io\\src\\com\\itheima\\d4_io\\d1_byteio");
File[] files = file.listFiles();
long length = 0;
for (File file1 : files) {
length+=file1.length();
}
System.out.println("總大小為"+length);
}
}練習(xí)2:統(tǒng)計目錄若干文件大小
package com.itheima.homework;
import java.io.File;
/**
* @author Administrator
**需求:
假設(shè)在`D:\itheima\`目錄下有若干個文件和目錄,請編寫程序統(tǒng)計`D:\itheima`目錄的大小。
**提示:**統(tǒng)計目錄的大小就是統(tǒng)計目錄(及其子目錄)中所有文件的大小之和
*/
public class Work2 {
public static void main(String[] args) {
File file = new File("day06-file-recursion-io");
System.out.println("總大小為"+fileSizeCount(file));
}
public static long fileSizeCount(File dir){
File[] files = dir.listFiles();
long length = 0;
for (File file1 : files) {
if (file1.isFile()) {
long len = file1.length();
System.out.println(file1.getName()+"-->"+len);
length+=file1.length();
}else{
// return fileSizeCount(file1); 不能return直接否定了外層循環(huán)
// fileSizeCount(file1);
//todo 沒有累加非同級目錄的文件大小,結(jié)果為909,只計算了1級目錄的文件大小
length +=fileSizeCount(file1);
//遞歸就應(yīng)該累加文件大小,就跟階乘類似
System.out.println("子級目錄長度:"+ length);
}
}
return length;
}
}練習(xí)3: 復(fù)制單個文件到目錄中
package com.itheima.homework;
import java.io.*;
/**
* @author Administrator
* **需求:**
假設(shè)在`D:\itheima\`目錄下有若干個文件**(只有文件沒有目錄)**,請編寫程序?qū)D:\itheima`目錄中的**一個文件**復(fù)制到當前模塊下的`itheima`目錄中,文件名不變。
* **要求:**不使用commons-io框架
*/
public class Work3 {
public static void main(String[] args) throws IOException {
copyFile();
}
public static void copyFile() {
//復(fù)制文件的思路:字節(jié)流符合視頻\圖片\音頻\文本,更普遍使用
try(
FileInputStream fis = new FileInputStream(new File("day06-file-recursion-io\\src\\com\\itheima\\d1_file\\Demo1.java"));
FileOutputStream fos = new FileOutputStream("day06-file-recursion-io\\src\\com\\itheima\\homework\\Demo1.java");
) {
byte[] bytes = new byte[1024];
int lenth = 0;
while((lenth=fis.read(bytes))!=-1){
fos.write(bytes,0,lenth);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}練習(xí)4:將某一目錄下所有文件復(fù)制到別的目錄下
package com.itheima.homework;
import java.io.*;
/**
* @author Administrator
* 假設(shè)在`D:\itheima\`目錄下有若干個文件**(只有文件沒有目錄)**,請編寫程序?qū)D:\itheima`目錄中的**所有文件**復(fù)制到當前模塊下的`itheima`目錄中。
*/
/*todo Error: FileInputStream 只能用于讀取文件,不能讀取目錄
* 遍歷目錄中的每個文件
為每個源文件創(chuàng)建獨立的 FileInputStream
為目標文件創(chuàng)建 FileOutputStream
*
* 輸出目錄拒絕訪問的原因是:
FileOutputStream 不能直接寫入目錄
FileOutputStream 只能用于創(chuàng)建或?qū)懭胛募荒苤苯訉懭肽夸?
代碼中 new FileOutputStream(outputFile) 試圖將數(shù)據(jù)寫入目錄 work4,這是不允許的
目標路徑應(yīng)該是文件而不是目錄
每個源文件都需要對應(yīng)一個目標文件
當前代碼試圖將所有文件內(nèi)容都寫入同一個目錄路徑*
*使用 while((len = fis.read(bytes)) != -1) 循環(huán)讀取
每次讀取后立即寫入目標文件
直到 read() 返回 -1(文件結(jié)束)才停止
這樣就能完整復(fù)制整個文件內(nèi)容,而不是只復(fù)制前1024字節(jié)。*/
public class Work4 {
public static void main(String[] args) throws IOException {
File inputFile = new File("day06-file-recursion-io\\src\\com\\itheima\\d4_io\\d1_byteio");
File outputFile = new File("day06-file-recursion-io\\src\\com\\itheima\\homework\\work4");
copyFiles(inputFile,outputFile);
}
public static void copyFiles(File dir,File outputFile) throws IOException {
//創(chuàng)建字節(jié)輸入輸出流
//這里的都是目錄
//讀取輸入流目錄的子級
File[] files = dir.listFiles();
for (File file : files) {
//遍歷父級目錄下的文件是否是文件
if (file.isFile()) {
try ( FileInputStream fis = new FileInputStream(file);
//todo 此時字節(jié)輸入流已經(jīng)拿到了文件
FileOutputStream fos = new FileOutputStream(new File(outputFile, file.getName()));
//todo 為輸出字節(jié)流的目標文件指定具體文件名,父目錄+子文件名
){
byte[] bytes = new byte[1024];
int len = 0;
while ((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}else{
copyFiles(file, new File(outputFile, file.getName()));
}
}
//讀入和輸出
//關(guān)閉流
}
}練習(xí)5:符緩沖流讀取數(shù)據(jù)并封裝對象
package com.itheima.homework;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author Administrator
* - 使用字符緩沖流讀取”students.txt”文件,將每行數(shù)據(jù)封裝為一個Student對象,并將Student對象存儲到一個集合
*
* - 遍歷并打印集合的所有Student信息
*/
public class Work5 {
public static void main(String[] args) throws IOException {
Reader file = new FileReader("day06-file-recursion-io\\student.txt");
BufferedReader br = new BufferedReader(file);
//讀取行
String line = null;
List<Student> studentArrayList = new ArrayList<>();
while((line=br.readLine())!=null){
String s = new String(line);
String[] split = s.split(",");
// System.out.println(Arrays.toString( split));
Student student = new Student(split[0], Integer.parseInt(split[1]));
studentArrayList.add(student);
/*//todo 而不是“每行都重復(fù) 3 次”,根本原因是:
//你把 new ArrayList<>() 寫在了 while 循環(huán)里
//→ 每讀一行就 new 一個新的空集合 → 只裝當前這一個 Student → 打印完就棄用 → 下一行再 new 一個新的空集合……*/
}
br.close();
System.out.println(studentArrayList);
}
}總結(jié)
到此這篇關(guān)于如何使用IO流實現(xiàn)文件數(shù)據(jù)的讀寫及文件復(fù)制的文章就介紹到這了,更多相關(guān)IO流實現(xiàn)文件數(shù)據(jù)讀寫及文件復(fù)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring學(xué)習(xí)通過AspectJ注解方式實現(xiàn)AOP操作
這篇文章主要為大家介紹了Spring學(xué)習(xí)通過AspectJ注解方式實現(xiàn)AOP操作,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-05-05
Java?中的?getDeclaredFields()使用與原理解析
在Java反射機制中,getDeclaredFields()用于獲取類的所有字段,包括私有字段,通過反射,可以在運行時動態(tài)地獲取類的信息并操作其成員,本文詳細介紹了getDeclaredFields()的使用方法、工作原理以及最佳實踐,涵蓋了反射的基本概念、使用場景和注意事項,感興趣的朋友一起看看吧2025-01-01
Elasticsearch?Analyzer?內(nèi)置分詞器使用示例詳解
這篇文章主要為大家介紹了Elasticsearch?Analyzer?內(nèi)置分詞器使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11

