Java 基礎 (IO 物件流,隨機儲存檔案流)
阿新 • • 發佈:2021-11-06
IO 物件流
ObjectInputStream 和 OjbectOutputSteam
用於儲存和讀取基本資料型別資料或物件的處理流。它的強大之處就是可以把Java中的物件寫入到資料來源中,也能把物件從資料來源中還原回來。
- 序列化: 用 ObjectOutputStream 類儲存基本型別資料或物件的機制
- 反序列化: 用 ObjectInputStream 類讀取基本型別資料或物件的機制
- ObjectOutputStream 利 ObjectInputStream 不能序列化 static 和transient 修飾的成員變數
物件序列化機制允許把記憶體中的 Java 物件轉換成平臺無關的二進位制流,從而允許把這種二進位制流持久地儲存在磁碟上,或通過網路將這種二進位制流傳輸到另一個網路節點。當其它程式獲取了這種二進位制流,就可以恢復成原來的 Java 物件
序列化的好處在於可將任何實現了 Serializable 介面的物件轉化為位元組資料,使其在儲存和傳輸時可被還原
如果需要讓某個物件支援序列化機制,則必須讓物件所屬的類及其屬性是可序列化的,為了讓某個類是可序列化的,該類必須實現如下兩個介面之一。否則,會丟擲 NotSerializableException 異常
> Serializable
> Externalizable
凡是實現 Serializable 介面的類都有一個表示序列化版本識別符號的靜態變數;
> private static final long serialVersionUID; > serialVersionUID 用來表明類的不同版本間的相容性。簡言之,其目的是以序列化物件進行版本控制,有關各版本反序列化時是否相容。 > 如果類沒有顯示定義這個靜態常量,它的值是Java執行時環境根據類的內部細節自動生成的。若類的例項變數做了修改,seriaVersionUID 可能發生變化。故建議,顯式宣告。
Person.java
package com.klvchen.java; import java.io.Serializable; public class Person implements Serializable { public static final long serialVersionUID = 475684125L; private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
ObjectInputOutputStreamTest.java
package com.klvchen.java;
import org.junit.Test;
import java.io.*;
public class ObjectInputOutputStreamTest {
/*
序列化過程:將記憶體中的 java 物件儲存到磁碟中或通過網路傳輸出去
使用 ObjectOutputStream 實現
*/
@Test
public void testObjectOutputStream(){
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
oos.writeObject(new String("我愛北京天安門"));
oos.flush();
oos.writeObject(new Person("wangyi", 23));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
反序列化:將磁碟檔案中的物件還原為記憶體中的一個 java 物件
使用ObjectInputStream 來實現
*/
@Test
public void testObjectInputStream(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj = ois.readObject();
String str = (String) obj;
Person p = (Person) ois.readObject();
System.out.println(str);
System.out.println(p);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
IO 隨機儲存檔案流 RandomAccessFile
* RandomAccessFile 宣告在 java.io 包下,但直接繼承於java.lang.Object類。並且它實現了 DataInput、DataOutput 這兩個介面,也就意味著這個類既可以讀也可以寫。
* RandomAccessFile 類支援“隨機訪問”的方式,程式可以直接跳到檔案的任意地方來讀、寫檔案
> 支援只訪問檔案的部分內容
> 可以向已存在的檔案後追加內容
* RandomAccessFile 物件包含一個記錄指標,用以標示當前讀寫處的位置。RandomAccessFile 類物件可以自由移動記錄指標:
> long getFilePointer(): 獲取檔案記錄指標的當前位置
> void seek(long pos): 將檔案記錄指標定位到 pos 位置
* 構造器
public RandomAccessFile(File file,String mode)
public RandomAccessFile(String name,String mode)
* 建立 RandomAccessFile 類例項需要指定一個 mode引數,該引數指定RandomAccessFile 的訪問模式:
>r: 以只讀方式開啟
>rw: 開啟以便讀取和寫入
>rwd: 開啟以便讀取和寫入;同步檔案內容的更新
>rws: 開啟以便讀取和寫入;同步檔案內容和元資料的更新
* 如果模式為只讀r。則不會建立檔案,而是會去讀取一個已經存在的檔案,如果讀取的檔案不存在則會出現異常。如果模式為rw讀寫。如果檔案不存在則會去建立檔案,如果存在則不會建立。
如果 RandomAccessFile 作為輸出流時,寫出到的檔案如果不存在,則在執行過程中自動建立如果寫出到的檔案存在,則會對原有檔案內容進行覆蓋。(預設情況下,從頭覆蓋>|
hello.txt
abcdefghijklm
RandomAccessFileTest.java
package com.klvchen.java;
import org.junit.Test;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
public class RandomAccessFileTest {
@Test
public void test1() {
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
raf1 = new RandomAccessFile(new File("1.png" ),"r");
raf2 = new RandomAccessFile(new File("2.png"), "rw");
byte[] buffer = new byte[1024];
int len;
while ((len = raf1.read(buffer)) != -1) {
raf2.write(buffer, 0 ,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (raf1 != null) {
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (raf2 != null) {
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void test2() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile("hello.txt", "rw");
raf1.seek(3); //將指標調到角標為3的位置
//儲存指標3後面的所有資料到StringBuilder中
StringBuilder builder = new StringBuilder((int) new File("hello.txt").length());
byte[] buffer = new byte[20];
int len;
while ((len = raf1.read(buffer)) != -1) {
builder.append(new String(buffer, 0, len));
}
//調回指標,寫入"xyz"
raf1.seek(3);
raf1.write("xyz".getBytes());
//將StringBuilder中的資料寫入到檔案中
raf1.write(builder.toString().getBytes());
raf1.close();
}
}