一、序列化

将对象的状态存储到特定存储介质中的过程

对象序列化,就是把一个对象变为二进制的数据流的一种方法,通过对象序列化可以方便的实现对象的传输或存储。
 
序列化保存对象的“全景图”,构建对象的“全景天窗”.
如果一个类的对象想被序列化,则对象所在的类必须实现java.io.Serializable接口

二、对象的序列化和反序列化

要想完成对象的输入或输出,还必须依靠对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream)
使用对象输出流输出序列化对象的步骤,有时也称为序列化,而使用对象输入流读入对象的过程,有时也称为反序列化
序列化步骤:
  1. 创建一个对象输出流ObjectOutputStream
  2. writeObject()方法输出序列化对象

反序列化步骤:

  1. 创建一个对象输入流ObjectInputStream
  2. readObject()方法读取流中的对象

三、对象输出流(ObjectOutputStream)和对象输入流(ObjectInputStream)

3.1、对象输出流:ObjectOutputStream

No.
方法或常量
类型
描述
1
public ObjectOutputStream(OutputStream out) throws IOException
构造
传入输出的对象
2
public final void writeObject(Object obj) throws IOException
普通
输出对象
此类的使用形式与PrintStream非常的相似,在实例化时也需要传入一个OutputStream的子类对象,之后根据传入的OutputStream子类的对象不同,输出的位置也不同。

3.2、对象输入流:ObjectInputStream

No.
方法或常量
类型
描述
1
public ObjectInputStream(InputStream in) throws IOException
构造
构造输入对象
2
public final Object readObject() throws IOException, ClassNotFoundException
普通
从指定位置读取对象
此类也是InputStream的子类,与PrintStream类的使用类似,此类同样需要接收InputStream类的实例才可以实例化

四、transient关键字

当使用Serializable接口实现序列化操作时,如果一个对象中的某个属性不希望被序列化的话,则可以使用transient关键字进行声明

import java.io.Serializable;
public class Person implements Serializable { // 此类的对象可以被序列化
private transient String name; // 此属性将不被序列化
private int age; // 此属性将被序列化
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String toString() { // 覆盖toString(),输出信息
return "姓名:" + this.name + ";年龄:" + this.age;
}
}

五、实现反序列化注意事项

  • 反序列化过程无需要使用构造器生成对象
  • 按顺序反序列化恢复对象
  • 父类Serializable或者存在无参数构造方法

六、例子

package com.pb.serializable;

import java.io.Serializable;

/*
* 学生类 并实现接口Serializable接口,使用之可以序列化
*/
/*
* 序列化
* 1.创建一个对象,这个对象将被序列化,并写入文件中,前提是这个类实现了Serializable接口
* 2.实例化ObjectOutputStream的对象
* 3.调用ObjectOutputStream的writerObject()方法,将对象写入流中
* 4.关闭流
* transient关键字
* 可以保护对象中的敏感信息不被写入到文件中
* 反序列化
* 1.实例化ObjectInputStream对象
* 2.调用ObjectInputStream的readObject()方法,获取对象时,要进行强制类型转换
* 3.关闭流
*/
public class Student implements Serializable {
private String name; // 姓名
private int age; // 年龄
private String gender; // 性别
private transient String password; // 密码属性不能被序列化 // 构造方法
public Student() {
// 无参
} // 有参数
public Student(String name, int age, String gender, String password) { this.name = name;
this.age = age;
this.gender = gender;
this.password = password;
}
//getter、setter方法 public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
if(age>0&&age<150)
this.age = age;
} public String getGender() {
return gender;
} public void setGender(String gender) {
if(gender.equals("男") || gender.equals("女"))
this.gender = gender;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} // 自我介绍
public void say() {
System.out.println("姓名:" + this.name + "\t年龄:" + this.age + "\t性别:"
+ this.gender+"\t密码 : "+this.password);
} }

序列化:

