FYI
ObjectInputStream 和 ObjectOutputStream 的作用是,对基本数据和对象进行序列化操作支持。
创建“文件输出流”对应的ObjectOutputStream对象,该ObjectOutputStream对象能提供对“基本数据或对象”的持久存储;当我们需要读取这些存储的“基本数据或对象”时,可以创建“文件输入流”对应的ObjectInputStream,进而读取出这些“基本数据或对象”。
注意: 只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能被ObjectInputStream/ObjectOutputStream所操作!
ObjectOutputStream 函数列表
// 构造函数
ObjectOutputStream(OutputStream output)
// public函数
void close()
void defaultWriteObject()
void flush()
ObjectOutputStream.PutField putFields()
void reset()
void useProtocolVersion(int version)
void write(int value)
void write(byte[] buffer, int offset, int length)
void writeBoolean(boolean value)
void writeByte(int value)
void writeBytes(String value)
void writeChar(int value)
void writeChars(String value)
void writeDouble(double value)
void writeFields()
void writeFloat(float value)
void writeInt(int value)
void writeLong(long value)
final void writeObject(Object object)
void writeShort(int value)
void writeUTF(String value)
void writeUnshared(Object object)
ObjectInputStream 函数列表
// 构造函数
ObjectInputStream(InputStream input)
int available()
void close()
void defaultReadObject()
int read(byte[] buffer, int offset, int length)
int read()
boolean readBoolean()
byte readByte()
char readChar()
double readDouble()
ObjectInputStream.GetField readFields()
float readFloat()
void readFully(byte[] dst)
void readFully(byte[] dst, int offset, int byteCount)
int readInt()
String readLine()
long readLong()
final Object readObject()
short readShort()
String readUTF()
Object readUnshared()
int readUnsignedByte()
int readUnsignedShort()
synchronized void registerValidation(ObjectInputValidation object, int priority)
int skipBytes(int length)
演示程序
import java.io.*;
import java.util.Arrays;
import java.util.HashMap;
/**
* 对象流演示
* ObjectInputStream 和 ObjectOutputStream 测试程序
* <p/>
* 注意:通过ObjectInputStream, ObjectOutputStream操作的对象,必须是实现了Serializable或Externalizable序列化接口的类的实例。
*/
public class ObjectStreamTest {
private static String FILENAME = "t.tmp";
public static void main(String[] args) throws IOException, ClassNotFoundException {
//baseapi();
testWriteApis();
testReadApis();
}
public static void testReadApis() {
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(FILENAME));
byte readByte = objectInputStream.readByte();
System.out.printf("byte = %s \n", readByte & 0xff);
System.out.printf("int = %d \n", objectInputStream.readInt());
System.out.printf("char = %s \n", objectInputStream.readChar());
System.out.printf("boolean = %s \n", objectInputStream.readBoolean());
System.out.printf("hashmap = %s \n", objectInputStream.readObject());
Box box = (Box) objectInputStream.readObject();
System.out.printf("box = %s \n", box);
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void testWriteApis() {
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(FILENAME));
objectOutputStream.writeByte((byte) 100);
objectOutputStream.writeInt(200);
// objectOutputStream.writeInt(200);
objectOutputStream.writeChar('a');
objectOutputStream.writeBoolean(true);
HashMap hashMap = new HashMap();
hashMap.put("one", 1);
hashMap.put("two", 2);
hashMap.put("three", 3);
objectOutputStream.writeObject(hashMap);
Box box = new Box();
box.setAge(11);
box.setName("iii");
box.setTmpname("ttt");
objectOutputStream.writeObject(box);
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private static class Box implements Serializable {
private static String staticName = "static";
private String name;
private int age;
private transient String tmpname;
public static String getStaticName() {
return staticName;
}
public static void setStaticName(String staticName) {
Box.staticName = staticName;
}
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 String getTmpname() {
return tmpname;
}
public void setTmpname(String tmpname) {
this.tmpname = tmpname;
}
@Override
public String toString() {
return "Box{" +
"name='" + name + '\'' +
", age=" + age +
", tmpname='" + tmpname + '\'' +
'}';
}
}
/*
private static void baseapi() throws IOException, ClassNotFoundException {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("object.txt"));
Employee[] employees = new Employee[3];
Employee employee1 = new Employee("thushear", 18, 1000);
Employee employee2 = new Employee("thushear", 22, 3000);
Employee manager = new Employee("wang", 33, 11000);
employee2.setManager(manager);
employee1.setManager(manager);
employees[0] = manager;
employees[1] = employee1;
employees[2] = employee2;
objectOutputStream.writeObject(employees);
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("object.txt"));
Employee[] employees1 = (Employee[]) objectInputStream.readObject();
System.out.println(Arrays.toString(employees1));
for (Employee employee : employees1) {
}
}*/
}
序列化的作用和用途
序列化,就是为了保存对象的状态;而与之对应的反序列化,则可以把保存的对象状态再读出来。
简言之:序列化/反序列化,是Java提供一种专门用于的保存/恢复对象状态的机制。
一般在以下几种情况下,我们可能会用到序列化:
a)当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;
b)当你想用套接字在网络上传送对象的时候;
c)当你想通过RMI传输对象的时候。
我们在介绍序列化定义时,说过“序列化/反序列化,是专门用于的保存/恢复对象状态的机制”。
从中,我们知道:序列化/反序列化,只支持保存/恢复对象状态,即仅支持保存/恢复类的成员变量,但不支持保存类的成员方法!
但是,序列化是不是对类的所有的成员变量的状态都能保存呢?
答案当然是否定的!
(01) 序列化对static和transient变量,是不会自动进行状态保存的。
transient的作用就是,用transient声明的变量,不会被自动序列化。
(02) 对于Socket, Thread类,不支持序列化。若实现序列化的接口中,有Thread成员;在对该类进行序列化操作时,编译会出错!
这主要是基于资源分配方面的原因。如果Socket,Thread类可以被序列化,但是被反序列化之后也无法对他们进行重新的资源分配;再者,也是没有必要这样实现。
演示程序
/**
* Created by thushear on 2015/8/30.
*/
import java.io.*;
import java.net.Socket;
/**
* 序列化
*/
public class SerialTest {
static String FILE = "serial.txt";
static String EXT_FILE = "extserial.txt";
static Serial serial = new Serial("thushear",111,222,333);
public static void main(String[] args) {
testWriteObject();
testReadObject();
// testExtWriteObject();
testWriteExternalObject();
testReadExternalObject();
}
public static void testReadObject() {
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(FILE));
Serial serial = (Serial) objectInputStream.readObject();
// Serial{weight=0, name='thushear', age=111,height = 222} weight 是初始默认值 height是因为static变量公用
// Serial{weight=333, name='thushear', age=111,height = 222} 手动扩展后 transient 和 static 都可以序列化
System.out.printf("serial = %s \n" , serial);
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static void testWriteObject() {
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(FILE));
objectOutputStream.writeObject(serial);
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void testExtWriteObject () {
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(FILE));
objectOutputStream.writeObject(new ExtSerial());
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void testWriteExternalObject() {
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(EXT_FILE));
objectOutputStream.writeObject(new ExternalSerial("thushear",111));
objectOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void testReadExternalObject() {
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(EXT_FILE));
ExternalSerial externalSerial = (ExternalSerial) objectInputStream.readObject();
System.out.printf("serial = %s \n" , externalSerial);
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/**
* 序列化的第二种方式 实现 Externalizable
*/
class ExternalSerial implements Externalizable {
public ExternalSerial() {
}
private String name ;
private int age ;
public ExternalSerial(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = (String) in.readObject();
age = in.readInt();
}
@Override
public String toString() {
return "ExternalSerial{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
/**
* 资源型的对象无法序列化
* java.io.NotSerializableException:
*/
class ExtSerial implements Serializable {
Thread thread = new Thread(){
@Override
public void run() {
System.out.println("serial thread runs");
}
};
Socket socket = new Socket();
}
/**
* static transient
* 实现Serializable接口才可以被序列化
*/
class Serial implements Serializable{
transient int weight ;
static int height ;
String name ;
int age ;
public Serial( String name, int age , int height , int weight) {
this.weight = weight;
this.name = name;
this.age = age;
this.height = height;
}
/**
* 手动序列化
* @param objectOutputStream
* @throws IOException
*/
private void writeObject(ObjectOutputStream objectOutputStream) throws IOException {
objectOutputStream.defaultWriteObject();
objectOutputStream.writeInt(weight);
objectOutputStream.writeInt(height);
}
/**
* 手动序列化
* @param objectInputStream
* @throws IOException
* @throws ClassNotFoundException
*/
private void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException {
objectInputStream.defaultReadObject();
weight = objectInputStream.readInt();
height = objectInputStream.readInt();
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public static int getHeight() {
return height;
}
public static void setHeight(int height) {
Serial.height = height;
}
public Serial(String name, int age) {
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Serial{" +
"weight=" + weight +
", name='" + name + '\'' +
", age=" + age +
",height = " + height +
'}';
}
}
程序说明:
“序列化不会自动保存static和transient变量”,因此我们若要保存它们,则需要通过writeObject()和readObject()去手动读写。
(01) 通过writeObject()方法,写入要保存的变量。writeObject的原始定义是在ObjectOutputStream.java中,我们按照如下示例覆盖即可:
private void writeObject(ObjectOutputStream out) throws IOException{
out.defaultWriteObject();// 使定制的writeObject()方法可以利用自动序列化中内置的逻辑。
out.writeInt(ival); // 若要保存“int类型的值”,则使用writeInt()
out.writeObject(obj); // 若要保存“Object对象”,则使用writeObject()
}
(02) 通过readObject()方法,读取之前保存的变量。readObject的原始定义是在ObjectInputStream.java中,我们按照如下示例覆盖即可:
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException{
in.defaultReadObject(); // 使定制的readObject()方法可以利用自动序列化中内置的逻辑。
int ival = in.readInt(); // 若要读取“int类型的值”,则使用readInt()
Object obj = in.readObject(); // 若要读取“Object对象”,则使用readObject()
}
Externalizable和完全定制序列化过程
如果一个类要完全负责自己的序列化,则实现Externalizable接口,而不是Serializable接口。
Externalizable接口定义包括两个方法writeExternal()与readExternal()。需要注意的是:声明类实现Externalizable接口会有重大的安全风险。writeExternal()与readExternal()方法声明为public,恶意类可以用这些方法读取和写入对象数据。如果对象包含敏感信息,则要格外小心。
下面,我们修改之前的SerialTest1.java测试程序;将其中的Box由“实现Serializable接口” 改为 “实现Externalizable接口”。