Java IO

在整個java.io包中最重要的就是5個類和1個接口,5個類指的是File、OutputStream、InputStream、Writer、Reader;1個接口指的是Serializable。

1.操作文件的類-File類(文件本身)

在整個io包中,唯一與文件本身有關的類就是File類。
使用File類可以進行創建或刪除文件等常用操作。
要想使用File類,則必須向File類的構造方法中傳遞一個文件路徑。
構造方法:public File(String pathname)

File類中的主要方法和常量
這裏寫圖片描述

(1)創建一個新文件
public boolean createNewFile() throws IOException
File類的實例化對象完成之後,就可以使用createNewFile()創建一個新文件,但是此方法使用了throws關鍵字聲明,所以在使用中,必須使用try…catch進行異常的處理。
注:windows中使用反斜槓表示目錄的分隔符“\”
Linux中使用正斜槓表示目錄的分隔符“/”。
在操作文件時一定要使用File.separator表示分隔符。

(2)刪除一個指定的文件
File類中也支持文件的操作,如果要刪除一個文件,則可以使用File類中的delete()方法。
判斷一個文件是否存在可以直接使用File類提供的exists()方法,此方法返回boolean類型。

(3)創建一個文件夾
使用File類創建一個文件夾,直接使用mkdir()方法就可以完成。

(4)列出指定目錄的全部文件
public String[] list():列出全部名稱,返回一個字符串數組;
public File[] listFiles():列出完整的路徑,返回一個File對象數組。
使用listFiles()方法列出目錄中的內容更加方便。

(5)判斷一個給定的路徑是否是目錄
直接使用isDirectory()方法判斷給定的一個路徑是否是目錄。

2.RondomAccessFile類(文件內容)

File類只是針對文件本身進行操作,如果要對文件內容進行操作,則可以使用RandomAccessFile類,此類屬於隨機讀取類,可以隨機地讀取一個文件中指定位置的數據。

常用操作方法:
這裏寫圖片描述

注:如果使用rw方式聲明RandomAccessFile類時,要寫入的文件不存在,系統將自動進行創建。

讀取時直接使用r的模式即可,以只讀的方式打開文件。
讀取時所有的字符串只能按照byte數組的方式讀取出來,而且所有的長度是8位。

隨機讀寫流可以實現對文件內容的操作,但是卻過於複雜,所以一般情況下操作文件內容往往會使用字節或字符流。

3.字節流與字符流基本操作

在程序中所有的數據都是以流的方式進行傳輸或保存的,程序需要數據時要使用輸入流讀取數據,而當程序需要將一些數據保存起來時,就要使用輸出流。
在java.io包中流的基本操作主要有字節流、字符流兩大類,兩類都有輸入和輸出操作。在字節流中輸出數據主要使OutputStream類完成,輸入使用的是InputStream類。在字符流中輸出主要是使用Writer類完成,輸入主要是使用Reader類完成。

其中主要操作流程:
①使用File類打開一個文件;
②通過字節流或字符流的子類指定輸出的位置;
③進行讀/寫操作;
④關閉輸入/輸出。
使用File類操作的時候一定要有路徑的問題,注意分隔符。
以上4個操作類都是抽象類,IO操作屬於資源操作,對於資源操作,操作的最後必須關閉,否則就有可能出現未知的錯誤。

(1)字節流
字節流主要是操作byte類型數據,以byte數組爲準,主要操作類是OutputStream、InputStream。
1)字節輸出流:OutputStream
OutputStream是整個IO包中字節輸出流的最大父類,定義如下:

public abstract class OutputStream extends Object implements Clostable,Flushable

此類是一個抽象類,如果要想使用此類的話,則首先必須通過子類實例化對象,如果要操作的是一個文件,則可以使用FileOutputStream。通過向上轉型之後,可以爲OutputStream實例化。
Cloneseable表示可以關閉的操作,因爲程序運行到最後肯定要關閉。
Flushable表示刷新,清空內存中的數據。
常用方法:

要想使用以上方法,必須使用子類實例化,此時使用FileOutputStream子類,此類的構造方法是:

public FileOutputStream(File file) throws FileNotFoundException

