Java I/O
I/O
流,用來實現程式或程序間的通訊,或讀寫外圍裝置、外部檔案等。Java把一切輸入抽象成輸入流,把一切輸出抽象成輸出流(也可以根據流方向來定義,流向內部的是輸入流,流向外部的是輸出流)。
Java的I/O架構由輸入流/輸出流/File/Serializable四部分組成。
一、File
File是檔案和目錄路徑名的抽象表示。File中定義了對檔案和目錄的基本操作:
- 路徑相關 如getAbsolutePath()
- 檔案屬性 如getParent()、getName()等
- 檔案讀寫許可權 如canRead()、setWritable、setReadable
- 檔案的建立、刪除 如 createNewFile、delete(當刪除某一目錄時,必須保證該目錄下沒有其他檔案才能正確刪除,否則將刪除失敗)
- 建立資料夾 mkdir(建立單級目錄)、mkdirs(建立多級目錄)
- 獲取路徑下的檔案、資料夾 list(只會列出路徑下的檔案、資料夾名稱)、listFiles(列出路徑下的檔案)
二、Serializable
Serializable是Java用來進行序列化的。序列化就是把Java物件轉換為位元組序列的過程。序列化之後位元組序列可以用於儲存和傳輸(儲存和傳輸就涉及到了I/O操作)。
在Java中只要讓需要序列化的類實現Serializable即可,同時Android提供了另一種序列化方式Parcelable介面,最後就是網路通訊常用的json,以上就是常見的序列化方式。
序列化-Serializable
序列化:
Bean bean = new Bean(); bean.setIndex(100); bean.setName("number"); FileOutputStream outputStream = new FileOutputStream(file); objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(bean); objectOutputStream.close()
通過上述程式碼我們可以將bean序列化並寫入file中。通過分析原始碼我們瞭解序列化過程如下:
- 寫入流的頭資訊
頭資訊包含兩個欄位他們是在ObjectStreamConstants類中定義的
/**
* Magic number that is written to the stream header.
*/
final static short STREAM_MAGIC = (short)0xaced;
/**
* Version number that is written to the stream header.
*/
final static short STREAM_VERSION = 5;
頭資訊存放在序列化後的位元組序列的頭部,在反序列化時會校驗其值。
-
之後呼叫writeObject開始真正的序列化流程,在writeObject會先獲取傳入的要序列化物件的本地描述並封裝到一個名叫ObjectStreamClass的類中,該類例項在構建的時候會儲存諸如類名、是否實現Serializable介面、傳入類的serialVersionUID、傳入類的欄位屬性資訊、writeObjectMethod 、readObjectMethod等等。其中很多資訊都是通過反射獲取的。
-
獲取本地描述例項之後會把這些資訊寫入流中,此時並未寫物件的資訊,即欄位的值。
-
最後寫入物件資訊,寫的時候會先寫基本型別的欄位值再寫非基本型別欄位值
至此序列化過程就完成了。
反序列化-Serializable
- 首先會先檢查位元組序列頭部資訊是否匹配
- 之後呼叫readObject() 讀取流中的位元組,首先會讀取到類的描述資訊ObjectStreamClass 物件,然後利用該物件記憶體儲處的資訊去例項化物件(反射建立物件未使用構造器)然後給物件欄位進行賦值,完成後返回反序列化後的物件。
注意:
靜態變數無法序列化
transient關鍵字修飾的變數無法序列化
多次序列化同一物件,內部只會序列化一次之後再次序列化會返回已序列化的編號。
Parcelable
使用:
public class ABean implements Parcelable { private int index; private String name; protected ABean(Parcel in) { index = in.readInt(); name = in.readString(); } //反序列化功能由CREATOR完成,在CREATOR的內部標明的如何建立序列物件和陣列 public static final Creator<ABean> CREATOR = new Creator<ABean>() { @Override //從Parcel中反序列化物件 public ABean createFromParcel(Parcel in) { //其內部呼叫Parcel的一系列readXXX方法實現反序列化過程 return new ABean(in); } @Override public ABean[] newArray(int size) { return new ABean[size]; } }; public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public String getName() { return name == null ? "" : name; } public void setName(String name) { this.name = name; } @Override public int describeContents() { return 0; } //序列化過程: //重寫writeToParcel方法,我們要在這裡逐一對需要序列化的屬性用Parcel的一系列writeXXX方法寫入 @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(index); dest.writeString(name); } }
上面是基本使用,首先要實現Parcelable介面以及其要求實現的兩個方法describeContents、writeToParcel,然後還需要定義一個名字為CREATOR的 Creator物件。
序列化是通過writeToParcel方法實現的,在該方法內部會先寫入傳入類的類名然後呼叫我們實現的writeToParcel方法按順序寫入。
反序列化是通過readParcelable方法實現的,在該方法內部會先通過反射獲取我們在類內部定義的CREATOR,然後呼叫CREATOR的createFromParcel來建立例項物件,這樣就完成了反序列化。
參考連結:
https://blog.csdn.net/willway_wang/article/details/106745481
https://www.cnblogs.com/wangle12138/p/8257016.html
三、輸入輸出流
分類:
- 按資料流的方向不同分類:輸入流、輸出流
- 按最小的資料單元分類:位元組流、字元流
- 按照功能不同分成節點流和處理流
輸入流
Java中所有輸入流都是InputStream或Reader的子類。
輸出流
所有輸出流都是OutputStream或Writer的子類。
位元組流
位元組流是指以 8 位(即 1 byte,8 bit)作為一個數據單元,資料流中最小的資料單元是位元組的流。InputStream/OutputStream 是位元組流的基類。
InputStream
輸入位元組流基類,其子類有FileInputStream、DataInputStream、BufferedInputStream、ByteArrayInputStream、ObjectInputStream等
- public abstract int read( ):讀取一個byte的資料,返回值是高位補0的int型別值。若返回值=-1說明沒有讀取到任何位元組讀取工作結束。
- public int read(byte b[ ]):讀取b.length個位元組的資料放到b陣列中。返回值是讀取的位元組數。該方法實際上是呼叫下一個方法實現的
- public int read(byte b[ ], int off, int len):從輸入流中最多讀取len個位元組的資料,存放到偏移量為off的b陣列中。
- public int available( ):返回輸入流中可以讀取的位元組數。注意:若輸入阻塞,當前執行緒將被掛起,如果InputStream物件呼叫這個方法的話,它只會返回0,這個方法必須由繼承InputStream類的子類物件呼叫才有用,
- public long skip(long n):忽略輸入流中的n個位元組,返回值是實際忽略的位元組數, 跳過一些位元組來讀取
- public int close( ) :我們在使用完後,必須對我們開啟的流進行關閉.
OutputStream
輸出位元組流基類,其子類有FileOutputStream、DataOutputStream、BufferedOutputStream、ByteArrayOutputStream、ObjectOutputStream等
- public void write(byte b[ ]):將引數b中的位元組寫到輸出流。
- public void write(byte b[ ], int off, int len) :將引數b的從偏移量off開始的len個位元組寫到輸出流。
- public abstract void write(int b) :先將int轉換為byte型別,把低位元組寫入到輸出流中。
- public void flush( ) : 將資料緩衝區中資料全部輸出,並清空緩衝區。
- public void close( ) : 關閉輸出流並釋放與流相關的系統資源。
字元流
Reader
字元輸入流基類,其子類有BufferedReader、InputStreamReader、StringReader等
- public int read() throws IOException; //讀取一個字元,返回值為讀取的字元
- public int read(char cbuf[]) throws IOException;/讀取一系列字元到陣列cbuf[]中,返回值為實際讀取的字元的數量/
- public abstract int read(char cbuf[],int off,int len) throws IOException;
Writer
字元輸出流基類,其子類有BufferedWriter、OutputStreamWriter、StringWriter等
- public void write(int c) throws IOException;//將整型值c的低16位寫入輸出流
- public void write(char cbuf[]) throws IOException;//將字元陣列cbuf[]寫入輸出流
- public abstract void write(char cbuf[],int off,int len) throws IOException;//將字元陣列cbuf[]中的從索引為off的位置處開始的len個字元寫入輸出流
- public void write(String str) throws IOException;//將字串str中的字元寫入輸出流
- public void write(String str,int off,int len) throws IOException;//將字串str 中從索引off開始處的len個字元寫入輸出流
節點流
Java根據流是否直接處理資料分為節點流和處理流,節點流是真正直接處理資料的流,在使用完畢後需要關閉。FileInputStream,FileOutputStrean,FileReader,FileWriter,StringReader,StringWriter等都是節點流。
處理流
處理流是裝飾加工節點流的,它是對一個已存在的流的連線和封裝,通過所封裝的流的功能呼叫實現資料讀寫。BufferedImputStrean,BufferedOutputStream,BufferedReader ,BufferedWriter,InputStreamReader,OutputStreamWriter等都是處理流。
比如Bufferedxxx流都是帶緩衝的流
最後補充一個圖(來源於網路):
四、RandomAccessFile
它是Java提供的隨機存取檔案內容的類,在建構函式中傳入所需許可權(讀、寫等)。其提供了seek函式,該函式可以把檔案游標定位到指定位置從而從此開始讀寫檔案內容。它還提供readInt、readDobule、writeInt等讀寫基本資料型別的方法。底層原理是記憶體對映檔案方式(nio包),即把檔案對映到記憶體後再操作,省去了頻繁磁碟io( 待驗證 )。
RandomAccessFile簡介與使用_LJHSkyWalker的部落格-CSDN部落格_randomaccessfile