package com.pb.serializable;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List; /*
* 序列化
* 1.创建一个对象,这个对象将被序列化,并写入文件中,前提是这个类实现了Serializable接口
* 2.实例化ObjectOutputStream的对象
* 3.调用ObjectOutputStream的writerObject()方法,将对象写入流中
* 4.关闭流
* transient关键字
* 可以保护对象中的敏感信息不被写入到文件中
*/
public class SerializableObj { public static void main(String[] args) { try {
//1.声明一个文件输出流
FileOutputStream fos = new FileOutputStream("d:/test/obj.txt");
//2.声明ObjectOutputStream流对象
ObjectOutputStream oos=new ObjectOutputStream(fos);
//也可以2步合一步
//ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("d:/test/obj.txt"));
Student stu1=new Student("张三", 23, "男", "123456");
Student stu2=new Student("李四", 24, "女", "123123");
Student stu3=new Student("王五", 25, "男", "123321");
Student stu4=new Student("赵六", 26, "男", "999999");
//创建集合并添加
List<Student> list=new ArrayList<Student>();
list.add(stu1);
list.add(stu2);
list.add(stu3);
list.add(stu4);
//3.将student对象序列化,写入输出oos流
oos.writeObject(stu1);
oos.writeObject(stu2);
oos.writeObject(stu3);
oos.writeObject(stu4);
oos.writeObject(list);
//4.关闭流
oos.close();
fos.close();
System.out.println("=======序列化完成======");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} } }

反序列化:

package com.pb.serializable;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.List; /*
* 反序列化
* 1.实例化ObjectInputStream对象
* 2.调用ObjectInputStream的readObject()方法,获取对象时,要进行强制类型转换
* 3.关闭流
*/
public class ReadSerializableObj { public static void main(String[] args) { try {
//1.声明一个文件输出流
FileInputStream fis = new FileInputStream("d:/test/obj.txt");
//2.实例化ObjectInputStream
ObjectInputStream ois=new ObjectInputStream(fis);
//也可以全2为一
//ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/test/obj.txt"));
//3.声明一个Student对象和集合
System.out.println("=====反序列化学生对象=======");
Student stu1= (Student) ois.readObject();
stu1.say();
Student stu2= (Student) ois.readObject();
stu2.say();
Student stu3= (Student) ois.readObject();
stu3.say();
Student stu4= (Student) ois.readObject();
stu4.say();
System.out.println("=====反序列化集合对象=======");
List<Student> list=(List<Student>) ois.readObject();
for (Student s : list) {
s.say();
} //4.关闭流
ois.close();
fis.close();
System.out.println("====反序列完成====");
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} } }

结果:

=====反序列化学生对象=======
姓名:张三 年龄:23 性别:男 密码 : null
姓名:李四 年龄:24 性别:女 密码 : null
姓名:王五 年龄:25 性别:男 密码 : null
姓名:赵六 年龄:26 性别:男 密码 : null
=====反序列化集合对象=======
姓名:张三 年龄:23 性别:男 密码 : null
姓名:李四 年龄:24 性别:女 密码 : null
姓名:王五 年龄:25 性别:男 密码 : null
姓名:赵六 年龄:26 性别:男 密码 : null
====反序列完成====

从结果中可以看到:password使用transient关键字后,没有被序列化,反序列化中,读出的是默认字段类型的默认值null

七、包含引用类型属性的对象序列化

引用类必须也为可序列化

public class Teacher implements Serializable {
private String name; // 姓名
private int age; // 年龄
private String gender; // 性别
private transient String password; // 密码属性不能被序列化 private Student stu; //这里的Student类必须也是可以序列化的, }

这里有个Teacher类,但属性中有一个Student类的对象,这时Teacher要实现序列时,Student必须也要实现Serializable接口才可以

7.1、序列化算法

