序列化
ObjectOutputStream類
java.io.ObjectOutputStream類毫目,將java對象的原始數(shù)據(jù)類型寫入到文件,實現(xiàn)對象的持久存儲逾一。
構造方法:
public ObjectOutputStream(OutputStream out):創(chuàng)建一個OutputStream的ObjectOutputStream。構造方法代碼如下:
FileOutputStream fileout=new FileOutputStream("employee.txt");
ObjectOutputStream out=new ObjectOutputStream(fileout);
序列化操作
- 一個對象想要序列化,必須滿足兩個條件:
- 該類必須實現(xiàn)java.io.Serializable接口嫩痰,Serializable是一個標記接口,不實現(xiàn)此接口的類將不會使任何狀態(tài)的序列化或反序列化窍箍,會拋出NotSerializableException
- 該類的所有屬性必須是可序列化的串纺。如果有一個屬性不需要可序列化的,則該屬性 必須注明是瞬態(tài)的椰棘,使用transient關鍵字修飾纺棺。
import java.io.Serializable;
/**
* 序列化和反序列化的時候,會拋出NotSerializableException沒有序列化異常邪狞。
* 類通過實現(xiàn) java.io.Serializable 接口以啟用器序列化功能祷蝌,未實現(xiàn)此接口的類將無法使其任何狀態(tài)序列化和反序列化。
* Serializable也叫標記型接口
* 要序列化和反序列化的類必須實現(xiàn)Serializable接口帆卓,就會給他添加一個標記
* 當我們進行序列化和反序列化的時候巨朦,就會檢測這個類上是否有Serializable標記米丘,有的話就可以序列化和反序列化。沒有就會報錯會拋出NotSerializableException異常糊啡。
*/
public class Student implements Serializable {
private String name;
private int age;
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() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
import org.junit.Test;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/**
* java.io.ObjectOutputStream extends OutputStream
* ObjectOutputStream:對象的序列化流
* 作用:把對象以流的方式寫入到文件中保存
*
* 構造方法:
* ObjectOutputStream(OutputStream out):創(chuàng)建寫入指定OutputStream的ObjectOutputStream
* 參數(shù):
* OutputStream out:字節(jié)輸出流拄查。
* 特有的成員方法:
* void writeObject(Object obj):將指定的對象寫入ObjectOutputStream
* 使用步驟:
* 1.創(chuàng)建ObjectOutputStream對象,構造方法中傳遞字節(jié)輸入流
* 2.使用ObjectOutputStream對象中的writeObject棚蓄,把對象寫入到文件中
* 3.釋放資源
*/
public class DemoObjectStream01 {
@Test
public void testObjectOutputStream() throws IOException {
//創(chuàng)建ObjectOutputStream對象靶累,構造方法中傳遞字節(jié)輸入流
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream("student.txt"));
//使用ObjectOutputStream對象中的writeObject,把對象寫入到文件中
objectOutputStream.writeObject(new Student("橙子",20));
//釋放資源
objectOutputStream.close();
}
}
ObjectInputStream類
ObjectInputStream反序列化流癣疟,將之前使用ObjectOutputStream序列化的原始數(shù)據(jù)恢復對象挣柬。
構造方法:
public ObjectInputStream(InputStream in):創(chuàng)建一個指定的InputStream的ObjectInputStream。
反序列化操作1
如果能找到一個對象的class文件睛挚,我們可以進行反序列化操作邪蛔,調用ObjectinputStream讀取對象的方法。
- public final Object readObject():讀取一個對象
package com.ft.first.objectStream;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
/**
* java.io.ObjectInputStream extends InputStream
* ObjectInputStream:對象反序列化流
* 作用:把文件保存的對象,以流的方式讀取出來使用
*
* 構造方法:
* ObjectInputStream(InputStream in):創(chuàng)建一個指定InputStream讀取的ObjectInputStream
* 參數(shù):
* InputStream in:字節(jié)輸入流。
* 特有的方法:
* Object readObject():從ObjectInputStream讀取對象
* 使用步驟:
* 1.創(chuàng)建ObjectInputStream對象略就,構造方法中傳遞字節(jié)輸入流。
* 2.使用ObjectInputStream對象的方法readObject讀取保存對象的文件
* 3.釋放資源
* 4.使用讀取出來的對象(打印)
* readObject()聲明拋出了ClassNotFoundException(class文件找不到異常)
* 當不存在對象的class文件時拋出此異常
* 反序列化的前期:
* 1.類必須實現(xiàn)Serializable
* 2.必須存在類對應的class文件
*/
public class DemoObjectInputStream01 {
@Test
public void TestObjectInputStream() throws IOException, ClassNotFoundException {
//創(chuàng)建ObjectInputStream對象匠抗,構造方法中傳遞字節(jié)輸入流。
ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream("student.txt"));
//使用ObjectInputStream對象的方法readObject讀取保存對象的文件
Object obj=objectInputStream.readObject();
//釋放資源
objectInputStream.close();
//使用讀取出來的對象(打印)
System.out.println(obj);
Student student=(Student) obj;
System.out.println(student.getName());
System.out.println(student.getAge());
}
}
關鍵字
static關鍵字:靜態(tài)關鍵字污抬。靜態(tài)優(yōu)先于非靜態(tài)加載到內存中汞贸。被static修飾的成員變量不能被序列化的,序列化的都是對象印机。
transient關鍵字:瞬態(tài)關鍵字矢腻。
指定成員變量不想被序列化可以使用transient和static,transient跟static差不多只是沒有static的含義射赛。
package com.ft.first.objectStream;
import java.io.Serializable;
/**
* 序列化和反序列化的時候多柑,會拋出NotSerializableException沒有序列化異常。
* 類通過實現(xiàn) java.io.Serializable 接口以啟用器序列化功能楣责,未實現(xiàn)此接口的類將無法使其任何狀態(tài)序列化和反序列化竣灌。
* Serializable也叫標記型接口
* 要序列化和反序列化的類必須實現(xiàn)Serializable接口,就會給他添加一個標記
* 當我們進行序列化和反序列化的時候秆麸,就會檢測這個類上是否有Serializable標記初嘹,有的話就可以序列化和反序列化。沒有就會報錯會拋出NotSerializableException異常蛔屹。
*
* static關鍵字:靜態(tài)關鍵字削樊。靜態(tài)優(yōu)先于非靜態(tài)加載到內存中豁生。被static修飾的成員變量不能被序列化的兔毒,序列化的都是對象漫贞。
* private static int age;
* 序列化寫入:
* objectOutputStream.writeObject(new Student("橙子",20));
* 反序列化讀取:
* Object obj=objectInputStream.readObject();
* 結果為:Student{name='橙子', age=0}
*
* transient關鍵字:瞬態(tài)關鍵字育叁。
* 被transient修飾的成員變量迅脐,不能被序列化
* private transient int age;
* 序列化寫入:
* objectOutputStream.writeObject(new Student("橙子",20));
* 反序列化讀取:
* Object obj=objectInputStream.readObject();
* 結果為:Student{name='橙子', age=0}
*/
public class Student implements Serializable {
private String name;
private transient int age;
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() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
反序列化操作2
另外豪嗽,當JVM反序列化對象時谴蔑,能找到class文件,但是class文件在序列化對象之后發(fā)生了修改龟梦,那么反序列化操作也會失敗隐锭,拋出一個InvalidClassException異常。發(fā)生這個異常的原因如下:
- 該類的序列化版本號與從流中讀取的類描述符的版本號不配匹计贰。
- 該類包含未知數(shù)據(jù)類型钦睡。
- 該類沒有可訪問的無參數(shù)構造方法
Serializable接口給需要序列化的類,提供了一個序列版本號躁倒。serialVersionUID該版本號的目的在于驗證序列化的對象和對應類是否版本配匹荞怒。
問題:
每次修改類的定義,都會給class文件生成一個新的序列號秧秉。
解決方案:
無論是否對類的定義是否進行修改褐桌,都不重新生成新的序列號∠笥可以手動給類添加一個序列號荧嵌。
格式在Serialzable接口規(guī)定:
可序列號類可以通過聲明為"serialVersionUID"的字段(該字段必須是靜態(tài)(static)、最終(final)的long型字段)顯示聲明其自己的serialVersionUID砾淌。
public class Student implements Serializable {
//定義這個類的serialVersionUID=42L; 不會出現(xiàn)序列號沖突
static final long serialVersionUID=42L; //常量不能改變
private String name;
//private transient int age;
private int age;
序列化集合
- 將存有多個自定義對象的集合序列化操作完丽,保存到list.txt文件中。
- 反序列化list.txt拇舀,并遍歷集合逻族,打印對象信息。
案例分析
- 把若干個學生對象骄崩,保存到集合中聘鳞。
- 把集合序列化。
- 反序列化讀取時要拂,只需要讀取一次抠璃,轉換為集合類型。
- 遍歷集合脱惰,可以打印所有學生信息搏嗡。
案例實現(xiàn)
import org.junit.Test;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* 練習:序列化集合
* 當我們想在文件中保存多個對象的時候,可以把多個對象存儲到一個集合中,對集合進行序列化和反序列化
* 分析:
* 1.定義一個存儲Student對象的ArrayList集合
* 2.往ArrayList集合中存儲Student對象
* 3.創(chuàng)建一個序列化流ObjectOutputStream對象
* 4.使用ObjectOutputStream對象中的方法writeObject采盒,對集合進行序列化
* 5.創(chuàng)建一個反序列化ObjectInputStream對象
* 6.使用ObjectInputStream對象中的方法readObject旧乞,讀取文件中保存的集合
* 7.把Object類型的集合轉換為ArrayList類型
* 8.遍歷ArrayList集合
* 9.釋放資源
*/
public class DemoObjectInputStream02 {
@Test
public void testListStudent() throws IOException, ClassNotFoundException {
//定義一個存儲Student對象的ArrayList集合
List list = new ArrayList();
//往ArrayList集合中存儲Student對象
list.add(new Student("張三", 18));
list.add(new Student("李四", 19));
list.add(new Student("王五", 20));
//創(chuàng)建一個序列化流ObjectOutputStream對象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("listStudent.txt"));
//使用ObjectOutputStream對象中的方法writeObject,對集合進行序列化
objectOutputStream.writeObject(list);
//創(chuàng)建一個反序列化ObjectInputStream對象
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("listStudent.txt"));
//使用ObjectInputStream對象中的方法readObject磅氨,讀取文件中保存的集合
Object obj = objectInputStream.readObject();
//把Object類型的集合轉換為ArrayList類型
ArrayList<Student> list1=(ArrayList<Student>)obj;
//遍歷ArrayList集合
for(Student s:list1){
System.out.println(s);
}
//釋放資源
objectInputStream.close();
objectOutputStream.close();
}
}
打印流
概述
平時我們在控制臺打印輸出尺栖,是調用print方法和println方法完成的,這兩個方法都是來自于java.io.PrintStream類烦租,該類能夠方便地打印各種數(shù)據(jù)類型的值延赌,是一種便捷的輸出方式。
最常見的:
System.out.println(" ");
PrintStream類
構造方法:
- public PrintStream(String fileName):使用指定的文件名創(chuàng)建一個新的打印流叉橱。
構造舉例挫以,代碼如下:
PrintStream ps=new PrintStream("ps.txt");
改變打印流方向
System.out就是PrintStream類型的,只不過它的流向是系統(tǒng)規(guī)定的窃祝,打印在控制臺上屡贺。不過,既然是流對象锌杀,我們就可以玩一個"小把戲"甩栈,改變它的流向。
import org.junit.Test;
import java.io.FileNotFoundException;
import java.io.PrintStream;
/**
* java.io.PrintStream:打印流
* PrintStream 為其他輸出流添加了功能糕再,使他們能夠方便地打印各種數(shù)據(jù)值表示形式量没。
* PrintStream特點:
* 1.只負責數(shù)據(jù)的輸出,不負責數(shù)據(jù)的讀取
* 2.與其他輸出流不同突想,PrintStream永遠不會拋出IOException,但會拋出文件找不到的異常
* 3.有特有的方法殴蹄,print、println
* print:(輸出任意的數(shù)據(jù)格式)
* println:(輸出任意的數(shù)據(jù)格式帶換行)
* 構造方法:
* PrintStream(File file):輸出的目的地是文件
* PrintStream(OutputStream out):輸出的目的地是一個輸出流
* PrintStream(String FileName):輸出的目的地是一個文件路徑
* PrintStream extends OutputStream
* 繼承自父類的成員方法:
* void close():關閉此輸出流并釋放與此流有關的所有系統(tǒng)資源猾担。
* void flush():刷新此輸出流并強制寫出所有緩沖的輸出字節(jié)袭灯。
* void write(byte[] b):將 b.length 個字節(jié)從指定的 byte 數(shù)組寫入此輸出流。
* void write(byte[] b, int off, int len):將指定 byte 數(shù)組中從偏移量 off 開始的 len 個字節(jié)寫入此輸出流绑嘹。
* abstract void write(int b):將指定的字節(jié)寫入此輸出流稽荧。
* 注意:
* 如果使用繼承自父類的write方法寫數(shù)據(jù),那么查詢數(shù)據(jù)的時候會查詢編碼表工腋。例:97->a
* 如果使用它自己的方法print姨丈、println寫數(shù)據(jù),寫的數(shù)據(jù)會原樣輸出.例:97->97
*/
public class OutStream {
@Test
public void testOut() throws FileNotFoundException {
//創(chuàng)建打印流PrintStream對象擅腰,構造方法中綁定要輸出的目的地
PrintStream printStream=new PrintStream("print.txt");
//如果使用繼承自父類的write方法寫數(shù)據(jù)蟋恬,那么查詢數(shù)據(jù)的時候會查詢編碼表。例:97->a
printStream.write(97);
//如果使用它自己的方法print趁冈、println寫數(shù)據(jù)歼争,寫的數(shù)據(jù)會原樣輸出.例:97->97
printStream.println(97);
//釋放資源
printStream.close();
}
/**
* 可以改變輸出語句的目的地(打印的流向)
* 輸出語句拜马,默認在控制臺輸出
* 使用:System.setOut方法改變輸出語句的目的地改為參數(shù)中傳遞的打印流的目的地。
* static void setOut(PrintStream out):重新分配"標準"輸出流沐绒。
*/
@Test
public void testOut01() throws FileNotFoundException {
System.out.println("我是在控制臺打印輸出");
PrintStream printStream=new PrintStream("打印流.txt");
//把輸出語句的目的地改變?yōu)榇蛴×鞯哪康牡? System.setOut(printStream);
System.out.println("我在打印流的目的地中輸出");
printStream.close();
}
}