可以直接將一個字符串變爲byte數組,然後將byte數組直接寫入到文件中,當然也可以通過循環把每一個字節一個個地寫入到文件之中。

2)追加新內容
以上的所有操作,如果重新執行程序,肯定會覆蓋文件中的已有內容,此時FileOutputStream類的另外一個構造方法進行實例化,這樣在寫入的時候就表示向文件中追加內容,此構造方法定義:

pubic FileOutputStream(File file,boolean append) throws FileNotFoundException

在構造方法中,如果將append的值設置爲true,則表示在文件的末尾追加內容。

3)字節輸入流InputStream
可以通過InputStream從文件中把內容讀取進來,其定義是:

public abstract class InputStream extends Object implements Closeable

與OutputStream類一樣,InputStream本身也是一個抽象類,必須依靠其子類,如果現在從文件中讀取,子類肯定是FileInputStream。
其方法定義爲public FileInoutStream(File file) throws FileNotFoundException

常用方法:
這裏寫圖片描述
如果要想知道文件大小,直接使用File類即可。

public long length()
byte b[]=new byte[(int)f.length]

如果不知道要輸入的內容有多大,則只能通過判斷是否讀到文件末尾的方式來讀取文件。

(2)字符流
在程序中一個字符等於兩個字節,Java提供了Reader和Writer兩個專門操作字符流的類。
1)字符輸出流Writer
Writer本身是一個字符流的輸出類,定義如下:
public abstract class Writer extends Object implements Appendable,Closeable,Flushable
此類本身也是一個抽象類,如果要使用此類,則肯定使用其子類,此時如果想文件中寫入內容,應該使用FileWriter的子類。
FileWriter的構造方法:public FileWriter(File file) throws IOException

常用方法:
這裏寫圖片描述

字符流的操作比字節流操作好在一點就是可以直接輸出字符串,不用再像之前那樣進行轉換操作了。

2)使用FileWriter追加文件的內容
可以使用FileWriter類的另一個構造方法進行追加:

public FileWriter(File file,boolean append)throws IOException

3)字符輸入流Reader
Reader是使用字符的方式從文件中取出數據。
其定義如下:

public abstract class Reader extends Object implements Readable,Closeable

Reader本身也是抽象類,如果現在要從文件中讀取內容,則可以直接使用FileReader子類。
FileReader的構造方法定義:

public FileReader(File file) throws FileNotFoundException

常用方法:
這裏寫圖片描述

(3)字節流和字符流的區別
字節流在操作的時候本身是不會用到緩衝區(內存)的,是與文件本身直接操作的,而字符流在操作的時候是使用到緩衝區的。
這裏寫圖片描述

字符流操作時使用到了緩衝區,而在關閉字符流時會強制性地將緩衝區中的內容進行輸出,但是如果程序沒有關閉,則緩衝區中的內容是無法輸出的。

什麼是緩衝區?
緩衝區可以簡單地理解爲一段內存區域。
某些情況下,如果一個程序頻繁地操作一個資源(如文件或數據庫),則性能會很低,此時爲了提升性能,就可以將一部分數據暫時讀入到內存的一塊區域之中,以後直接從此區域中讀取數據即可,因爲讀取內存速度會比較快,這樣可以提升程序的性能。
在字符流的操作中,所有的字符都是在內存中形成的,在輸出前會將所有的內容暫時保存在內存之中,所有使用了緩衝區暫存數據。

如果想在不關閉時也可以將字符流的內容全部輸出,則可以使用Writer類中的flush()方法完成,此方法可以強制性清空緩衝區中的內容。

關於使用字符流好還是字符流好?
推薦使用字節流。
所有的文件在硬盤或在傳輸時都說以字節方式進行的,包括圖片等都是按字節的方式存儲的,而字符是只有在內存中才會形成,所有在開發中,字節流使用更廣泛。

4.轉換流-OutputStreamWriter類與InputStreamReader類

在整個IO包實際上分爲字節流和字符流,但是除了這兩個流之外,還存在一組字節流-字符流的轉換類。
OutputStreamWriter:是Writer的子類,將輸出的字符流變爲字節流,即將一個字符流的輸出對象變爲字節流的輸出對象。
InputStreamReader:是Reader的子類,將輸入的字節流變爲字符流,即將一個字節流的輸入對象變爲字符流的輸入對象。