  • 对象分配序列号
  • 当程序试图序列化一个对象时,将会检查是否已经被序列化,只有序列化后的对象才能被转换成字节序列输出
  • 如果对象已经被序列化,则程序直接输出一个序列化编号,而不在重新序列化

7.2、序列化机制

Teacher类:

package com.pb.serializable;

import java.io.Serializable;
/*
* 教师类实例Serializable接口
*/
public class Teacher implements Serializable {
private String name; // 姓名
private int age; // 年龄
private String gender; // 性别
private transient String password; // 密码属性不能被序列化 private Student stu; //这里的Student类必须也是可以序列化的, //构造方法
public Teacher() {
//无参数
} public Teacher(String name, int age, String gender, String password,
Student stu) {
//有参数
this.name = name;
this.age = age;
this.gender = gender;
this.password = password;
this.stu = stu;
} //getter、setter方法
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 getGender() {
return gender;
} public void setGender(String gender) {
this.gender = gender;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public Student getStu() {
return stu;
} public void setStu(Student stu) {
this.stu = stu;
} // 自我介绍
public void say() {
System.out.println("姓名:" + this.name + "\t年龄:" + this.age + "\t性别:"
+ this.gender+"\t密码 : "+this.password);
} }

序列化和化序列化类:

package com.pb.serializable;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; /*
* 老师类的序列化和反序列化 2个老师引用同一个学生对象
*/
public class WriterTeacherObj { public static void main(String[] args) { /*
* 序列化
* 1.序列化时时候,会查找当前对象的序列化编号是否存在,不存在,保存该对象
* 2.写入操作时,当对象的序列化编号存在时候,会输出一个当前对象的序列化编号,而不是当前对象
* 3.反序列化对象时, 程序会自动查找,当前对象的序列化编号,之后如果,发现当前的对象的序列化编号被其它对象引用时,则返回同一个对象
*/
try {
//1.实例化ObjectOutputStream
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("d:/test/teacher.txt"));
//2.实例化老师对象
Student student=new Student("马达", 23, "男", "123123");
//2个老师引用同一个学生对象
Teacher teacher_han=new Teacher("韩冰", 22, "女", "123456789", student);
Teacher teacher_zhang=new Teacher("张三丰", 108, "男", "123456789", student);
//3.对象序列
oos.writeObject(teacher_han);
oos.writeObject(teacher_zhang);
//4.关闭流
oos.close();
System.out.println("====================序列化完成=================");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
/*
* 反序列化
*/
System.out.println("==================开始反序列化================");
try {
//1.实例化ObjectInputStream对象
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("d:/test/teacher.txt"));
//2.反序列声明一个Teacher对象强制类型转换
Teacher teacher_han=(Teacher) ois.readObject();
Teacher teacher_zhang=(Teacher) ois.readObject();
//3.输入Teacher中的信息
System.out.println("==================老师信息================");
teacher_han.say();
teacher_zhang.say();
System.out.println("=================老师中学生信息=============");
teacher_han.getStu().say();
teacher_zhang.getStu().say();
//4.关闭流
ois.close();
System.out.println("===========反序列化完成========="); //判断2个老师的学生是否为同一个
if(teacher_han.getStu()==teacher_zhang.getStu()){
System.out.println("张老师和韩老师教的是同一个学生");
}else{
System.out.println("张老师和韩老师教的不是同一个学生");
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} } }

结果:

====================序列化完成=================
==================开始反序列化================
==================老师信息================
姓名:韩冰 年龄:22 性别:女 密码 : null
姓名:张三丰 年龄:108 性别:男 密码 : null
=================老师中学生信息=============
姓名:马达 年龄:23 性别:男 密码 : null
姓名:马达 年龄:23 性别:男 密码 : null
===========反序列化完成=========
张老师和韩老师教的是同一个学生

Java从零开始学三十九(对象序列化)的更多相关文章

  1. Java从零开始学三十八(JAVA IO- 重定向IO)

    一.三个静态变量 java.lang.System提供了三个静态变量 System.in(默认键盘) System.out(默认显示器) System.err 二.重写向方法 System提供了三个重 ...

  2. Java从零开始学三十六(JAVA IO- 字符流)

    一.字符流 BufferedReader:BufferedReader是从缓冲区之中读取内容,所有的输入的字节数据都将放在缓冲区之中 BufferedWriter:把一批数据写入到缓冲区,当缓冲区区的 ...

  3. Java从零开始学三十五(JAVA IO- 字节流)

    一.字节流 FileOutputStream是OutputStream 的直接子类 FileInputStream也是InputStream的直接子类 二.文本文件的读写 2.1.字节输入流 Test ...

  4. Java从零开始学三十三四(JAVA IO-流简述)

