1.概述
Java提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。
序列化:对象转换为字节 ObjectOutputStream
反序列化:字节重构为对象 ObjectInputStream

2.ObjectOutputStream类
java.io.ObjectOutputStream类继承自OutputStream,是对象的序列化流,把对象以流的方式写入到文件中保存。
构造方法(红色为常用)创建写入指定OutputStream的ObjectOutputStream。
| 构造方法摘要 | |
|---|---|
protected | ObjectOutputStream()为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。 |
| ObjectOutputStream(OutputStream out)创建写入指定 OutputStream 的 ObjectOutputStream。 |
参数:OutputStream out:字节输出流
特有成员方法:将指定的对象写入ObjectOutputStream。
void | writeObject(Object obj)将指定的对象写入 ObjectOutputStream。 |
使用步骤:
1.创建ObjectOutputStream对象,构造方法中传递字节输出流。
2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中。
3.释放资源。
public class Person { private String name; private int age; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", 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; } public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } }
// 1.创建ObjectOutputStream对象,构造方法中传递字节输出流。 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\a\\e.txt")); // 2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中。 Person person = new Person("易烊千玺",18); oos.writeObject(person); // 3.释放资源。 oos.close();
运行截图:

可以看到例子中序列化(和反序列化)的时候会抛出异常NotSerializableException,查看API文档可以看到,即当实例需要具有序列化接口时,抛出此异常。序列化运行时或实例的类会抛出此异常。参数应该为类的名称。
通俗理解就是需要类通过实现java.io.Serializable接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
Serializable接口也叫标记型接口,要进行序列化和反序列化的必须实现Serializable接口,就会给类添加一个标记,当我们进行序列化和反序列化的时候,就会检测类上的是否有这个标记,如果有就可以序列化和反序列化;如果没有,就会抛出NotSerializableException异常。
public class Person implements Serializable { private String name; private int age; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", 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; } public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } }
2.ObjectInputStream类
java.io.ObjectInputStream类继承自InputStream,是对象的反序列化流,把文件中以字节保存的对象信息以流的形式读取到内存中。
构造方法(红色为常用)创建写入指定InputStream的ObjectInputStream。
| 构造方法摘要 | |
|---|---|
protected | ObjectInputStream()为完全重新实现 ObjectInputStream 的子类提供一种方式,让它不必分配仅由 ObjectInputStream 的实现使用的私有数据。 |
| ObjectInputStream(InputStream in)创建从指定 InputStream 读取的 ObjectInputStream。 |
参数:InputStream in:字节输入流
特有成员方法:读取对象
Object | readObject()从 ObjectInputStream 读取对象。 |
使用步骤:
1.创建ObjectInputStream对象,构造方法中传递字节输入流。
2.使用ObjectInputStream对象中的方法readObject,读取保存对象文件。
3.释放资源。
4.使用读取出来的对象(打印)
private static void in() throws IOException, ClassNotFoundException { // 1.创建ObjectInputStream对象,构造方法中传递字节输入流。 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\a\\e.txt")); // 2.使用ObjectInputStream对象中的方法readObject,读取保存对象文件。 Object object = ois.readObject(); // 3.释放资源。 ois.close(); // 4.使用读取出来的对象(打印) System.out.println(object.toString()); }
注意:readObject方法声明抛出了ClassNotFoundException(class文件找不到异常),当不存在这个对象class文件时抛出此异常。
4.序列化操作
序列化的前提:
1.类必须实现Serializble
2.必须存在类对应的class文件
即一个对象想要序列化,必须满足两个条件:
(1)该类必须实现java.io.Serializable接口,Serializable是一个标记接口,不是先此接口的类将不会使任何状态序列化或反序列化,会抛出NotSerializableException。
(2)该类的所有属性必须是可序列化的,如果有一个属性不需要可序列化的,则该属性必须注明是瞬态的,使用transient关键字修饰。
对比知识点:static关键字(静态关键字)静态优先于非静态加载到内存中(静态优先于对象进入到内存中,被static修饰的成员变量不能被序列化(能读写文件,只是内容与赋值的不同),序列化的都是对象,静态的属于类不属于对象)
举例,在之前的代码基础上修改:
public class Person implements Serializable { private String name; private static int age; // static修饰...
运行截图:

会发现输出age=0,不根据赋值内容而变,而是默认初始化的0。
注意!!!!:将序列化和反序列化分别使用两个程序进行,同一个程序会导致static变量已经被JVM加载,取出来的值就是自己赋值的。
而被transient关键字(瞬态关键字)修饰的成员变量,也不能被序列化(能读写文件,只是内容与赋值的不同)。
public class Person implements Serializable { private String name; private transient int age; // transient修饰...
运行截图:

5.反序列化操作2
另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:
(1)该类的序列版本号与从流中读取的类描述符的版本号不匹配;
(2)该类包含未知数据类型;
(3)该类没有可访问的无参数构造方法。
Serializable接口给需要序列化的类,提供了一个序列版本号serialVersionID,该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

每次修改类的定义,都会给class文件生成一个新的序列号,因此如果想要无论是否对类的定义进行修改 都不重新生成新的序列号,可以手动给类天机啊一个序列号。
格式:
在Serializable接口规定:可序列化类可以通过伤声明“serialVersionUID”的字段(该字段必须是静态static、最终final的long型字段)显式声明其自己的serialVersionUID。
static final long serialVersionUID = 42L;
本文介绍了Java中的对象序列化和反序列化机制,包括使用ObjectOutputStream和ObjectInputStream进行操作。序列化时,对象必须实现Serializable接口,否则会抛出NotSerializableException。transient关键字用于标记不希望序列化的属性。反序列化时,若class文件修改,可能导致InvalidClassException。serialVersionUID用于确保类版本匹配。
240

被折叠的 条评论
为什么被折叠?



