对象流
对象是存在于内存中的。有时候我们需要将对象保存到硬盘上或者将对象传输到另一台计算机上等,这时需要将对象转换为一个字节序列,而这个过程就成为对象序列化。相反,这样一个字节序列需要将其转换为对应的对象,这个过程成为对象的反序列化。
ObjectOutputStream 实现对象序列化
ObjectOutputStream 是用来对对象进行序列化的输出流,方法如下:
void writeObject(Object b):将给定的对象转换为一个字节序列后写出
ObjectInputStream 实现对象反序列化
ObjectInputStream是用来对对象进行反序列化的输入流,方法如下:
Object readObject():从流中读取字节并转换为对应的对象
Serializable 接口
ObjectOutputStream在对对象进行序列化时有一个要求,就是需要序列化的对象所属的类必须实现Serializable接口。实现该接口不需要重写任何方法,其只是作为可序列化的标志。通常实现该接口的类需要提供一个常量serialVersionUID,表明该类的版本。若不显示的声明,在对象序列化时也会根据当前类的各个方面计算该类的默认serialVersionUID,但不同平台编译器实现有所不同,所以若想跨平台,都应显示的声明版本号。
如果声明的类的对象序列化存到硬盘上面,之后随着需求的变化更改了类的属性(增加或减少或改名),那么当反序列化时,就会出现InvalidClassException,这样会造成不兼容的问题。但当serialVersionUID相同时,它就会将不一样的field以type的预设值反序列化,可避开不兼容性问题。
transient 关键字
对象在序列化后得到的字节序列往往比较大,有时我们在对一个对象进行序列化时可以忽略某些不必要的属性,从而对序列化后得到的字节序列“瘦身”。
transient:被该关键字修饰的属性在序列化时其值将被忽略。
public class Person implements Serializable{
/**
* transient关键字
* 对象在序列化后得到的字节序列往往比较大,有时我们在对一个对象进行序列化时可以忽略某些不必要的属性,从而对序列化后得到的字节序列“瘦身”。
* transient:被该关键字修饰的属性在序列化时其值将被忽略。
*/
private static final long serialVersionUID = 1L;
private String name;
private Integer age;
private char gender;
transient private double salary;
transient private List<String> otherInfo;
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", gender=" + gender + ", salary=" + salary + ", otherInfo="
+ otherInfo + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public char getGender() {
return gender;
}
public void setGender(char gender) {
this.gender = gender;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public List<String> getOtherInfo() {
return otherInfo;
}
public void setOtherInfo(List<String> otherInfo) {
this.otherInfo = otherInfo;
}
}
public class ObjectIO {
/**
* 对象流是一组高级流,作用是方便读写java中任何对象
* 对象输出流,可以写出java对象
* 工作原理:将给定java对象按照其结构转化为一组字节然后写出.
*/
public static void main(String[] args) {
Person person = new Person();
person.setName("Young");
person.setAge(20);
person.setGender('男');
person.setSalary(4000);
List<String> otherList = new ArrayList<String>();
otherList.add("是一位诗人");
otherList.add("也是一位伟人");
person.setOtherInfo(otherList);
System.out.println(person.toString());
serializeObject("D:\\b.txt", person);
deserializeObject("D:\\b.txt");
}
/**
* 序列化对象
*/
public static void serializeObject(String pathName,Object object) {
try (
//初始化 对象输出字节流
ObjectOutputStream output =
new ObjectOutputStream(new FileOutputStream(new File(pathName),false));
){
/*
* 当调用output.writeObject时,实际上做了两件事:
* 将 “object” 对象按照结果转换为了一组字节(对象序列化)
* 然后再将这组字节通过FOS写入到文件中,将数据写入硬盘的过程成为:持久化
* 可能会报错: java.io.NotSerializableException,
* 这是因为对象所属的类没有继承 Serializable 接口,需要继承接口并生成serialVersionUID
*/
output.writeObject(object);
System.out.println("对象序列化完毕");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 反序列化对象
*/
public static void deserializeObject(String pathName) {
try (
ObjectInputStream input =
new ObjectInputStream(new FileInputStream(new File(pathName)));
){
Person p = (Person) input.readObject();
System.out.println(p.toString());
System.out.println("从硬盘反序列化完成");
} catch (Exception e) {
e.printStackTrace();
}
}
}