一、序列化

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

对象序列化,就是把一个对象变为二进制的数据流的一种方法,通过对象序列化可以方便的实现对象的传输或存储。
 
序列化保存对象的“全景图”,构建对象的“全景天窗”.
如果一个类的对象想被序列化,则对象所在的类必须实现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. Codeforces Round #260 (Div. 2) B. Fedya and Maths

    B. Fedya and Maths time limit per test 1 second memory limit per test 256 megabytes input standard i ...

  2. angularjs-ui插件ui-select和html的select注意事项及区别

    项目中使用了angular-ui里的ui-select指令,地址https://github.com/angular-ui/ui-select 1. ng-model没有双向数据绑定 最开始没有看手册 ...

  3. Android 手机 无线 ADB

    要用网络调试Android需要设备已经获取root权限 如果手机没有命令行工具,请先在手机端安装终端模拟器,然后在终端输入: $su #stop adbd #setprop service.adb.t ...

  4. 74HC245 74HCT245 74LV245 74LVC245 74LVC4245A 74LVC8T245 74LVC16T245 74ALVC164245

    74HC245/74HCT245 The 74HC245; 74HCT245 is a high-speed Si-gate CMOS device and is pin compatible wit ...

  5. axure8.1可用授权码

    Licensee: University of Science and Technology of China (CLASSROOM)Key: DTXRAnPn1P65Rt0xB4eTQ+4bF5IU ...

  6. 七问C#关键字const和readonly

    const和readonly经常被用来修饰类的字段,两者有何异同呢? const 1.声明const类型变量一定要赋初值吗? --一定要赋初值 public class Student { publi ...

  7. Android之ConnectivityManager

    在android平台中ConnectivityManager主要负责查询网络连接状态以及在连接状态有变化的时候发出通知.其主要的功能职责如下: 1.  监视网络状态,包括(Wi-Fi.GPRS.UMT ...

  8. 【docker】docker启动、重启、关闭命令,附带:docker启动容器报错:docker: Error response from daemon: driver failed programming external connectivity on endpoint es2-node

    在关闭并放置centos 的防火墙重启之后[操作:https://www.cnblogs.com/sxdcgaq8080/p/10032829.html] 启动docker容器就发现开始报错: [ro ...

  9. 12c weblogic需要输入用户名密码

    在目录/home/weblogic_12c/user_projects/domains/base_domain/servers/AdminServer/security下新增文件boot.proper ...

  10. PHP 7.0 5.6 下安裝 phpLDAPadmin 发生错误的修正方法

    在稍具規模的網路環境中, 網管時常選用 LDAP 來進行帳號的統整管理, 一方面提供管理便利度, 另一方面使用者也不必因為不同系統而記憶不同帳號, phpLDAPadmin 是一套常見的 LDAP 管 ...