柚子快報(bào)激活碼778899分享:【Java】I/O 操作詳解
柚子快報(bào)激活碼778899分享:【Java】I/O 操作詳解
??個(gè)人主頁(yè):island1314
?? ?歡迎關(guān)注:?點(diǎn)贊 ??留言 ?收藏 ?? ? ?
目錄
1. 引言 ?
2. File?類 ?
2.1 創(chuàng)建 File?對(duì)象
2.2 File 類的常用方法
2.3 遍歷目錄下的文件
2.4 刪除文件及目錄
3. 字節(jié)流 ?
3.1 基本概念
3.2 字節(jié)流讀文件
3.3 字節(jié)流寫文件
3.4 字節(jié)流復(fù)制文件
4. 字符流 ?
4.1 字符流定義及基本用法
4.2 字符流讀文件
4.3 字符流寫文件
4.4 數(shù)據(jù)編碼解碼問(wèn)題
5. 轉(zhuǎn)換流 ?
6. 緩沖流 ?
6.1 字節(jié)緩沖流
6.2 字符緩沖流
7. 序列化反序列化 ?
8. 小結(jié) ?
1. 引言 ?
?I/O 操作主要是指 使用 Java 程序完成輸入(Input)、輸出(Output) 操作。輸入是指將文件內(nèi)容以數(shù)據(jù)流的形式讀入內(nèi)存,輸出是指通過(guò) Java 程序?qū)?nèi)容中的數(shù)據(jù)寫入文件,輸入輸出操作在實(shí)際開(kāi)發(fā)中比較廣泛。
IO:輸入/輸出(Input/Output)流:是一種抽象概念,是對(duì)數(shù)據(jù)傳輸?shù)目偡Q.也就是說(shuō)數(shù)據(jù)在設(shè)備間的傳輸稱為流,流的本質(zhì)是數(shù)據(jù)傳輸IO流就是用來(lái)處理設(shè)備間數(shù)據(jù)傳輸問(wèn)題的.常見(jiàn)的應(yīng)用: 文件復(fù)制; 文件上傳; 文件下載
IO流的分類:
(1)按照數(shù)據(jù)的流向
輸入流:讀數(shù)據(jù)輸出流:寫數(shù)據(jù)
(2)按照數(shù)據(jù)類型來(lái)分:
字節(jié)流
字節(jié)輸入流字節(jié)輸出流字符流
字符輸入流字符輸出流
IO流的使用場(chǎng)景
如果操作的是純文本文件,優(yōu)先使用字符流如果操作的是圖片、視頻、音頻等二進(jìn)制文件,優(yōu)先使用字節(jié)流如果不確定文件類型,優(yōu)先使用字節(jié)流,字節(jié)流是萬(wàn)能的流
2. File?類 ?
java.io 包中的?File 類?是唯一一個(gè)可以代表磁盤文件的對(duì)象,它定義了一些用于操作文件的方法。通過(guò)調(diào)用?File 類?提供的各種方法,可以創(chuàng)建、刪除或者重命名文件,判斷硬盤上某個(gè)文件是否存在,查詢文件最后修改時(shí)間,等等。本節(jié)將針對(duì)File 類?進(jìn)行詳細(xì)講解。創(chuàng)建File對(duì)象
2.1 創(chuàng)建 File?對(duì)象
File 類?提供了多個(gè)構(gòu)造方法用于創(chuàng)建?File?對(duì)象。File 類?的常用構(gòu)造方法如下所示:
方法聲明功能描述FILE(String pathname)通過(guò)指定的 一個(gè)字符串類型的文件路徑?創(chuàng)建一個(gè) FILE 對(duì)象FILE(String parent, String child)根據(jù)指定的?一個(gè)字符串類型的父路徑和一個(gè)字符串類型的子路徑(包括文件名稱)創(chuàng)建一個(gè) FILE 對(duì)象FILE (FILE parent, String child)根據(jù)指定的?一個(gè)FILE 類的父路徑和一個(gè)字符串類型的子路徑(包括文件名稱)創(chuàng)建一個(gè) FILE 對(duì)象
所有的構(gòu)造方法都需要傳入文件路徑,那么我們應(yīng)該如何去用呢?
如果程序只處理一個(gè)目錄和文件,并且知道該目錄或文件的路徑,就建議使用 構(gòu)造 1如果程序處理的是一個(gè)公共目錄中的若干子目錄或文件,就建議使用 構(gòu)造 2 和 構(gòu)造 3
案例:
public static void main(String[] args) {
// 方法一:通過(guò)絕對(duì)路徑創(chuàng)建
File f1 = new File("D:\\file\\a.txt");
// 方法二:通過(guò)相對(duì)路徑創(chuàng)建
File f2 = new File("src\\Hello.txt");
}
注:目錄符號(hào)(\) 用 \\ 表示,因?yàn)?\ 在Java中是特殊字符,具有轉(zhuǎn)義作用,因此用 \\ 表示,此外我們也可以用 / 來(lái)作目錄符號(hào)。
2.2 File 類的常用方法
方法聲明功能描述boolean exists()判定 File? 對(duì)象對(duì)應(yīng)的文件或目錄是否存在boolean delete()刪除 File? 對(duì)象對(duì)應(yīng)的文件或目錄boolean createNewFile()當(dāng) File 對(duì)象對(duì)應(yīng)文件不存在時(shí)則創(chuàng)建新文件,并且將新建的 File 對(duì)象指向新文件String getName()?返回 File 對(duì)象 表示的文件或目錄的名稱String getPath()?返回 File 對(duì)象 表示的文件或目錄的路徑String getAbsolutePath()? 返回 File 對(duì)象 表示的文件或目錄的相對(duì)路徑
在 UNIX / Linux 等系統(tǒng)上,如果路徑以 斜線 / 開(kāi)始,則這個(gè)路徑為絕對(duì)路徑在 Widows?等系統(tǒng)上,如果路徑以 盤符?開(kāi)始,則這個(gè)路徑為絕對(duì)路徑String getParentFile()?返回 File 對(duì)象 對(duì)應(yīng)目錄的父目錄(注:返回的目錄不包含最后一級(jí)子目錄)boolean canRead()判定 File? 對(duì)象對(duì)應(yīng)的文件或目錄是否可讀boolean canWrite()判定 File? 對(duì)象對(duì)應(yīng)的文件或目錄是否可寫boolean isFile()判斷 File 對(duì)象對(duì)應(yīng)的是否是文件boolean isDirectory()判斷 File 對(duì)象對(duì)應(yīng)的是否是目錄boolean isAbsolute()判斷 File 對(duì)象對(duì)應(yīng)的是否是絕對(duì)路徑long lastModified()返回 1970 年 1 月 1 日 0 時(shí) 0 分 0 秒 到文件最后修改時(shí)間的 毫秒值long length()返回文件內(nèi)容的長(zhǎng)度(注:?jiǎn)挝粸?字節(jié))String[] list()遞歸列出指定目錄的全部?jī)?nèi)容(包括子目錄和文件),只列出名稱File[] listFiles()返回一個(gè)包含 File 對(duì)象所有子文件和子目錄的 File 數(shù)組
演示:
public static void main(String[] args) {
File file = new File("src/test.txt"); // / 也可以作 目錄符號(hào)
System.out.println("文件是否存在:" + file.exists());
System.out.println("文件名:" + file.getName());
// ... 具體大家自己可以實(shí)踐
}
補(bǔ)充學(xué)習(xí):?createTempFile() 方法 和 deleteOnExit() 方法
在一些特定情況下,程序需要讀寫一些臨時(shí)文件,為此,F(xiàn)ile類提供了createTempFile() 方法 和 deleteOnExit() 方法,用于操作臨時(shí)文件。createTempFile() 方法用于創(chuàng)建一個(gè)臨時(shí)文件,deleteOnExit()?方法在Java虛擬機(jī)退出時(shí)自動(dòng)刪除臨時(shí)文件。
下面通過(guò)一個(gè)案例演示這兩個(gè)方法的使用:
public static void main(String[] args) throws IOException {
// 提供臨時(shí)文件的前綴和擴(kuò)展名
File file = File.createTempFile("itcast-", ".txt");
file.deleteOnExit(); // java 虛擬機(jī)退出時(shí) 自動(dòng)刪除文件 file
System.out.println("file 是否為文件:" + file.isFile());
System.out.println("file 的相對(duì)路徑:" + file.getPath());
}
2.3 遍歷目錄下的文件
File 類中提供了 list()方法,可以獲取目錄下所有文件和目錄的名稱。獲取目錄下所有文件和目錄名稱后,可以通過(guò)這些名稱遍歷目錄下的文件,按照調(diào)用方法的不同,對(duì)目錄下的文件遍歷可分為以下3種方式。
遍歷指定目錄下的所有文件遍歷指定目錄下指定擴(kuò)展名的文件遍歷包括子目錄中的文件在內(nèi)的所有文件
下面分別對(duì)這3種遍歷方式進(jìn)行詳細(xì)講解。
(1)遍歷指定目錄下的所有文件
File 類的 list()方法可以遍歷指定目錄下的所有文件。下面通過(guò)一個(gè)案例演示如何使用?list()方法遍歷目錄下的所有文件,如下:
public static void main(String[] args) {
File file = new File("src/IO");
if(file.isDirectory()) {
String[] names = file.list(); // 獲取目錄下所有文件的文件名
for(String name:names) {
System.out.println(name); //輸出文件名
}
}
}
(2)遍歷指定目錄下指定擴(kuò)展名的文件 ? 上述代碼實(shí)現(xiàn)了遍歷一個(gè)目錄下所有文件的功能,然而有時(shí)程序只需要獲取指定類型的文件,如獲取指定目錄下所有擴(kuò)展名為“.java”的文件。
針對(duì)這種需求,F(xiàn)ile類提供了一個(gè)重載的 list()方法,該方法接收一個(gè) FilenameFilter 類型的參數(shù)。FilenameFilter 是一個(gè)接口,被稱作文件過(guò)濾器,其中定義了抽象方法accept()用于依次對(duì)指定File的所有子目錄或文件進(jìn)行迭代。在調(diào)用list()方法時(shí),需要實(shí)現(xiàn) FilenameFilter,并在accept()方法中進(jìn)行篩選,從而獲得指定類型的文件。
下面通過(guò)一個(gè)案例演示如何遍歷指定目錄下所有擴(kuò)展名為“.java”的文件,如下:
public static void main(String[] args) {
File file = new File("src/IO");
// 創(chuàng)建文件過(guò)濾器對(duì)象
FilenameFilter filter = new FilenameFilter() {
// 實(shí)現(xiàn) accept 方法
@Override
public boolean accept(File dir, String name) {
File currFile = new File(dir, name);
// 如果文件以 .java 結(jié)尾返回true
if(currFile.isFile() && name.endsWith(".java")){
return true;
}
else return false;
}
};
if(file.exists()) {
String[] lists = file.list(); // 獲取目錄下所有文件的文件名
for(String name:lists) {
System.out.println(name); //輸出文件名
}
}
}
(3)遍歷包括子目錄下的文件在內(nèi)的所有文件 ? 前面的兩個(gè)例子演示的都是遍歷當(dāng)前目錄下的文件。有時(shí)候在一個(gè)目錄下,除了文件,還有子目錄,如果想獲取所有子目錄下的文件,list()方法顯然不能滿足要求,這時(shí)可以使用File 類提供的另一個(gè)方法—— listFiles()。
? 該方法返回一個(gè)File對(duì)象數(shù)組,當(dāng)對(duì)數(shù)組中的元素進(jìn)行遍歷時(shí),如果元素中還有子目錄需要遍歷,則可以遞歸遍歷子目錄。下面通過(guò)一個(gè)案例演示包括子目錄文件的所有文件的遍歷,如下:
public static void main(String[] args) {
// 創(chuàng)建一個(gè)代表目錄的 File 對(duì)象
File file = new File("src");
fileDir(file);
}
public static void fileDir(File dir)
{
File[] files = dir.listFiles(); // 獲得表示目錄下的所有文件數(shù)組
for(File file :files) // 遍歷所有子目錄和文件
{
if(file.isDirectory()){
fileDir(file); // 如果是目錄則遞歸調(diào)用
}
System.out.println(file.getAbsolutePath()); // 獲取文件的絕對(duì)路徑
}
}
2.4 刪除文件及目錄
?在操作文件時(shí),可能會(huì)遇到需要?jiǎng)h除一個(gè)目錄下某個(gè)文件或刪除整個(gè)目錄的操作,這時(shí)就可以調(diào)用File 類中的 delete() 方法。
public static void main(String[] args) {
File file = new File("src/IO");
if(file.exists()){
System.out.println(file.delete());
}
}
// 輸出
false
為啥會(huì)輸出 false 呢?
?因?yàn)槲募h除失敗了,F(xiàn)ile 類中的 delete() 方法只能刪除一個(gè)指定的文件,假如 File 對(duì)象代表一個(gè)目錄,而且這個(gè)目錄下包含子目錄或文件,則?File 類中的 delete() 方法 時(shí)不允許刪除整個(gè)目錄的。 此時(shí)就需要采用遞歸的方法來(lái)全部刪除
public static void main(String[] args) {
File file = new File("src/IO");
deleteDir(file);
System.out.println("刪除成功??!");
}
public static void deleteDir(File dir)
{
if(dir.exists())
{
File[] files = dir.listFiles(); // 獲得表示目錄下的所有文件數(shù)組
for(File file :files) // 遍歷所有子目錄和文件
{
if(file.isDirectory()){
deleteDir(file); // 如果是目錄則遞歸調(diào)用
}
else file.delete();// 如果是文件直接刪除
}
// 刪除整個(gè)目錄下的所有文件之后,就刪除這個(gè)目錄
dir.delete();
}
}
注意:
刪除目錄是從 Java 虛擬機(jī)直接刪除而不放入到回收站,文件一旦被刪除就無(wú)法恢復(fù),因此在進(jìn)行文件刪除操作的時(shí)候需要格外小心!?
3. 字節(jié)流 ?
3.1 基本概念
? 在程序的開(kāi)發(fā)中,經(jīng)常需要處理設(shè)備之間的數(shù)據(jù)傳輸,而在計(jì)算機(jī)中,無(wú)論是文本,圖片、音頻還是視頻,所有文件都是以二進(jìn)制(字節(jié))形式存在的。對(duì)于字節(jié)的輸入輸出,I/0 系統(tǒng)提供了一系列流,統(tǒng)稱為字節(jié)流。字節(jié)流是程序中最常用的流,根據(jù)數(shù)據(jù)的傳輸方向可將其分為字節(jié)輸入流和字節(jié)輸出流。 ? JDK 提供了兩個(gè)抽象類—— InputStream?和 OutputStream?,它們是字節(jié)流的頂級(jí)父類,所有的字節(jié)輸入流都繼承?InputStream?,所有的字節(jié)輸出流都繼承?OutputStream?。
字節(jié)流抽象基類
InputStream:這個(gè)抽象類是表示字節(jié)輸入流的所有類的超類OutputStream:這個(gè)抽象類是表示字節(jié)輸出流的所有類的超類子類名特點(diǎn):子類名稱都是以其父類名作為子類名的后綴字節(jié)輸出流
FileOutputStream(String name):創(chuàng)建文件輸出流以指定的名稱寫入文件
?注: I/O 流的輸入輸出是相對(duì)于 程序而言的
InputStream 類常用方法
方法聲明功能描述int read()從輸入流讀取一字節(jié)(8位), 把它轉(zhuǎn)化位 0 - 255 的整數(shù),并返回這個(gè)整數(shù)int read (byte[ ] b)從輸入流讀取若干字節(jié),把它們保存到參數(shù) b? 指定的字節(jié)數(shù)組中,返回的整數(shù)表示讀取的字節(jié)數(shù)int read (byte[ ] b, int off, int len)從輸入流讀取若干字節(jié),把它們保存到參數(shù) b? 指定的字節(jié)數(shù)組中,off指定字節(jié)數(shù)組保存數(shù)據(jù)的起始索引,len 表示讀取的字節(jié)數(shù)void close()關(guān)閉輸入流并且釋放與其相關(guān)的所有系統(tǒng)資源
上表中的3個(gè) read()?方法都是用來(lái)讀數(shù)據(jù)的。其中:
第一個(gè) read()?方法是從輸入流中逐個(gè)讀入字節(jié);而第二個(gè)和第三個(gè)read()方法則可以將若干字節(jié)以字節(jié)數(shù)組的形式一次性讀入,從而提高讀數(shù)據(jù)的效率。在進(jìn)行 I/O?操作時(shí),當(dāng)前 I/O 流會(huì)占用一定的內(nèi)存,由于系統(tǒng)資源非常寶貴,因此,在I/0操作結(jié)束后,應(yīng)該調(diào)用close()方法關(guān)閉 I/O 流,從而釋放當(dāng)前 I/O 流 所占的系統(tǒng)資源。
OutputStream 類常用方法
方法聲明功能描述void write(int b)將指定的字節(jié)寫入此文件輸出流,一次寫一個(gè)字節(jié)數(shù)據(jù)void write(byte[] b)將參數(shù) b指定的字節(jié)數(shù)組的所有字節(jié)寫入到此文件輸出流,一次寫一個(gè)字節(jié)數(shù)組數(shù)據(jù)void write(byte[] b, int off, int len)將指定 byte 數(shù)組從偏移量off(起始索引)開(kāi)始的 len字節(jié)寫入此文件輸出流?void flush()刷新輸出流并且強(qiáng)制寫出所有緩沖的輸出字節(jié)void close()關(guān)閉輸出流并且釋放與其關(guān)聯(lián)的所有系統(tǒng)資源
?上表前3個(gè)是重載的write()方法,都用于向輸出流寫入字節(jié)。
其中,第一個(gè)write()方法逐個(gè)寫入字節(jié);后兩個(gè)write()方法將若干字節(jié)以字節(jié)數(shù)組的形式一次性寫人,從而提高寫數(shù)據(jù)的效率。flush()方法用來(lái)將當(dāng)前輸出流緩沖區(qū)(通常是字節(jié)數(shù)組)中的數(shù)據(jù)強(qiáng)制寫入目標(biāo)設(shè)備,此過(guò)程稱為刷新。close()方法用來(lái)關(guān)閉1/0流并釋放與當(dāng)前1/0流相關(guān)的系統(tǒng)資源。
InputStream?和 OutputStream 這兩個(gè)類雖然提供了一系列和讀寫數(shù)據(jù)有關(guān)的方法,但是這兩個(gè)類是抽象類,不能被實(shí)例化,因此,針對(duì)不同的功能, InputStream?類?和 OutputStream 類提供了不同的子類,形成了體系結(jié)構(gòu),如下圖:
InputStream 體系結(jié)構(gòu)圖:
OutputStream 體系結(jié)構(gòu)圖:
3.2 字節(jié)流讀文件
里InputStream?就是JDK提供的基本輸入流,它是所有輸入流的父類,F(xiàn)ileInputStream?是InputStream?的子類,它是操作文件的字節(jié)輸入流,專門用于讀取文件中的數(shù)據(jù)。因?yàn)閺奈募x取數(shù)據(jù)是重復(fù)的操作,所以需要通過(guò)循環(huán)語(yǔ)句實(shí)現(xiàn)數(shù)據(jù)的持續(xù)讀取。
下面通過(guò)一個(gè)案例實(shí)現(xiàn)字節(jié)流對(duì)文件數(shù)據(jù)的讀取。在實(shí)現(xiàn)案例之前,先做以下操作:
首先在 Java項(xiàng)目的根目錄下創(chuàng)建文本文件test.txt在文件中輸入內(nèi)容“itcast”?并保存然后使用字節(jié)輸入流對(duì)象讀取 test.txt文本文件
案例代碼:
public static void main(String[] args) throws IOException {
// 創(chuàng)建一個(gè)文件字節(jié)輸入流,并且指定源文件名稱
FileInputStream in = new FileInputStream("src/IO/test.txt");
int b = 0; // 定義 int 類型的變量 b,用于 記住每次讀取的 1 字節(jié)
while(true) {
b = in.read(); // 變量 b 記住讀取的每一字節(jié)
if(b == -1){ // 如果讀取的字節(jié) 位 -1,則跳出循環(huán)
break;
}
System.out.println(b + " "); // 否則輸出b
}
in.close();
}
// 輸出
105 116 99 97 115 116
由于計(jì)算機(jī)中的數(shù)據(jù)都是以字節(jié)的形式存在的。在test.txt文件中,字符i、?t、c、a、s、t 各占一字節(jié),所以最終結(jié)果顯示的就是文件test.txt中的6字節(jié)對(duì)應(yīng)的十進(jìn)制數(shù)(即這6個(gè)字母的ASCII碼值)。
? 注意:
有時(shí),在文件讀取的過(guò)程中可能會(huì)發(fā)生錯(cuò)誤。例如,由于文件不存在而導(dǎo)致無(wú)法讀取?;蛘哂脩魶](méi)有讀取權(quán)限等等。這些錯(cuò)誤都由Java虛擬機(jī)自動(dòng)封裝成 IOException 異常并拋出。例如,當(dāng)讀取一個(gè)不存在的文件時(shí),控制臺(tái)會(huì)報(bào)告異常信息,
當(dāng)讀取一個(gè)不存在的文件時(shí),程序就會(huì)有一個(gè)潛在的問(wèn)題。如果文件讀取過(guò)程中發(fā)生了 I/O 錯(cuò)誤,InputStream?就無(wú)法正常關(guān)閉,系統(tǒng)資源也無(wú)法及時(shí)釋放,這樣會(huì)造成系統(tǒng)資源浪費(fèi)。
對(duì)此,可以使用?try…· finally 語(yǔ)句保證?InputStream?在任何情況下都能夠正確關(guān)閉。修改上述代碼,將讀取文件的代碼放入try語(yǔ)句塊中,將關(guān)閉輸入流的代碼放入finaly語(yǔ)句塊中,具體代碼如下:
public static void main(String[] args) throws Exception {
InputStream input = null;
try {
// 創(chuàng)建一個(gè)文件字節(jié)輸入流
FileInputStream in = new FileInputStream("src/IO/test.txt");
int b = 0; // 定義 int 類型的變量 b,用于 記住每次讀取的 1 字節(jié)
while (true) {
b = in.read(); // 變量 b 記住讀取的每一字節(jié)
if (b == -1) { // 如果讀取的字節(jié) 位 -1,則跳出循環(huán)
break;
}
System.out.print(b + " "); // 否則輸出b
}
} finally {
if (input != null) {
input.close();
}
}
}
3.3 字節(jié)流寫文件
OutputStream 是JDK提供的基本輸出流,與InputStream類似.
OutputStream是所有輸出流的父類。OutputStream?是一個(gè)抽象類,如果使用此類,則必須先通過(guò)子類實(shí)例化對(duì)象。OutputStream類有多個(gè)子類,其中FileOutputStream子類是操作文件的字節(jié)輸出流,專門用于把數(shù)據(jù)寫入文件。
案例演示:
public static void main(String[] args) throws Exception {
OutputStream out = new FileOutputStream("src/IO/example.txt");
String str = "Island1314";
byte[] b = str.getBytes();
for(int i = 0; i < b.length; i++){
out.write(b[i]);
}
out.close();
}
由上可知,使用 FileOutputStream?寫數(shù)據(jù)時(shí),程序自動(dòng)創(chuàng)建了文件 example.txt,并將數(shù)據(jù)寫入example.txt?文件。需要注意的是,如果通過(guò)?FileOutputStream?向一個(gè)已經(jīng)存在的文件中寫入數(shù)據(jù),那么該文件中的數(shù)據(jù)會(huì)被覆蓋。
若希望在已存在的文件內(nèi)容之后追加新內(nèi)容,我們應(yīng)該怎么做:
可使用?FileOutputStream?的構(gòu)造函數(shù)?public FileOutputStream(String name,boolean append)創(chuàng)建文件輸出流以指定的名稱寫入文件,并把a(bǔ)ppend參數(shù)的值設(shè)置為true。如果第二個(gè)參數(shù)為true ,則字節(jié)將寫入文件的末尾而不是開(kāi)頭
public static void main(String[] args) throws Exception {
OutputStream out = new FileOutputStream("src/IO/example.txt",true);
String str = "\r\n201314";
byte[] b = str.getBytes();
for(int i = 0; i < b.length; i++){
out.write(b[i]);
}
out.close();
}
// 在 example.txt 查看
Island1314
201314
// 解釋:程序通過(guò)字節(jié)輸出流對(duì)象out向文件example.txt寫入后,并沒(méi)有將文件原來(lái)的數(shù)據(jù)清空,而是將新寫入的數(shù)據(jù)追加到了文件的末尾。
上面的 \r \n 又是什么意思呢 》 解釋如下:
對(duì)于字節(jié)流寫數(shù)據(jù),應(yīng)該如何實(shí)現(xiàn)換行
windows:\r\nlinux:\nmac:\r
需要注意的是:I/O流 在進(jìn)行數(shù)據(jù)讀寫操作時(shí)會(huì)出現(xiàn)異常。為了保持代碼的簡(jiǎn)潔,在InputStream 讀文件和OutputStream寫文件的程序中都使用了throws關(guān)鍵字將異常拋出。然而一旦遇到 I/O異常,I/O流 的 close()方法 將無(wú)法得到執(zhí)行,I/O流 對(duì)象占用的系統(tǒng)資源將得不到釋放。
因此,為了保證I/O流 的 close()方法 必須執(zhí)行,通常將關(guān)閉?I/O流 的操作寫在?finally代碼塊中。
3.4 字節(jié)流復(fù)制文件
在應(yīng)用程序中,I/O 流通常都是成對(duì)出現(xiàn)的,即輸入流和輸出流一起使用。例如:文件的復(fù)制就需要通過(guò)輸入流讀取一個(gè)文件中的數(shù)據(jù),再通過(guò)輸出流將數(shù)據(jù)寫入另一個(gè)文件。
下面通過(guò)一個(gè)案例演示文件內(nèi)容的復(fù)制:
首先在 src 項(xiàng)目的根目錄下創(chuàng)建 source目錄和 target 目錄,然后在 source 目錄中存放 a.png文件,最后將 source目錄下的 a.png 復(fù)制到 target 目錄下并重新命名為 b.png。
public static void main(String[] args) throws Exception{
// 創(chuàng)建一個(gè)文件輸入流,用于讀取 sorce 目錄的 a.png 文件
InputStream in = new FileInputStream("src/source/a.png");
// 創(chuàng)建一個(gè)文件輸出流,用于將讀取數(shù)據(jù)寫入到 target 目錄的 b.png 文件
OutputStream out = new FileOutputStream("src/target/b.png");
int len; //用于記住每次讀取的 1 字節(jié)
// 獲取復(fù)制文件前的系統(tǒng)時(shí)間
long begintime = System.currentTimeMillis();
while ((len = in.read())!= -1){ // 讀取 1 字節(jié)并且判斷是否讀到文件末尾
out.write(len); // 將讀取的 1 字節(jié)寫入文件
}
// 獲取文件復(fù)制結(jié)束后的時(shí)間
long endtime = System.currentTimeMillis();
System.out.println("復(fù)制文件所消耗時(shí)間:" + (endtime - begintime) + "ms");
in.close();
out.close();
}
// 輸出:
復(fù)制文件所消耗時(shí)間:6038ms
上述代碼實(shí)現(xiàn)了文件的復(fù)制:
通過(guò)while循環(huán)將a.png的所有字節(jié)逐個(gè)進(jìn)行復(fù)制。每循環(huán)一次,就通過(guò)調(diào)用FileInputStream的read()方法讀取一字節(jié),并通過(guò)調(diào)用FileOutputStream 的write()方法將該字節(jié)寫入指定文件,直到 len 的值為 -1,表示讀到了文件末尾,結(jié)束循環(huán),完成文件的復(fù)制。程序運(yùn)行結(jié)束后,會(huì)在命令行窗口打印復(fù)制文件所消耗的時(shí)間。由上可知,程序復(fù)制文件共消耗了6038ms。在復(fù)制文件時(shí),由于計(jì)算機(jī)性能等各方面原因,會(huì)導(dǎo)致復(fù)制文件所消耗的時(shí)間不確定,因此每次運(yùn)行程序的結(jié)果未必相同。
在程序運(yùn)行結(jié)束后,打開(kāi)target目錄,發(fā)現(xiàn)source目錄中的 a.png 文件被成功復(fù)制到 target目錄中
注意事項(xiàng):
上述實(shí)現(xiàn)的文件復(fù)制過(guò)程是逐字節(jié)讀寫,需要頻繁地操作文件,效率非常低
打個(gè)比方:
從北京運(yùn)送烤鴨到上海,如果有一萬(wàn)只烤鴨,每次運(yùn)送一只,就必須運(yùn)輸一萬(wàn)次,這樣的效率顯然非常低。為了減少運(yùn)輸次數(shù),可以先把一批烤鴨裝在車廂中,這樣就可以成批地運(yùn)送烤鴨,這時(shí)的車廂就相當(dāng)于一個(gè)緩沖區(qū)
因此在通過(guò)流的方式復(fù)制文件時(shí),為了提高效率,也可以定義一個(gè)字節(jié)數(shù)組作為緩沖區(qū)。
在復(fù)制文件時(shí),可以一次性讀取多個(gè)字節(jié)的數(shù)據(jù),并保存在字節(jié)數(shù)組中,然后將字節(jié)數(shù)組中的數(shù)據(jù)一次性寫入文件。程序中的緩沖區(qū)就是一塊內(nèi)存,它主要用于暫時(shí)存放輸入輸出的數(shù)據(jù),由于使用緩沖區(qū)減少了對(duì)文件的操作次數(shù),所以可以提高數(shù)據(jù)的讀寫效率。
利用緩沖區(qū)復(fù)制文件,修改代碼如下:
public static void main(String[] args) throws Exception{
// 創(chuàng)建一個(gè)文件輸入流,用于讀取 sorce 目錄的 a.png 文件
InputStream in = new FileInputStream("src/source/a.png");
// 創(chuàng)建一個(gè)文件輸出流,用于將讀取數(shù)據(jù)寫入到 target 目錄的 b.png 文件
OutputStream out = new FileOutputStream("src/target/b.png");
// 以下是用 緩沖區(qū) 讀寫文件
byte[] buff = new byte[1024]; // 定義一個(gè)字節(jié)數(shù)組作緩沖區(qū)
int len; //用于記住每次讀取的 1 字節(jié)
// 獲取復(fù)制文件前的系統(tǒng)時(shí)間
long begintime = System.currentTimeMillis();
while ((len = in.read(buff))!= -1){ // 讀取 1 字節(jié)并且判斷是否讀到文件末尾
out.write(buff, 0, len); // 將讀取的 1 字節(jié)寫入文件
}
// 獲取文件復(fù)制結(jié)束后的時(shí)間
long endtime = System.currentTimeMillis();
System.out.println("復(fù)制文件所消耗時(shí)間:" + (endtime - begintime) + "ms");
in.close();
out.close();
}
// 輸出:
復(fù)制文件所消耗時(shí)間:8ms
可以看出復(fù)制文件消耗時(shí)間明顯減少,說(shuō)明使用緩沖區(qū)讀寫文件可以有效地提高程序讀寫效率
4. 字符流 ?
4.1 字符流定義及基本用法
? 前面講解的內(nèi)容都是通過(guò)字節(jié)流直接對(duì)文件進(jìn)行讀寫。如果讀寫的文件內(nèi)容是字符,考慮到使用字節(jié)流讀寫字符可能存在傳輸效率以及數(shù)據(jù)編碼問(wèn)題、此時(shí)建議使用字符流。
同字節(jié)流一樣,字符流也有兩個(gè)抽象的頂級(jí)父類,分別是 Reader類 和?Writer類。
Reader 類是字符輸入流,用于從某個(gè)源設(shè)備讀取字符;Writer類是字符輸出流。用于向某個(gè)目標(biāo)設(shè)備寫入字符。在JDK中,Reader 類和Writer 類提供了一系列與讀寫數(shù)據(jù)相關(guān)的方法。
注:字符流 = 字節(jié)流 + 編碼表
Reader 類的常用方法
方法聲明功能描述int read()以字符為單位讀數(shù)據(jù)int read(char[] cbuf)將數(shù)據(jù)讀入 char 類型的數(shù)組,并返回?cái)?shù)組長(zhǎng)度int read(char[] cbuf, int off, int len)將數(shù)據(jù)讀入 char 類型的數(shù)組的指定區(qū)間,并返回?cái)?shù)組長(zhǎng)度void close()關(guān)閉數(shù)據(jù)流long transferTo(Writer out)將數(shù)據(jù)之間讀入字符輸出流
Writer 類的常用方法
方法聲明功能描述void write(int c)以字符為單位寫數(shù)據(jù)void write(char[] cbuf)將 char 類型的數(shù)組中的數(shù)據(jù)寫出void write(char[] cbuf, int off, int len)將 char 類型的數(shù)組中指定區(qū)間的數(shù)據(jù)寫出void write(String str)將 String 類型的數(shù)據(jù)寫出void write(String str, , int off, int len)將 String 類型中指定區(qū)間的數(shù)據(jù)寫出void flush()強(qiáng)制將緩沖區(qū)的數(shù)據(jù)同步到輸出流 (刷新流),之后還可以繼續(xù)寫數(shù)據(jù)void close()關(guān)閉數(shù)據(jù)流
?Reader?類?和 Writer?類作為字符流的頂級(jí)父類,也有許多子類,形成了體系結(jié)構(gòu),分別如下:
Reader 體系結(jié)構(gòu)圖:
Writer 體系結(jié)構(gòu)圖:
? 在上面我們可以看到字符流的繼承關(guān)系和字節(jié)流的繼承關(guān)系類似,Reader 類 和 Writer 類的很多子類都是成對(duì)出現(xiàn)。例如:
FileReader 和 FileWriter 用于讀寫文件BufferedReader 和 BufferedWriter 是具有緩沖功能的字符流,使用他們可以提高讀寫效率
4.2 字符流讀文件
塞在程序開(kāi)發(fā)中,經(jīng)常需要對(duì)文本文件的內(nèi)容進(jìn)行讀取。如果想從文件中直接讀取字符,便可以使用字符輸入流?FileReader,通過(guò)它可以從關(guān)聯(lián)的文件中讀取一個(gè)或一組字符。
下面通過(guò)一個(gè)案例演示如何使用?FileReader 讀取文件中的字符:
首先新建文本文件?test.txt?并在其中輸入字符 “itcast”然后創(chuàng)建字符輸入流 FileReader對(duì)象以讀取 reader.txt文件中的內(nèi)容
public static void main(String[] args) throws Exception {
// 創(chuàng)建一個(gè) FileReader 對(duì)象,用來(lái)讀取文件字符
FileReader reader = new FileReader("src/IO/test.txt");
int ch; // 用于記錄讀取的字符
while((ch = reader.read()) != -1){ // 循環(huán)判斷是否讀到文件末尾
System.out.print((char) ch); // 不是文件末尾就打印字符
}
reader.close(); // 關(guān)閉字符輸入流,釋放資源
}
// 輸出
itcast
注:FileReader對(duì)象的 read() 方法返回的是 int 類型的值,如果想獲得字符,就必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換。
4.3 字符流寫文件
???上面講解了字符流對(duì)文本文件內(nèi)容的讀取。現(xiàn)在講解通過(guò)字符流向文本文件中寫入內(nèi)容,此時(shí)需要使用FileWriter類,該類可以一次向文件中寫人一個(gè)或一組字符。
下面通過(guò)一個(gè)案例演示如何使用 ?FileWriter?將字符寫入文件
public static void main(String[] args) throws Exception {
// 創(chuàng)建一個(gè) FileWriter 對(duì)象,用于向文件寫入數(shù)據(jù)
FileWriter writer = new FileWriter("src/IO/example.txt");
String str = "IsLand1314";
writer.write(str); // 將字符數(shù)據(jù)寫入到文本文件中
writer.write("\r\n"); //輸出換行
writer.close();
}
注意:
?FileWriter 同 FileOutputStream?一樣,如果指定的文件不存在,就會(huì)先創(chuàng)建文件,再寫入數(shù)據(jù);如果文件存在,則原文件內(nèi)容會(huì)被覆蓋。如果想在文件末尾追加數(shù)據(jù),同樣需要調(diào)用重載的構(gòu)造方法,將上面第三行代碼修改為:
FileWriter writer = new FileWriter("src/IO/example.txt", true);
再次運(yùn)行程序就可以在文件中實(shí)現(xiàn)追加的功能
4.4 數(shù)據(jù)編碼解碼問(wèn)題
由于字節(jié)流操作中文不是特別的方便,所以Java就提供字符流
字符流 = 字節(jié)流 + 編碼表
中文的字節(jié)存儲(chǔ)方式
用字節(jié)流復(fù)制文本文件時(shí),文本文件也會(huì)有中文,但是沒(méi)有問(wèn)題,原因是最終底層操作會(huì)自動(dòng)進(jìn)行字節(jié)拼接成中文,如何識(shí)別是中文的呢?漢字在存儲(chǔ)的時(shí)候,無(wú)論選擇哪種編碼存儲(chǔ),第一個(gè)字節(jié)都是負(fù)數(shù)
函數(shù)聲明功能描述byte[] getBytes()使用平臺(tái)的默認(rèn)字符集將該 String編碼為一系列字節(jié)byte[] getBytes(String charsetName)使用指定的字符集將該 String編碼為一系列字節(jié)String(byte[] bytes)使用平臺(tái)的默認(rèn)字符集解碼指定的字節(jié)數(shù)組來(lái)創(chuàng)建字符串String(byte[] bytes, String charsetName)通過(guò)指定的字符集解碼指定的字節(jié)數(shù)組來(lái)創(chuàng)建字符串
代碼演示:
public static void main(String[] args) throws UnsupportedEncodingException {
//定義一個(gè)字符串
String s = "中國(guó)";
//byte[] bys = s.getBytes(); //[-28, -72, -83, -27, -101, -67]
//byte[] bys = s.getBytes("UTF-8"); //[-28, -72, -83, -27, -101, -67]
byte[] bys = s.getBytes("GBK"); //[-42, -48, -71, -6]
System.out.println(Arrays.toString(bys));
//String ss = new String(bys);
//String ss = new String(bys,"UTF-8");
String ss = new String(bys,"GBK");
System.out.println(ss);
}
5. 轉(zhuǎn)換流 ?
? 前面提到I/0流分為字節(jié)流和字符流,字節(jié)流和字符流之間可以進(jìn)行轉(zhuǎn)換。JDK提供了兩個(gè)類用于將字節(jié)流轉(zhuǎn)換為字符流,分別是 InputStreamReader 和 ?OutputStreamReader。
InputStreamReader:是從字節(jié)流到字符流的橋梁,父類是 Reader
它讀取字節(jié),并使用指定的編碼將其解碼為字符它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平臺(tái)的默認(rèn)字符集
OutputStreamReader:是從字符流到字節(jié)流的橋梁,父類是 Writer
是從字符流到字節(jié)流的橋梁,使用指定的編碼將寫入的字符編碼為字節(jié)它使用的字符集可以由名稱指定,也可以被明確指定,或者可以接受平臺(tái)的默認(rèn)字符集
通過(guò)?InputStreamReader 和 OutputStreamReader 將字節(jié)流轉(zhuǎn)換為字符流,可以提高文件的讀寫效率
方法聲明功能描述InputStreamReader(InputStream in)使用默認(rèn)字符編碼創(chuàng)建InputStreamReader對(duì)象InputStreamReader(InputStream in,String chatset)使用指定的字符編碼創(chuàng)建InputStreamReader對(duì)象OutputStreamWriter(OutputStream out)使用默認(rèn)字符編碼創(chuàng)建OutputStreamWriter對(duì)象OutputStreamWriter(OutputStream out,String charset)使用指定的字符編碼創(chuàng)建OutputStreamWriter對(duì)象
下面通過(guò)一個(gè)案例演示如何將字節(jié)流轉(zhuǎn)為字符流
首先.在src項(xiàng)目的根目錄下新建文本文件 test.txt并在文件中輸入“Island1314”其次,在sre文件夾中創(chuàng)建一個(gè)類,在類中創(chuàng)建字節(jié)輸入流 FileInputStream對(duì)象讀取src.txt文件中的內(nèi)容,并將字節(jié)輸入流轉(zhuǎn)換成字符輸入流。再次,創(chuàng)建一個(gè)字節(jié)輸出流對(duì)象,并指定目標(biāo)文件為des.txt最后,將字節(jié)輸出流轉(zhuǎn)換成字符輸出流將字符輸出到文件中
public static void main(String[] args) throws Exception {
// 創(chuàng)建字節(jié)輸入流 in ,并且指定源文件 test.txt
FileInputStream in = new FileInputStream("src/IO/test.txt");
// 將字節(jié)輸入流 in 轉(zhuǎn)化為 字符輸入流 isr
InputStreamReader isr = new InputStreamReader(in);
// 創(chuàng)建字節(jié)輸出流 out ,并且指定源文件 des.txt
FileOutputStream out = new FileOutputStream("src/IO/des.txt");
// 將字節(jié)輸出流 out 轉(zhuǎn)化為 字符輸出流 osw
OutputStreamWriter osw = new OutputStreamWriter(out);
int ch; // 定義一個(gè)變量用于記錄讀取的字符
while((ch = isr.read()) != -1) // 循環(huán)判斷是否讀到文件末尾
{
osw.write(ch);
}
isr.close(); // 關(guān)閉字符輸入流,節(jié)省資源
osw.close(); // 關(guān)閉字符輸出流,節(jié)省資源
}
?
6. 緩沖流 ?
6.1 字節(jié)緩沖流
BufferedOutputStream:該類實(shí)現(xiàn)緩沖輸出流.通過(guò)設(shè)置這樣的輸出流,應(yīng)用程序可以向底層輸出流寫入字節(jié),而不必為寫入的每個(gè)字節(jié)導(dǎo)致底層系統(tǒng)的調(diào)用BufferedInputStream:創(chuàng)建BufferedInputStream將創(chuàng)建一個(gè)內(nèi)部緩沖區(qū)數(shù)組.當(dāng)從流中讀取或跳過(guò)字節(jié)時(shí),內(nèi)部緩沖區(qū)將根據(jù)需要從所包含的輸入流中重新填充,一次很多字節(jié)字節(jié)流緩沖區(qū)的核心優(yōu)勢(shì)就是一次讀取多個(gè)字節(jié)數(shù)據(jù),從而減少硬盤操作子樹(shù)
構(gòu)造方法:
BufferedOutputStream(OutputStream out)創(chuàng)建字節(jié)緩沖輸出流對(duì)象BufferedInputStream(InputStream in)創(chuàng)建字節(jié)緩沖輸入流對(duì)象
public static void main(String[] args) throws IOException {
//字節(jié)緩沖輸出流:BufferedOutputStream(OutputStream out)
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src/IO/test.txt"));
//寫數(shù)據(jù)
bos.write("hello\r\n".getBytes());
bos.write("world\r\n".getBytes());
//釋放資源
bos.close();
//字節(jié)緩沖輸入流:BufferedInputStream(InputStream in)
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src/IO/test.txt"));
//一次讀取一個(gè)字節(jié)數(shù)據(jù)
// int by;
// while ((by=bis.read())!=-1) {
// System.out.print((char)by);
// }
//一次讀取一個(gè)字節(jié)數(shù)組數(shù)據(jù)
byte[] bys = new byte[1024];
int len;
while ((len=bis.read(bys))!=-1) {
System.out.print(new String(bys,0,len));
}
//釋放資源
bis.close();
}
6.2 字符緩沖流
BufferedWriter:將文本寫入字符輸出流,緩沖字符,以提供單個(gè)字符,數(shù)組和字符串的高效寫入,可以指定緩沖區(qū)大小,或者可以接受默認(rèn)大小。默認(rèn)值足夠大,可用于大多數(shù)用途BufferedReader:從字符輸入流讀取文本,緩沖字符,以提供字符,數(shù)組和行的高效讀取,可以指定緩沖區(qū)大小,或者可以使用默認(rèn)大小。 默認(rèn)值足夠大,可用于大多數(shù)用途
BufferedWriter(Writer out)創(chuàng)建字符緩沖輸出流對(duì)象BufferedReader(Reader in)創(chuàng)建字符緩沖輸入流對(duì)象
public static void main(String[] args) throws IOException {
//創(chuàng)建字符緩沖輸出流
BufferedWriter bw = new BufferedWriter(new FileWriter("src/IO/test.txt"));
//寫數(shù)據(jù)
for (int i = 0; i < 10; i++) {
bw.write("hello" + i);
//bw.write("\r\n");
bw.newLine();
bw.flush();
}
//釋放資源
bw.close();
//創(chuàng)建字符緩沖輸入流
BufferedReader br = new BufferedReader(new FileReader("src/IO/test.txt"));
String line;
while ((line=br.readLine())!=null) {
System.out.println(line);
}
br.close();
}
7. 序列化反序列化 ?
? 程序在運(yùn)行過(guò)程中,數(shù)據(jù)都保存在Java對(duì)象(內(nèi)存)中,但很多情況下還需要將一些數(shù)據(jù)永久保存到磁盤上。為此,Java 提供了對(duì)象序列化機(jī)制,可以將對(duì)象中的數(shù)據(jù)保存到磁盤。
對(duì)象序列化(serialize)是指將一個(gè)Java對(duì)象轉(zhuǎn)換成一個(gè)?I/O流?的字節(jié)序列的過(guò)程。
對(duì)象序列化機(jī)制可以使內(nèi)存中的Java對(duì)象轉(zhuǎn)換成與平臺(tái)無(wú)關(guān)的二進(jìn)制流,通過(guò)編寫程序,既可以將這種二進(jìn)制流持久地保存在磁盤上,又可以通過(guò)網(wǎng)絡(luò)將其傳輸?shù)搅硪粋€(gè)網(wǎng)絡(luò)節(jié)點(diǎn)。
論 其他程序在獲得了二進(jìn)制流后,還可以將二進(jìn)制流恢復(fù)成原來(lái)的Java對(duì)象,這種將 I/O流 中的字節(jié)序列恢復(fù)為Java對(duì)象的過(guò)程稱為 反序列化(deserialize)。
? 如果想讓某個(gè)對(duì)象支持序列化機(jī)制,那么這個(gè)對(duì)象所屬的類必須是可序列化的。在Java中,可序列化的類必須實(shí)現(xiàn) Serializable 或 Externalizable 兩個(gè)接口之一。
Serializable?接口或 Externalizable 接口實(shí)現(xiàn)序列化機(jī)制的主要區(qū)別
Serializable 接口Externalizable 接口系統(tǒng)自動(dòng)存儲(chǔ)必要的信息由程序員自己決定要存儲(chǔ)的信息Java 內(nèi)部支持,易于實(shí)現(xiàn),只需實(shí)現(xiàn)該接口即可,不需要其他代碼支持該接口只提供了兩個(gè)抽象方法,實(shí)現(xiàn)該接口時(shí)必須重寫這兩個(gè)抽象方法性能較差性能較好
? 與實(shí)現(xiàn)?Serializable??接口相比,雖然實(shí)現(xiàn) Externalizable 接口可以帶來(lái)性能上的一定提升,但由于后者需要實(shí)現(xiàn)兩個(gè)抽象方法,所以將導(dǎo)致編程的復(fù)雜度提高。
在實(shí)際開(kāi)發(fā)時(shí),大部分情況下使用Serializable 接口的方式實(shí)現(xiàn)對(duì)象序列化。
?使用Serializable?接口實(shí)現(xiàn)對(duì)象序列化非常簡(jiǎn)單,只需要讓目標(biāo)類實(shí)現(xiàn) Serializable 接口即可,無(wú)須實(shí)現(xiàn)任何方法。例如,自定義Person類,讓Person類實(shí)現(xiàn)?Serializable接口,如下:
public class Person implements Serializable{
// 為該類指定 serialVersionUID 變量值
private static final long serialVersionUID = 1L;
// 聲明變量
private int id;
private String name;
private int age;
//... 此處省略各屬性的 gettter 和 setter 方法
}
? 在上述代碼中,Person類實(shí)現(xiàn)了?Serializable接口,并指定了?serialVersionUID變量值,該屬性的值的作用是標(biāo)識(shí)Java類的序列化版本。如果不顯式定義 serialVersionUID變量值,那么serialVersionUID屬性的值將由 Java 虛擬機(jī) 根據(jù)類的相關(guān)信息計(jì)算得出
補(bǔ)充知識(shí):serialVersionUID
? serialVersionUID適用于Java的對(duì)象序列化機(jī)制。簡(jiǎn)單來(lái)說(shuō),Java的對(duì)象序列化機(jī)制是通過(guò)判斷類的?serialVersionUID?來(lái)驗(yàn)證版本一致性的。在進(jìn)行反序列化時(shí),Java虛擬機(jī)會(huì)把字節(jié)流中的?serialVersionUID?與本地相應(yīng)實(shí)體類的?serialVersionUID?進(jìn)行比較。如果相同,就認(rèn)為是一致的,可以進(jìn)行反序列化;否則就會(huì)拋出序列化版本不一致的異常。
因此,為了在反序列化時(shí)確保序列化版本的兼容性,最好在每一個(gè)要序列化的類中加入 private static final long serialVersionUID 的變量值,具體數(shù)值可自定義,默認(rèn)是1L。如果不顯式指定 serialVersionUID?的值,系統(tǒng)可以根據(jù)類名、接口名、成員方法及屬性等生成一個(gè)64位的哈希值,將這個(gè)哈希值作為serialVersionUID的值。定義了serialVersionUID的值,如果serialVersionUID所屬類的某個(gè)對(duì)象被序列化,即使該對(duì)象對(duì)應(yīng)的類被修改了,該對(duì)象也依然可以被正確地反序列化。
8. 小結(jié) ?
本章主要介紹了 I/O流 的相關(guān)知識(shí)。
? 包括File類,包括創(chuàng)建File對(duì)象、File 類的常用方法、遍歷目錄下的文件和刪除文件及目錄;字節(jié)流,包括字節(jié)流的概念、字節(jié)流讀文件、字節(jié)流寫文件和文件的復(fù)制;字符流,包括字符流的定義及基本用法、字符流讀文件和字符流寫文件;轉(zhuǎn)換流的使用;序列化和反序列化。通過(guò)本章的學(xué)習(xí),讀者應(yīng)該了解 I/O 流,并且熟練掌握了 I/O 流的相關(guān)知識(shí)。
補(bǔ)充:字節(jié)流與字符流區(qū)別
字節(jié)流是IO中最基礎(chǔ)的形式,它以字節(jié)(8位)為單位進(jìn)行數(shù)據(jù)傳輸,適用于處理所有類型的數(shù)據(jù),包括文本、圖片、音頻和視頻等二進(jìn)制數(shù)據(jù)。在Java中,字節(jié)流的基類是InputStream和OutputStream。字節(jié)流在操作時(shí)通常不會(huì)使用緩沖區(qū),直接與文件本身進(jìn)行操作,這意味著每次調(diào)用read方法都可能伴隨著一次磁盤IO,因此效率相對(duì)較低。為了提高效率,可以使用如BufferedInputStream和BufferedOutputStream這樣的緩沖字節(jié)流。
字符流則是以Unicode碼元(16位)為單位進(jìn)行數(shù)據(jù)傳輸,主要用于處理文本數(shù)據(jù)。字符流在處理數(shù)據(jù)時(shí)會(huì)涉及字符編碼的轉(zhuǎn)換,如UTF-8或GBK等。在Java中,字符流的基類是Reader和Writer。字符流在輸出前會(huì)完成Unicode碼元序列到相應(yīng)編碼方式的字節(jié)序列的轉(zhuǎn)換,并使用內(nèi)存緩沖區(qū)來(lái)存放轉(zhuǎn)換后的字節(jié)序列,等待都轉(zhuǎn)換完畢再一同寫入磁盤文件中。
主要區(qū)別在于:
字節(jié)流操作的基本單元為字節(jié),而字符流操作的基本單元為Unicode碼元。 字節(jié)流不使用緩沖區(qū),字符流使用緩沖區(qū)。 字節(jié)流可以處理任何類型的數(shù)據(jù),字符流主要處理文本數(shù)據(jù)。 字節(jié)流與文件直接操作,字符流在操作時(shí)使用緩沖區(qū)。
【*★,°*:.☆( ̄▽ ̄)/$:*.°★* 】那么本篇到此就結(jié)束啦,如果我的這篇博客可以給你提供有益的參考和啟示,可以三連支持一下 !!??????
我的博客即將同步至騰訊云開(kāi)發(fā)者社區(qū),邀請(qǐng)大家一同入駐:https://cloud.tencent.com/developer/support-plan?invite_code=1uyo4puciczbw
柚子快報(bào)激活碼778899分享:【Java】I/O 操作詳解
好文閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。