    一.流概念(stream) File类并不能对文件内容进行读写. 读文件就是指:把文件的内中的数据读取到内存中来 写文件就是指:把内存中的数据写入到文件中去. 通过什么读写文件呢?文件流. 1.1.流 ...

  5. Java从零开始学三十二(正则表达式)

    一.为什么要有正则 正则表达式可以方便的对数据进行匹配,可以执行更加复杂的字符串验证.拆份.替换功能. 例如:现在要求判断一个字符串是否由数字组成,则可以有以下的两种做法: 不使用正则完成 使用正则完 ...

  6. Java从零开始学三十(String和StringBuffer类)

    一.StringBuffer连接字符操作 当一个字符串的内容需要被经常改变时就要使用StringBuffer 在StringBuffer中使用append()方法,完成字符串的连接操作   二.Str ...

  7. Java从零开始学二十九(大数操作(BigIntger、BigDecimal)

    一.BigInteger 如果在操作的时候一个整型数据已经超过了整数的最大类型长度long的话,则此数据就无法装入,所以,此时要使用BigInteger类进行操作. 不可变的任意精度的整数.所有操作中 ...

  8. Java从零开始学十一(类和对象)

    一.面象对象 二.什么是类 我肯定说,不知道.不清楚. 简单讲类是java中的基本单元,类是具有相同特性和行为的对象集合 三.类的定义 3.1.类的定义 class 类名称{ 数据类型  属性 ; … ...

  9. Java从零开始学四十五(Socket编程基础)

    一.网络编程中两个主要的问题 一个是如何准确的定位网络上一台或多台主机,另一个就是找到主机后如何可靠高效的进行数据传输. 在TCP/IP协议中IP层主要负责网络主机的定位,数据传输的路由,由IP地址可 ...

随机推荐

  1. wikioi 1380 没有上司的舞会 树形dp

    1380 没有上司的舞会 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond       题目描述 Description Ural大学有N个职员,编号为1~N.他 ...

  2. MVC高级编程-目录

    MVC高级编程 ================================================== 控制器 视图 模型 表单和HTML辅助方法 数据注解和验证 成员资格.授权和安全性 ...

  3. wrote a programming language

    https://medium.freecodecamp.org/the-programming-language-pipeline-91d3f449c919

  4. IOS开发之深拷贝与浅拷贝(mutableCopy与Copy)详解

    copy与retain的区别: copy是创建一个新对象,retain是创建一个指针,引用对象计数加1.Copy属性表示两个对象内容相同,新的对象retain为1 ,与旧有对象的引用计数无关,旧有对象 ...

  5. arcgis连接excel出现数据库失败 外部数据库驱动程序意外错误

    微软搞事情,删除以下更新就行:win7 KB4041678 KB4041681SERVER 2008 R2 KB4041678 KB4041681WIN10 KB4041676 KB4041691SE ...

  6. Git 学习(八)其他

    Git 学习(八)其他 通过以上七章Git的学习,基本操作已差不多了,本章介绍一点落网之鱼:  包括如何忽略文件.配置别名.以及使用GitHub等. 当然,Git的强大远不是七章内容可概括的,之后可结 ...

  7. python的内存回收机制即gc模块讲解

    最后容易造成内存问题的通常就是全局单例.全局缓存.长期存活的对象 引用计数(主要), 标记清除, 分代收集(辅助) 引用计数为0则会被gc回收.标记删除可以解决循环引用的问题.分代:0代--年轻代:1 ...

  8. 【BZOJ】【2286】【SDOI2011】消耗战

    虚树+树形DP Orz ZYF……果然好神…… 建虚树先按dfn排序,再用一个单调栈来维护当前这条[链],往里加边……说实话还没弄懂- - 留个坑吧…… RE的原因:这条链往出退的时候没写top--; ...

  9. MySQL中limit的用法

    SELECT * FROM table  LIMIT [offset,] rows | rows OFFSET offset   LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数.L ...

  10. OpenCV学习(27) 直方图(4)

    我们可以利用OpenCV的直方图,backproject直方图和meanshift算法来跟踪物体.下面通过简单的例子来说明如何实现跟踪算法,我们有两幅狒狒的图片,如下图所示:我们首先在左图中框选狒狒的 ...