勿以恶小而为之,勿以善小而不为--------------------------刘备

劝诸君,多行善事积福报,莫作恶

上一章简单介绍了 DataOutputStream和DataInputStream(八),如果没有看过,​​请观看上一章​​

一. 为什么要序列化?

上一章节写入文件的员工列表信息数据:

id

name

sex

age

desc

1

老蝴蝶


20

一个好人

2

蝴蝶


21

一个人

3

岳泽霖


22

快乐的人

4

岳建立


23

想要快乐的人

通过 DataOutputStream 和 DataInputStream ,发现太复杂了,需要固定的格式进行处理, 当数据信息复杂时,就疯了。

我们发现,员工的信息可以封装成一个对象,如 Person 对象, 该对象里面有 id,name,sex,age,desc 属性。

public class Person {

private int id;
private String name;
private char sex;
private int age;
private String desc;


public Person() {
}

public Person(int id, String name, char sex, int age, String desc) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.desc = desc;
}

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 char getSex() {
return sex;
}

public void setSex(char sex) {
this.sex = sex;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getDesc() {
return desc;
}

public void setDesc(String desc) {
this.desc = desc;
}

@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
", sex=" + sex +
", age=" + age +
", desc='" + desc + '\'' +
'}';
}
}

我们存储时,能直接存储对象就好了, 取数据时,直接取对象, 方便操作。

这种形式,其实就是序列化。

序列化是将对象存储到文件里面, 反序列化是将文件转换成对象。

序列化和反序列化所需要的对象为 ObjectOutputStream 和 ObjectInputStream.

二. 序列化 ObjectOutputStream

二.一 方法

二.一.一 构造方法

二.一.一.一 方法

方法

作用

ObjectOutputStream(OutputStream out)

传入 outputStream 对象

二.一.一.二 演示
@Test
public void objConTest() throws Exception{

File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"data2.txt");
//传入 outputStream
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));
}

二.一.二 写入方法

方法

作用

void flush()

刷新此数据输出流

void writeInt(int v)

写入 int 类型的值

void writeLong(long v)

写入long 类型的值

void writeFloat(float v)

写入float 值

void writeDouble(double v)

写入double 值

void writeBoolean(boolean v)

写入boolean 类型的值

void writeChar(int v)

写入char 类型的值, 可以直接写入单个中文字符。 可写入 \t 和\n

void writeChars(String s)

写入字符串

void writeUTF(String str)

使用机器无关的方式使用 modified UTF-8编码将字符串写入底层输出流。

void writeObject(Object obj)

写入对象

对于 ObjectOutputStream 对象,只需要记住 writeObject() 方法即可。

二.二 演示 ObjectOutputStream

在序列化对象之前, 必须要保证 那个实体类, 即 Person 类,实现了 java.io.Serializable 接口, 否则会抛出异常的。

public class Person implements Serializable {


}

二.二.一 写入 单个对象

@Test
public void write1Test() throws Exception{

File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"data2.txt");
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));

Person person=new Person(1,"两个蝴蝶飞",'男',24,"一个快乐的程序员");

//如果没有序列化,会报错。
objectOutputStream.writeObject(person);

objectOutputStream.close();
}

运行程序, 查看 data2.txt 文件内容:

对象序列化和反序列化(九)_对象序列化

大概可以猜测是三部分, 第一部分是 类全限定名称, 第二部分是属性, 第三部分是 数据。

二.二.二 写入集合对象

同样是 writeObject ()方法处理。

@Test
public void write2Test() throws Exception{

File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"data3.txt");
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));

Person person1=new Person(1,"两个蝴蝶飞1",'男',24,"一个快乐的程序员1");
Person person2=new Person(2,"两个蝴蝶飞2",'男',24,"一个快乐的程序员2");
Person person3=new Person(3,"两个蝴蝶飞3",'男',24,"一个快乐的程序员3");
Person person4=new Person(4,"两个蝴蝶飞4",'男',24,"一个快乐的程序员4");

Person[] ps=new Person[]{person1,person2,person3,person4};
//如果没有序列化,会报错。
objectOutputStream.writeObject(ps);

objectOutputStream.close();
}

运行程序,查看 data3.txt

对象序列化和反序列化(九)_对象反序列化_02

我们虽然看不懂,但 ObjectInputStream 却能看懂,能解析。

三. 反序列化 ObjectInputStream

三.一 方法

三.一.一 构造方法

三.一.一.一 方法

方法

作用

ObjectInputStream(InputStream in)

传入 inputStream 对象

三.一.一.二 演示
@Test
public void readConTest() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"data2.txt");

ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));
}