如果以文件操作爲例,則在內存中的字符數據需要通過OutputSteamWriter變爲字節流才能保存在文件之中,讀取的時候需要將讀入的字節流通過InputStreamReader變爲字符流。
這裏寫圖片描述

不管如何操作,最終全部是以字節的形式保存在文件中。
OutputStreamWriter的構造方法:

public OutputStreamWriter(OutputStream out)

FileOutStream是OutputStream的直接子類,FileInStream是InputStream的直接子類,但是在字符流中文件的兩個操作類卻有一些特殊,FileWriter並不直接是Writer的子類,而是OutputStream的子類,而FileReader也不直接是Reader的子類,而是InputStream的子類,兩個子類中間都需要進行轉換的操作。
基於如上可以發現,不管是使用字節流還是字符流實際上最終都是以字節的形式操作輸入/輸出流的。

5.內存操作流

前面的內容輸入和輸出都是從文件中來的,當然也可以將輸入和輸出的位置設置到內存上,此時要使用ByteArrayInputStream、ByteArrayOutputStream來完成內存的輸入和輸出功能。

ByteArrayInputStream主要完成將內容寫入到內存中;ByteArrayOutputStream主要將內存的內容進行輸出。
這裏寫圖片描述
注:內存操作流的操作對象一定是以內存爲準的。
內存操作流一般在生成一些臨時信息時纔會使用,如果將這些臨時信息保存在文件中,則代碼執行完後肯定還要刪除這個臨時文件會比較麻煩,這時使用內存操作流是最合適的。

6.管道流(線程通信流)

管道流的主要作用是可以進行兩個線程間的通訊,分爲管道輸出流(PipeOutputStream)、管道輸入流(PipedInputStream),如果要想進行管道輸出,則必須把輸出流連在輸入流上,在PipedOutputStream類上有如下的一個方法用於連接管道:

public void connect(PipedInputStream snk) throws IOException

這裏寫圖片描述

7.打印流

在整個IO包中,打印流是輸出信息最方便的類,主要包含字節打印流(PrintStream)和字符打印流(PrintWriter)。
打印流提供了非常方便的打印功能,可以打印任何的數據類型,如小數、整數、字符串等。
PrinfStream類的常用方法:
這裏寫圖片描述
在這個類中定義了很多的print()或println()方法,System.out.println(),此方法可以打印任何的數據類型。

在PrintStream中定義的構造方法中可以清楚的發現有一個構造方法可以直接結算OutputStream類的實例,這是因爲與OutputStream相比起來,PrintStream可以更加方便的輸出數據,這就好比將OutputStream類重新包裝了與喜愛,使之輸出更加方便。
這裏寫圖片描述

在JDK1.5之後,Java又對PrintStream類進行了擴充,增加了格式化的輸出方式,直接使用printf()方法就可以完成操作,但是在進行格式化輸出的時候需要知道其輸出的數據類型。
這裏寫圖片描述

8.System類對IO的支持(★★★)

System類對IO給予了一定的支持。
這裏寫圖片描述
(1)System.out
System.out是PrintStream的對象,在PrintStream中定義了一些了的print()和println()方法,所以之前使用的System.out.println()或System.out.print()語句調用的實際上就是PrintStream類的方法。
使用System,out輸出的時候就是將輸出的位置定義在了顯示器之中。
OutputStream的哪個子類爲其實例化,就具備了向哪裏輸出的能力。FileoutputStream是定位在文件裏,而System,out是定位在屏幕上。
PrintStream是OutputStream的子類。

(2)System.err
System.err表示的是錯誤信息輸出,如果程序出現錯誤,則可以直接使用System,err進行輸出。

System,out和System,in的區別
System.out和System.err的輸出結果是一樣的,兩者只能從概念上理解。
System.out和System.err都是PrintStream的實例化對象,而且通過實例代碼可以發現兩者都可以輸出錯誤信息,但是一般來講System.out是將信息顯示給用戶看,是正常的信息顯示,而System.err的信息正好相反是不希望用戶看到的,會直接在後臺打印,專門顯示錯誤的。
一般來講,如果要想輸出錯誤信息的時候最好不要使用System,out而是直接使用System,in,這一點只能從其概念上劃分。
這裏寫圖片描述

