一.介绍
序列化流:把对象按照流一样的方式存入文本文件或者在网络中传输。 对象 -- 流数据(ObjectOutputStream)
反序列化流:把文本文件中的流对象数据或者网络中的流对象数据还原成对象。流数据 -- 对象(ObjectInputStream)
序列化流和反序列化流不同于字节流、字符流、内存字节流,这些都是把字符串写入/读取文件,序列化流和反序列化流可以将对象写入/读取文本文件中.
但是我们要注意的是,写入和读取的文件要实现Serializable这个接口,当然这个接口是没有任何方法的,直接implements就好,类似于这种没有任何方法的接口被称为标记接口.
二.知识点介绍
1、概念
2、序列化
3、序列化流
4、对象反序列化流
5、将用户信息存储到文件中与读取文件用户
6、序列化接口
7、瞬态关键字transient
三.上课视频对应说明文档
1、序列化流概念
序列化流可以将对象进行序列化和反序列化。
用于从流中读取对象的操作流 ObjectInputStream称为反序列化流
用于向流中写入对象的操作流 ObjectOutputStream称为序列化流
注意:对象序列化时,对象的数据类型定义时一定要实现Serializable接口,从而实现可持久化。
2、序列化
对象的寿命通常随着生成该对象的程序的终止而终止,有时候需要把在内存中的各种对象的状态(也就是实例变量,不是方法)保存下来,并且可以在需要时再将对象恢复。虽然你可以用你自己的各种各样的方法来保存对象的状态,但是Java给你提供一种应该比你自己的好的保存对象状态的机制,那就是序列化。
总结:Java 序列化技术可以使你将一个对象的状态写入一个Byte 流里(系列化),并且可以从其它地方把该Byte 流里的数据读出来(反序列化)。
2.1、序列化的用途
(1)想把的内存中的对象状态保存到一个文件中或者数据库中时候
(2)想把对象通过网络进行传播的时候
2.2、序列化的实现
实现Serializable接口
3、序列化流
将一个对象以对象的形式存储到文件中。
ObjectOutputStream writeObject():写出对象
代码示例:
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
/*
* 将一个对象存储到持久化(硬盘)的设备上。
*/
writeObj();//对象的序列化。
}
public static void writeObj() throws IOException {
//1,明确存储对象的文件。
FileOutputStream fos = new FileOutputStream("aa\\obj.txt");
//2,给操作文件对象加入写入对象功能。
ObjectOutputStream oos = new ObjectOutputStream(fos);
//3,调用了写入对象的方法。
oos.writeObject(new Person("wangcai",20));
//关闭资源。
oos.close();
}
}
4、对象反序列化流
当把一个对象持久化存储起来之后,需要使用反序列化技术获取存储起来的对象
ObjectInputStream readObject():读取对象。使用此ObjectInputStream对象就可以完成反序列化动作
代码示例:
public class ObjectStreamDemo {
public static void main(String[] args)throws IOException, ClassNotFoundException {
readObj();//对象的反序列化。
}
public static void readObj() throws IOException, ClassNotFoundException {
//1,定义流对象关联存储了对象文件。
FileInputStream fis = new FileInputStream("aa\\obj.txt");
//2,建立用于读取对象的功能对象。
ObjectInputStream ois = new ObjectInputStream(fis);
Person obj = (Person)ois.readObject();
System.out.println(obj.toString());
}
}
5、将用户信息存储到文件中与读取文件用户
5.1、用户实体类
代码示例:
import java.io.Serializable;
/*
*
* Serializable 接口
* 作用: 想要序列化,就必须实现这个 接口. 不用重写任何方法. 只是标记 一下.
*/
public class User implements Serializable{
String name ;
String psw ;
int age;
public User() {
super();
}
public User(String name, String psw) {
super();
this.name = name;
this.psw = psw;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPsw() {
return psw;
}
public void setPsw(String psw) {
this.psw = psw;
}
@Override
public String toString() {
return "User [name=" + name + ", psw=" + psw + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + ((psw == null) ? 0 : psw.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (psw == null) {
if (other.psw != null)
return false;
} else if (!psw.equals(other.psw))
return false;
return true;
}
}
5.2、序列化存储
代码示例:
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
/*
* 序列化和反序列化
* 序列化: 从内存保存到硬盘
* 反序列化 : 从硬盘读取到内存 .
* 操作的数据都是对象 . 都是Object
*
*/
public class Demo {
public static void main(String[] args) throws IOException {
// 创建序列化 流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.txt"));
User user = new User("老王", "laowang");//java.io.NotSerializableException: cn.javahelp_02.User
oos.writeObject(user);
oos.close();
}
}
5.3、反序列化读取
代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
/*
* 读取序列化的信息, 反序列化.
*/
public class Demo2 {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
// 创建反序列化 流 .
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));
Object obj = ois.readObject();
System.out.println(obj);
if (obj instanceof User) {
User u = (User) obj;
String name = u.getName();
System.out.println(name);
}
ois.close();
}
}
在创建对象读取(报读取文件结尾异常)思考:不知道里面存储多少元素,但是读到结尾就报异常,这样怎么办。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
/*
* 读取序列化的信息, 反序列化.
*/
public class Demo2 {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
// 创建反序列化 流 .
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.txt"));
Object obj = ois.readObject();
System.out.println(obj);
if (obj instanceof User) {
User u = (User) obj;
String name = u.getName();
System.out.println(name);
}
Object obj2 = ois.readObject(); //End Of File Exception 读取文件结尾异常
System.out.println(obj2);
ois.close();
}
}
5.4、集合序列化解决不知道存储元素数问题
代码示例:
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
/*
* 把集合序列化.
*/
public class Demo3 {
public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
ArrayList<User > arrayList = new ArrayList<>();
User user = new User("金莲","jinlian");
User user2 = new User("西门","daguanren");
User user3 = new User("大郎","cuibing");
arrayList.add(user);
arrayList.add(user2);
arrayList.add(user3);
//创建 序列化流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user2.txt"));
oos.writeObject(arrayList);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user2.txt"));
Object readObject = ois.readObject();
System.out.println(readObject);
// 强制 转换 在遍历 readObject --> ArrayList<User>
ArrayList<User> userList = (ArrayList<User>) readObject;
for (User u : userList) {
System.out.println(u.getName() +" -- "+ u.getPsw());
}
}
}
6、序列化接口
当一个对象要能被序列化,这个对象所属的类必须实现Serializable接口。否则会发生异常NotSerializableException异常。
同时当反序列化对象时,如果对象所属的class文件在序列化之后进行了修改,那么进行反序列化也会发生异常InvalidClassException。发生这个异常的原因如下:
该类的序列版本号与从流中读取的类描述符的版本号不匹配
该类包含未知数据类型
该类没有可访问的无参数构造方法
Serializable标记接口。该接口给需要序列化的类,提供了一个序列版本号。 serialVersionUID. 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。
标记作用,当年的类没变,虽然添加了属性,但是可以
代码示例:
public class Person implements Serializable {
/*
* serialVersionUID = 1L
* 只是一个对象的标记. 防止 你的类升级了, 对象 与类的信息 不一致.
*/
//给类显示声明一个序列版本号。
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
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 String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
7、瞬态关键字transient
当一个类的对象需要被序列化时,某些属性不需要被序列化,这时不需要序列化的属性可以使用关键字transient修饰。只要被transient修饰了,序列化时这个属性就不会被序列化了。
同时静态修饰也不会被序列化,因为序列化是把对象数据进行持久化存储,而静态的属于类加载时的数据,不会被序列化。
代码示例:
public class Person implements Serializable {
/*
* 给类显示声明一个序列版本号。
*
*只要被transient修饰了,序列化时这个属性就不会琲序列化了。不想保存
*/
private static final long serialVersionUID = 1L;
private static String name;
private transient/*瞬态*/ int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
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 String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
网友评论