IO流:物件流、Poperties類
1、物件流OjectInputStream和ObjecOutputStream 可以用它來實現物件的序列化和反序列化,但讀寫的物件必須實現Serializable序列化介面 物件的輸出流將指定的物件寫入到檔案的過程,就是將物件序列化的過程,物件的輸入流將指定序列化好的檔案讀出來的過程,就是物件反序列化的過程 常用構造方法: ObjectOutputStream oos = new ObjectOutputStream(OutputStream out);//建立一個寫入指定OutputStream的ObjectOutputStream物件. ObjectInputStream ois = new ObjectInputStream(InputStream in);//建立從指定 InputStream 讀取的 ObjectInputStream
public class Student implements Serializable{ /** *但是,如果這時候這個obj.txt是我們專案中一個檔案,而專案到後期在原來Student類的基礎上新增成員變數String sex; *private int id; *private String name; *private int age; *private String sex;//新新增成員變數 *這時候如果我們再反序列化,則會引發異常:java.io.InvalidClassException: xuliehua.User; local class incompatible: stream classdesc serialVersionUID = 2161776237447595412, local class serialVersionUID = -3634244984882257127 *serialVersionUID 是用於記錄class檔案的版本資訊的,serialVersionUID這個數字是JVM(JAVA虛擬界)通過一個類的類名、成員、包名、工程名算出的一個數字。 * 而這時候序列化檔案中記錄的serialVersionUID與專案中的不一致,即找不到對應的類來反序列化 *如果序列化與反序列化的時候可能會修改類的成員,那麼最好一開始就給這個類指定一個serialVersionUID,如果一類已經指定的serialVersionUID,然後 * 在序列化與反序列化的時候,jvm都不會再自己算這個 class的serialVersionUID了 */ private static final long serialVersionUID = 1L;
private int id; private String name; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } 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; } public Student(int id, String name, int age) { this.id = id; this.name = name; this.age = age; } public Student() { } public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
public class ObjectInputStream {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException { //序列化 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:\\test\\test02.txt")); oos.writeObject(new Student(1,"小明",24)); oos.writeObject(new Student(2,"小張",25)); oos.writeObject(new Student(3,"小李",26)); oos.writeObject(new Student(4,"小王",27)); oos.writeObject(new Student(5,"小呂",28)); oos.close(); //反序列化 java.io.ObjectInputStream ois = new java.io.ObjectInputStream(new FileInputStream("d:\\test\\test02.txt")); Object o = null; while((o = ois.readObject())!=null) { Student s = (Student)o; System.out.println(s); } ois.close(); }
}
寫入檔案內容: sr IOObject.Student I ageI idL namet Ljava/lang/String;xp t 灝忔槑sq ~ t 灝忓紶sq ~ t 灝忔潕sq ~ t 灝忕帇sq ~ t 灝忓悤 執行列印內容: Student [id=1, name=小明, age=24] Student [id=2, name=小張, age=25] Student [id=3, name=小李, age=26] Student [id=4, name=小王, age=27] Student [id=5, name=小呂, age=28] 反序列化時丟擲java.io.EOFException異常
問題描述:在反序列化物件時,當物件出入流將檔案的全部類反序列化之後,始終會丟擲java.io.EOFException.
原因:java API文件中對於反序列化物件時使用的java.io.ObjectInputStream類的readObject()方法的描述有一句話是"該方法始終會丟擲異常",也就是說該異常無法避免的.
解決方法:
該異常是輸入流已經到結尾了的標誌,我們可以將其捕獲,然後不做任何操作,即結束了該次反序列化操作
public class ObjectStreamTest { public static void main(String[] args) throws IOException, ClassNotFoundException { FileOutputStream out = new FileOutputStream("test3.txt"); ObjectOutputStream outputStream = new ObjectOutputStream(out); outputStream.writeObject(new Student("魏金浩" , 23, 138896)); outputStream.close(); Student i; FileInputStream in = new FileInputStream("test3.txt"); ObjectInputStream inputStream = new ObjectInputStream(in); i = (Student)inputStream.readObject();//容易造成異常的發生,可能讀取超界 System.out.println(i); inputStream.close(); } } 優化程式碼: public static void main(String[] args) throws IOException, ClassNotFoundException { FileOutputStream out = new FileOutputStream("test3.txt"); ObjectOutputStream outputStream = new ObjectOutputStream(out); ArrayList<Student> a = new ArrayList<>(); a.add(new Student("魏金浩" , 23, 138896)); outputStream.writeObject(a); outputStream.close(); ArrayList<Student> b; FileInputStream in = new FileInputStream("test3.txt"); ObjectInputStream inputStream = new ObjectInputStream(in); b = (ArrayList)inputStream.readObject();//我們只需要讀取一次所以不會造成越界 inputStream.close(); for (Student student : b) { System.out.println(student); } }
transient關鍵字: 當你不想要某些欄位序列化時候,可以用transient關鍵字修飾 private int id; private String name; private int age; private transient String sex;//新新增的成員變數//新增關鍵字transient後,序列化時忽略
總結:
1. 如果物件需要被寫出到檔案上,那麼物件所屬的類必須要實現Serializable介面。 Serializable介面沒有任何的方法,是一個標識介面而已。 2. 物件的反序列化建立物件的時候並不會呼叫到構造方法的。 3. serialVersionUID 是用於記錄class檔案的版本資訊的,serialVersionUID這個數字是通過一個類的類名、成員、包名、工程名算出的一個數字。 4. 使用ObjectInputStream反序列化的時候,ObjeectInputStream會先讀取檔案中的serialVersionUID,然後與本地的class檔案的serialVersionUID進行對比,如果這兩個id不一致,反序列則失敗。 5. 如果序列化與反序列化的時候可能會修改類的成員,那麼最好一開始就給這個類指定一個serialVersionUID,如果一類已經指定的serialVersionUID,然後在序列化與反序列化的時候,jvm都不會再自己算這個 class的serialVersionUID了。 6. 如果一個物件某個資料不想被序列化到硬碟上,可以使用關鍵字transient修飾。 7. 如果一個類維護了另外一個類的引用,則另外一個類也需要實現Serializable介面。
2、Poperties類
Java中有個比較重要的類Properties(Java.util.Properties),主要用於讀取Java的配置檔案,各種語言都有自己所支援的配置檔案,配置檔案中很多變數是經常改變的,這樣做也是為了方便使用者,讓使用者能夠脫離程式本身去修改相關的變數設定 在Java中,其配置檔案常為.properties檔案,格式為文字檔案,檔案的內容的格式是“鍵=值”的格式,文字註釋資訊可以用"#"來註釋。 主要的方法: 1. getProperty ( String key),用指定的鍵在此屬性列表中搜索屬性。也就是通過引數 key ,得到 key 所對應的 value。
2. load ( InputStream inStream),從輸入流中讀取屬性列表(鍵和元素對)。通過對指定的檔案(比如說上面的 test.properties 檔案)進行裝載來獲取該檔案中的所有鍵 - 值對。以供 getProperty ( String key) 來搜尋。
3. setProperty ( String key, String value) ,呼叫 Hashtable 的方法 put 。他通過呼叫基類的put方法來設定 鍵 - 值對。
4. store ( OutputStream out, String comments),以適合使用 load 方法載入到 Properties 表中的格式,將此 Properties 表中的屬性列表(鍵和元素對)寫入輸出流。與 load 方法相反,該方法將鍵 - 值對寫入到指定的檔案中去。
5. clear (),清除所有裝載的 鍵 - 值對。該方法在基類中提供。
Java讀取Properties檔案 最常用的還是通過java.lang.Class類的getResourceAsStream(String name)方法來實現,如下可以這樣呼叫: InputStream in = getClass().getResourceAsStream("資源Name");
或者下面這種也常用: InputStream in = new BufferedInputStream(new FileInputStream(filepath)); 綜合例項: public class PopertyTest {
public static void main(String[] args) throws IOException { Properties pp = new Properties(); //讀取檔案中的資訊並寫入Properties FileInputStream f = new FileInputStream("a.poperties"); pp.load(f); String s = pp.getProperty("name"); System.out.println(s); //返回Properties表中的鍵的Set集合 Set<String > set = pp.stringPropertyNames(); Iterator<String> it = set.iterator(); while(it.hasNext()) { String ss= it.next(); System.out.println(ss+"="+pp.getProperty(ss)); } FileOutputStream fos = new FileOutputStream("a.poperties"); //增加Properties中的屬性 pp.setProperty("sex", "女"); //將此 Properties 表中的屬性列表(鍵和元素對)寫入輸出流 pp.store(fos, "alfj"); } }