(3)System.in
System.in實際上是一個鍵盤的輸入流,其本身是InputStream類型的對象。那麼,此時可以利用System,in完成從鍵盤讀取數據的功能。

如果輸入的是英文字母,是沒有任何的問題,而如果輸入的是中文,則會產生亂碼,因爲數據是一個個字節的方式讀進來的,一個漢字是分兩次讀取的,所以造成了亂碼。
最好的輸入放是將全部輸入的數據暫時放到一塊內存之中,之後一次性的從內存中讀取出數據,這樣所有數據只讀了一次,則不會造成亂碼,而且也不會受長度的限制。
這裏寫圖片描述

(4)輸入/輸出重定向
System類提供的重定向方法:
這裏寫圖片描述
①爲System.out輸出重定向

System.setOut(new PrintStream(new FileOutputStream(“d:”+File.separator+”red.tex”)));
System.out.println(“hello world”);

②爲System.err輸出重定向
雖然在System類中提供了setErr()這個錯誤輸出的重定向方法,但是一般情況下,請不要使用這個方法修改System.err的重定向,因爲從概念上講System,err的錯誤信息是不希望用戶看到的。

注:setOut()方法只負責System.out的輸出重定向,而setErr()方法只負責System.err的輸出重定向,兩者不可混用。
③爲System.in的輸入重定向

System.setIn(new FileOutputStream(“d:”+File.separator+”red.tex”));
InputStream input=System.in;
byte b[]=new byte[1024];
int len=input.read(b);
Sytem,out.println(“輸入內容:”+new String(b,0,len));
input.close();

注:對於System.in來講主要是針對鍵盤的輸入,如果將其修改,肯定要損失其功能,故在開發中不建議修改System.in的操作。
System.in讀取的時候會出現中文亂碼的問題,則可以通過BufferReader完成讀取功能。

9.BufferedReader類

BufferedReader類用於從緩衝區中讀取內容,所有的輸入字節數據都將放在緩衝區中。
Buffer:表示緩衝區的,緩衝區中的內容可以更改,可以提高效率。
如果要想接收任意長度的數據,而且避免亂碼產生,就可以使用BufferedReader。
這裏寫圖片描述
BufferedReader中定義的構造方法只能接收字符輸入流的實例,所以必須使用字符輸入流和字節輸入流的轉換類InputStreamReader將字節輸入流System.in變爲字符流。
這裏寫圖片描述
(1)鍵盤輸入數據的標準格式
將System,in變爲字符流放入到BufferedReader後,可以通過readLine()方法等待用戶輸入信息。

BufferedReader buf=new BufferedReader(new InputStreamReader(System.in));
String str=null;
System.out.print(“請輸入內容:”);
try{
    str=buf.readLine(); //讀取輸入內容
}catch(IOException e){
    e.printStackTrace();
}
System.out.println(“輸入的內容爲:”+str);

可以發現,程序非但沒有了長度的限制,也可以正確的接收正文了。

(2)相關操作實例
①加法操作
從鍵盤接收過來的內容全部是採用字符串的形式存放的,所以直接將字符串通過包裝類Integer將字符串變爲基本數據類型。
②菜單顯示
菜單類調用操作類,而具體的實際操作由操作類完成。

注:在開發中一定要完成功能,之後再考慮類的設計。
主方法就是一個客戶端,代碼越少越好。

10.Scaner類(★★★)(輸入數據類)

(1)簡介
在JDK1.5之後Java提供了專門的輸入數據類,此類不只可以完成輸入數據操作,也可以方便地對輸入數據進行驗證,此類存放在java.util包中。
常用方法如下:
這裏寫圖片描述
此類不在java.io包中,而在java.util包中,所以此類是一個工具類。
Scanner類中提供了一個可以接收InputStream類型的構造方法,這就表示只要是字節輸入流的子類都可以爲Scanner類實例化,以進行方便的讀取。

(2)使用Scanner類輸入數據
①實現基本的數據輸入
最簡單的數據輸入直接使用Scanner類的next()方法即可。

