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");
}
}
IO流:對象流、Poperties類