三.一.二 读取方法

方法

作用

int readInt()

读取 int ,实际上就是读取4个长度

long readLong()

读取 long

float readFloat()

读取float

double readDouble()

读取double

boolean readBoolean()

读取boolean

char readChar()

读取char, 实际内部读取两个字节

String readUTF()

读取字符串

Object readObject()

读取对象

对于 ObjectInputStream 对象,只需要记住 readObject() 方法即可。

三.二 演示 ObjectInputStream

三.二.一 读取序列化的单个对象

@Test
public void read1Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"data2.txt");

ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));

Object obj= objectInputStream.readObject();
//向上转型
Person person=(Person)obj;

System.out.println("员工:"+person.toString());

objectInputStream.close();
}

运行程序,查看控制台打印输出:

对象序列化和反序列化(九)_Java对象序列化_03

三.二.二 读取序列化的对象集合

@Test
public void read2Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"data3.txt");

ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));

Object readObj= objectInputStream.readObject();
//对象数组
Object[] objs=(Object[])readObj;

for(Object obj:objs){
//对每一个进行强制转换。
Person person=(Person)obj;
System.out.println("员工:"+person);
}

objectInputStream.close();
}

运行程序,查看控制台打印输出:

对象序列化和反序列化(九)_对象序列化_04

可以正确的读取。

四. 序列化部分属性 transient

有时候,一个实体类中属性过多,不需要全部保存起来,或者某个属性太重要,需要保证安全,不能存储起来, 那么就需要 使用 transient 关键字进行阻止序列化。

如 阻止序列化 name 和 sex

对象序列化和反序列化(九)_Java对象序列化_05

重新运行一下 write2Test() 和 read2Test() 方法,查看控制台

对象序列化和反序列化(九)_对象序列化_06

就发现, name 和 sex 没有被序列化。

五. Externalizable 接口实现序列化

除了 Serializable 接口可以实现序列化外,还可以使用 java.io.Externalizable 接口 进行序列化, 但常用的还是 Serializable 接口, 这个接口 Externalizable 只需要了解即可。

package java.io;

import java.io.ObjectOutput;
import java.io.ObjectInput;

/**
* @since JDK1.1
*/
public interface Externalizable extends java.io.Serializable {
void writeExternal(ObjectOutput out) throws IOException;

void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}

writeExternal() 方法 设置的是 保存哪些属性,

readExternal() 方法,设置的是读取哪些属性。

注意,保存和读取属性的顺序 必须保持一致。

五.一 Externalizable 实现序列化

五.一.一 实体类 实现 接口

public class Person2 implements Externalizable {

}

五.一.二 重写 writeExternal() 方法,设置保存的属性

@Override
public void writeExternal(ObjectOutput out) throws IOException {
//保存哪些, 保存的数据。
out.writeInt(id);
out.writeObject(name);
//不保存 sex 属性
out.writeInt(age);
out.writeObject(desc);
}

五.一.三 重写 readExternal() 方法,读取保存的属性

@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

//读取的, 按照保存的顺序读取
this.id=in.readInt();
this.name=(String)in.readObject();

this.age=in.readInt();

this.desc=(String)in.readObject();
}

五.一.四 序列化单个对象

@Test
public void write1Test() throws Exception{

File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"data4.txt");
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new FileOutputStream(file));

Person2 Person2=new Person2(1,"两个蝴蝶飞",'男',24,"一个快乐的程序员");

//如果没有序列化,会报错。
objectOutputStream.writeObject(Person2);

objectOutputStream.close();
}

与 以前的方法是一样的。

运行程序,查看 data4.txt 的内容。

对象序列化和反序列化(九)_对象序列化_07

五.一.五 反序列化单个对象

@Test
public void read1Test() throws Exception{
File file=new File("E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
+File.separator+"data4.txt");

ObjectInputStream objectInputStream=new ObjectInputStream(new FileInputStream(file));

Object obj= objectInputStream.readObject();
Person2 Person2=(Person2)obj;

System.out.println("员工:"+Person2);

objectInputStream.close();
}

运行程序:

对象序列化和反序列化(九)_Java对象序列化_08

对象集合的序列化和反序列化与以前的也是一样的,就不重复写了。

五.二 Serializable 和 Externalizable 的区别

区别

Serializable

Externalizable

实现上

实现简单,Java提供了支持

实现复杂,需要开发人员写保存和读取

执行效率上

由Java统一 保存,性能较低

开发人员决定,速度可能会高

保存信息所占空间

空间大

空间较小

开发中,常使用的是 Serializable

序列化非常重要,一定要掌握。

谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!