import java.util.Scanner;
public class ScannerDemo01{
public static void main(String []args){
    Scanner scan=new Scanner(System.in);
    System.out.print(“請輸入數據:”);
    String str=scan.next();
    System.out.println(“輸入的數據爲:”+str);
}
}

以上的程序如果輸入了帶空格的內容,則只能取出空格之前的數據。要想避免這種情況,我們可以試着把分隔符修改爲”\n(空格)”

import java.util.Scanner;
public class ScannerDemo02{
public static void main(String []args){
    Scanner scan=new Scanner(System.in);
    scan.useDelinmiter(“\n”);
    System.out.print(“請輸入數據:”);
    String str=scan.next();
    System.out.println(“輸入的數據爲:”+str);
}
}

以上完成了字符串內容的輸入,如果要輸入int或float類型的數據,在Scanner中也有支持,但是在輸入之前最好先使用hasNextXxx()方法進行驗證。

import java.util.Scanner;
public class ScannerDemo02{
public static void main(String []args){
    Scanner scan=new Scanner(System.in);
    int i=0;
    float f=0.0f;
    System.out.print(“請輸入整數:”);
    if(scan.hasNextInt()){
        i=scan.nextInt();
        System.out.println(“整數數據是:”+i);
    }else{
        System.out.println(“輸入的不是整數”);
    }
    System.out.print(“請輸入小數:”);
    if(scan.hasNextFloat()){
        f=scan.nextFloat();
        System.out.println(“小數數據是:”+f);
    }else{
        System.out.println(“輸入的不是小數”);
    }
}
}

②實現日期格式的數據輸入
在Scanner類中沒有提供專門的日期格式輸入操作,所以要想得到一個日期類型的數據,則必須自己編寫正則驗證,並手工轉換。
③從文件中得到數據
如果要從文件中取得數據,則直接將File類的實例傳入到Scanner的構造方法即可。

11.數據操作流

在IO包中提供了兩個與平臺無關的數據操作流,分別爲數據輸出流(DataOutputStream)和數據輸入流(DataInputStream)。通常數據輸出流會按照一定的格式將數據輸出,再通過數據輸入流按照一定的格式將數據讀入,這樣可以方便地對數據進行處理。
(1)DataOutputStream類
DataOutputStream是OutputStream的子類,此類定義如下:

public class DataOutputStream extends FilterOutputStream implements DataOutput

此類繼承自FilterOutputStream類(FilterOutputStream是OutputStream的子類),同時實現了DataOutput接口,在DataOutput接口定義了一系列的寫入各種數據的方法。
注:只有在對象序列化的時候纔會用到DataOutput接口。

(2)DataInputStream類
DataInputStream是InputStream的子類,專門負責讀取使用DataOutputStream輸出的數據,此類的定義如下:

public class DataInputStream extends FilterInputStream implements DataInt

此類繼承自FilterInputStream類(FilterInputStream是InputStream的子類),同時實現DataInput接口,在DataInput接口中定義了一系列讀入各種數據的方法。
注:只有在對象序列化的時候纔會用到DataInput接口。

12.合併流

合併流的主要功能是將兩個文件的內容合併成一個文件。
如果要實現合併流,則必須使用SequenceInputStream類。
此類的常用方法:
這裏寫圖片描述

13.壓縮流

(1)ZIP壓縮輸入/輸出流簡介
ZIP是一種較爲常見的壓縮格式,在Java中要想實現ZIP的壓縮需要導入java.util.zip包,可以使用此包中的ZipFile、ZipOutputStream、ZipInputStream和ZipEntry這幾個類完成。

JAR及GZIP文件格式的壓縮輸入、輸出流
在Java IO中,不僅可以實現ZIP壓縮格式的輸入、輸出,也可以實現JAR及GZIP文件格式的壓縮:
★JAR壓縮的文件類保存在java,util.jar包中,常用的類有如下幾個:

●JAR壓縮輸出流:JarOutputStream
●JAR壓縮輸入流:JarInputStream
●JAR文件:JARFile
●JAR實體:JAREntry

★GZIP是Unix系統的壓縮文件,在Linux中經常會用到*.gz的文件,就是GZIP格式,GZIP壓縮的支持類保存在java.util.zip包中,常用的類有如下兩個:

