1. 程式人生 > >IO流:物件流、Poperties類

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");     }    }