●GZIP壓縮輸出流:GZIPOutputStream
●GZIP壓縮輸入流:GZIPInputStream

在每一個壓縮文件中都會存在多個子文件,那麼這每一個的子文件在Java中就使用ZipEntry表示。
ZipEntry類的常用方法:
這裏寫圖片描述
在實例化ZipEntry的時候,要設置名稱,此名稱實際上就是壓縮文件中每一個元素的名稱。

正常情況下在IO操作中,所有的類庫都是在io包中。
壓縮的輸入/輸出流也屬於InputStream或OutputStream的子類,但是卻沒有定義在java.io包中,而是以一種工具類的形式提供的,在操作時還需要使用java.io包的支持。

(2)ZipOutputStream類
如果要完成一個文件或文件夾的壓縮,則要使用ZipOutputStream類。ZipOutputStream是OutputStream的子類。
常用的操作方法:
這裏寫圖片描述

(3)ZipFile類(一個專門表示壓縮文件的類)
在Java中,每一個壓縮文件都可以使用ZipFile表示,還可以使用ZipFile根據壓縮後的文件名稱找到每一個壓縮文件中的ZipEntry並將其進行解壓縮操作。
這裏寫圖片描述

(4)ZipInputStream類
ZipInputStream是InputStream的子類,通過此類可以方便地讀取ZIP格式的壓縮文件。
使用ZipInputStream可以像ZipFile一樣取得ZIP壓縮文件中的每一個ZipEntry。

14.回退流

在Java IO中所有的數據都是採用順序的讀取方式,即對於一個輸入流來講都是採用從頭到尾順序讀取的。如果在輸入流中某個不需要的內容被讀取進來,則只能通過程序將這些不需要的內容處理掉,爲了解決這樣的讀取問題,在Java中提供了一種回退流(PushbackInputStream、PushbackReader),可以把讀取進來的某些數據重新退回到輸入流的緩衝區之中。
回退操作機制:
這裏寫圖片描述

回退操作同樣分爲字節流和字符流。從圖上可以看出通過unread()方法將內容重新送回到輸入流的緩衝區中。
PushbackInputStream類的常用方法:
這裏寫圖片描述
對於回退操作來說,提供了三個unread()方法,這三個操作方法與InputStream類中的read()方法是一一對應的,所以回退完全是針對於輸入流進行操作的。

15.字符編碼

在Java程序的開發中最常見的是ISO8859-1、GBK/GB2312、unicode、UTF編碼。

ISO8859-1:屬於單字節編碼,最多隻能表示0~255的字符範圍,主要在英文上應用。

GBK/GB2312:中文的國際編碼,專門用來表示漢字,是雙字節編碼。如果在此編碼中出現中文,則使用ISO8859-1編碼,GBK可以表示簡體中文和繁體中文,而GB2312只能表示簡體中文,GBK兼容2312。

unicode:Java中使用此編碼方式,是最標準的一種編碼,使用十六進制表示編碼。但此編碼不兼容ISO8859-1編碼。

UTF:由於unicode不支持ISO8859-1編碼,而且容易佔用更多的空間,而且對於英文字母也需要使用兩個字節編碼,這樣使用unicode不便於傳輸和存儲,因此產生了UTF編碼。UTF編碼兼容了ISO8859-1編碼,同時也可以用來表示所有的語言字符,不過UTF編碼是不定長編碼,每一個字符的長度爲1-6個字節不等。一般在中文網頁中使用此編碼,可以節省空間。

如果沒有處理好編碼的事情,則肯定在程序中出現亂碼,如果要避免產生亂碼,則程序的編碼與本地的默認編碼保持一致即可,而如果要知道本地的默認編碼,則可以使用System類即可。

亂碼的產生就一個原因:輸出內容的編碼與接收內容的編碼不一致。

16.對象序列化(★★★★★)

(1)基本簡介
對象序列化就是把一個對象變爲二進制的數據的一種方法。通過對象序列化可以方便地實現對象的傳輸或存儲。
如果一個類的對象想被序列化,則對象所在的類必須實現java.io.Serializable接口。此接口定義:public interface Serializable()
此接口屬於一個標識接口,表示具備了被序列化的能力。

要完成對象的輸入或輸出,還必須依靠對象輸出流(ObjectOutputStream)和對象輸入流(ObjectInputStream)。
使用對象輸出流輸出序列化對象的步驟有時也稱爲序列化,而使用對象輸入流讀入對象的過程有時也稱爲反序列化。
這裏寫圖片描述

(2)對象輸出流ObjectOutputStream
一個對象如果要進行輸出,則必須使用ObjectOutputStream類,此類的定義:
public class ObjectOutputStream extends OutputStream implements ObjectOutput,ObjectStreamConstants
ObjectOutputStream類屬於OutputStream的子類,其常用方法:
這裏寫圖片描述
此類的使用形式與PrintStream非常的相似,在實例化時也需要傳入一個OutputStream的子類對象,之後根據傳入的OutputStream子類的對象不同,輸出的位置也不同。

一個對象被序列化後,到底哪些內容被保存了下來,是屬性還是方法?
只有屬性被序列化。每個對象都具有相同的方法,但是每個對象的屬性不一定相同,也就是說,對象保存的只有屬性信息,那麼在序列化操作時也同樣是這個道理,只有屬性被序列化。

(3)對象輸入流ObjectInputStream
使用ObjectInputStream可以直接把被序列化好的對象反序列化。
ObjectInputStream的定義如下:

public class ObjectInputStream extends InputStream implements ObjectInput ObjectStreamConstants

常用方法:
這裏寫圖片描述
此類也是InputStream的子類,與PrintStream類的使用類似,此類同樣需要接收InputStream類的實例纔可以實例化。

如果用戶想根據自己的需要選擇被序列化的屬性,則可以使用另外一種序列化接口-Externalizable接口。

(4)Externalizable接口
被Serializable接口聲明的類其對象的內容都將被序列化,如果現在用戶希望可以自己指定序列化的內容,則可以讓一個類實現Externalizable接口。此接口定義如下:

public interface Externalizable extends Serializable{
    public void writeExternal(ObjectOutput out) throws IOException
    public void readExternal(ObjectInput in) throws IOException,ClassNotFoundException;
}

Externalizable接口是Serializable接口的子接口,在此接口中定義了兩個方法,這兩個方法如下:

這兩個方法的參數類型是ObjectOutput和ObjectInput,兩個接口的定義如下:
●ObjectOutput接口定義:

public interface ObjectOutput extends DataOutput

●ObjectInput接口定義:

public interface ObjectInput extends DataInput

可以發現這兩個接口分別繼承DataOutput和DataInput,這樣在這兩個方法中就是可以像DataOutputStream和DataInputStream那樣直接輸出和讀取各種類型的數據。

如果一個類要使用Externalizable實現序列化時,在此類時中必須存在一個無參構造方法,因爲在反序列化時會默認調用無參構造實例化對象,如果沒有此無參構造,則運行時將會出現異常,這一點實現機制與Serializable接口是不同的。
這裏寫圖片描述

在開發中使用Serializable接口是最多的,而Externalizable接口基本上是不會出現的。

(5)transient關鍵字
Serializable接口實現的操作實際上是將一個對象中的全部屬性進行序列化,當然也可以使用Externalizable接口以實現部分屬性的序列化,但這樣操作比較麻煩。
當使用Serializable接口實現反序列化操作時,如果一個對象中的某個屬性不希望被序列化,則可以使用transient關鍵字進行聲明。

(6)序列化一組對象
對象輸出時只提供了一個對象的輸出操作(writeObject(Object obj)),並沒有提供多個對象的輸出,所以如果現在要同時序列化多個對象,就可以使用對象數組進行操作。因爲數組屬於引用數組類型,所以可以直接使用Object類型進行接收。
這裏寫圖片描述

總結:

①對象序列化的作用:對象序列化並不一定都向文件中保存,也有可能面向於其他的輸入或輸出。
②被序列化對象的類必須實現Serializable接口,如果某個屬性不希望被保存下來,則可以使用transient關鍵字聲明。
③ObjectOutputStream序列化對象,ObjectInputStream反序列化對象。
④Externalizable接口的作用:開發人員手工實現序列化的操作。
⑤使用序列化保存一組對象的時候要使用對象數組的形式操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章