先说概念:

一、相关概念

序列化:把内存中的java对象转换成与平台无关的二进制字节序列,以便永久保存在磁盘上或通过网络进行传输。序列化是Java提供的一种将对象写入到输出流、并在之后将其读回的机制。

Java提供的对对象进行读写的流对象(即承载对象的媒介)为ObjectOutputStream 和 ObjectInputStream ,它们的作用就是把对象转换为字节序列,并承载这些序列,并提供了writeObject() 和 readObject() 方法对这些字节序列进行读写。

那么,如何实现对象的序列化与反序列化呢?

二、序列化、反序列化的方法

方法(一)-- Serializable接口

2.1 使用Serializable接口的默认序列化机制

1、实现Serializable接口

如果某个类对象要被序列化,该类必须实现Serializable接口,只接口内无具体方法,所以只需要声明实现该接口,而无需实现具体方法。

public class Student implements Serializable {
…… }

2、通过ObjectOutputStream 类对象的writeObject(obj)方法,将obj参数指定的对象进行序列化,把得到的字节序列写到目标输出流中。

        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("student.dat"));
Student s = new Student();
out.writeObject(s);
out.close();

3、通过ObjInputStream类对象的readObject()方法从源输入流中读取字节序列,再反序列化为对象,并将其返回。

        //反序列化得到对象
ObjectInputStream in = new ObjectInputStream(new FileInputStream("student.dat"));
Student s1 = (Student) in.readObject();
in.close();

 

以上是简单的对象序列化,使用默认序列化机制,会将对象的全部域都进行序列化,同时对该对象引用的其他对象也进行序列化,同样的,对这些引用对象进行序列化的时候如果有另外的引用,这些引用也会被序列化,以此类推。如果一个对象包含的成员变量是容器对象,容器的元素还是容器,那么序列化过程会变得十分繁琐。另外,有时候对象的一些数据域只在本地有意义,对这样的数据进行序列化后存储或传输没有意义,甚至有时会在反序列化后使用对象时引起崩溃。所以,我们需要指定一些字段不需要序列化。

2.2 自定义序列化

序列化的实际应用中通常需要忽略某些数据域或简化序列化过程,此时就需要自定义序列化的过程,自定义序列化的方法有如下几种:1、使用transient关键字;2、使用writeObject()和readObject()方法  3、使用External接口实现序列化。

1、使用transient关键字

在对未来会进行序列化的类进行定义时,使用transient关键字声明无需序列化的字段。默认序列化机制会忽略被transient修饰的字段。

2、使用writeObject()与readObject()

在可序列化的类中添加私有的writeObject和readObject方法,定义这个两个方法后,不再使用默认的序列化机制,而是执行这两个方法中的内容。在这个方法中可以向默认的读写行为中添加验证或任何其他行为。

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
in.defaultReadObject();//调用默认序列化机制
//添加逻辑
} private void writeObject(ObjectOutputStream out) throws IOException {
out.defaultWriteObject();//调用默认序列化机制
//添加逻辑
}

使用这种方法可以序列化某些不可被序列化的域。有类似这样的情形,某可序列化的类中一个域是某不可序列化的类的对象,但是又需要反序列化的时候获取这个对象。此时可以使用这里提到的方法进行序列化。

step1:将不可序列化的类的对象声明为transient,以避免NotSerializableException。示例:

(student可序列化,teacher不可)

public class Student implements Serializable {

    private String name;
private int age;
private transient Teacher teacher;
…………
}

step2:writeObject方法中调用defaultWriteObject方法写出可序列化的域,然后使用标准的DataOutput调用写出不可序列化的域中的数据。defaultWriteObject是ObjectOutputStream类的一个特殊方法,只能在可序列化类的writeObject方法中调用。注意该方法为private类型。示例:

    private void writeObject(ObjectOutputStream out) throws IOException {
//对可序列化的域进行序列化
out.defaultWriteObject();
//不可序列化的域对该域内基本数据类型分别进行序列化
out.writeInt(teacher.getCourseNo());
out.writeUTF(teacher.getName());
}

step3:readObject方法中反过来执行上述过程,注意该方法也为private类型。示例:

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
//对可序列化的域进行反序列化
in.defaultReadObject();
//不可序列化的域通过对该域内基本数据类型分别进行反序列化,然后重新创建该域
int courseNo = in.readInt();
String name = in.readUTF();
teacher = new Teacher(courseNo, name);
}

完整示例代码:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable; public class Student implements Serializable { private String name;
private int age;
private transient Teacher teacher;
public Student(){} public void setName(String n, int a){
this.name = n;
this.age = a;
}
public String getName(){
return this.name;
} private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{
//对可序列化的域进行反序列化
in.defaultReadObject();
//不可序列化的域通过对该域内基本数据类型分别进行反序列化,然后重新创建该域
int courseNo = in.readInt();
String name = in.readUTF();
teacher = new Teacher(courseNo, name);
} private void writeObject(ObjectOutputStream out) throws IOException {
//对可序列化的域进行序列化
out.defaultWriteObject();
//不可序列化的域对该域内基本数据类型分别进行序列化
out.writeInt(teacher.getCourseNo());
out.writeUTF(teacher.getName());
}
}

Student.java

public class Teacher {
private int courseNo;
private String name; public int getCourseNo() {
return courseNo;
} public String getName() {
return name;
} public Teacher(int courseNo, String name){
this.courseNo = courseNo;
this.name = name;
} }

Teacher.java

注意,

方法(二)-- External接口

3、使用Externalizable接口

无论是使用transient关键字,还是使用writeObject()和readObject()方法,其实都是基于Serializable接口的序列化。除了让序列化机制来保存和恢复对象,类还可以定义它自己的机制。JDK中提供了另一个序列化接口--Externalizable,使用该接口之后,之前基于Serializable接口的序列化机制就将失效。声明实现Externalizable接口的类必须重写readExternal()和writeExternal()方法。示例:

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput; public class Student2 implements Externalizable {
public String name;
public int age;
public Teacher teacher;
public Student2(){} @Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
} @Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF();
age = in.readInt(); }
}

Externalizable继承于Serializable,跟readObject方法不同的是,使用该接口时,序列化的细节需要程序员自行完成,writeExternal和readExternal方法需要对包括超类数据在内的整个对象的存储和恢复负全责。

在写出对象时,序列化机制在输出流中仅仅是记录该对象所属的类。在读入可外部化的类时,对象输入流会调用类的无参构造函数创建一个对象,然后调用readExternal方法。所以实现Externalizable的类必须提供一个无参构造函数,并且权限为public

注意:readObject和writeObject是私有的,并且只能被序列化机制调用;readExternal 和 writeExternal是公共的,readExternal还潜在的允许修改现有对象的状态。

三、总结

1、java序列化就是为了存储或传输对象的某个瞬时状态。

2、java中实现对象序列化的方式有两种:

方法一:

  可序列化类实现Serializable接口,该接口默认情况下没有需要实现的方法。

  如果有不可序列化的字段,如果该字段在不需要反序列化,则使用transient关键字声明该字段即可;如果该字段在反序列化后还有需要,则通过在可序列化类中定义writeObject和readObjcet方法,将不可序列化的字段中的数据分别进行序列化后,在反序列化时读取数据并重新创建对象。

方法二:

  可序列化类实现Extrernal接口,该接口必须实现readExternal和weiteExternal方法。并在该方法内对每个需要进行序列化的字段挨个进行序列化,反序列化时再挨个反序列化恢复。

Java I/O - 对象的输入输出与序列化的更多相关文章

  1. java中复制对象通过反射或序列化

    在使用缓存读取数据后修改发现缓存被修改.于是找了下复制对象的方法. 关于对象克隆 按我的理解,对象是包含引用+数据.通常变量复制都是将引用传递过去.比如: Person p1 = new Person ...

  2. Java I/O流输入输出,序列化,NIO,NIO.2

    Java IO流 File类: File类是java.io包下代表和平台无关的文件和目录,File不能访问文件内容本身. File类基本操作: System.out.println("判断文 ...

  3. 【译】Java中的对象序列化

    前言 好久没翻译simple java了,睡前来一篇. 译文链接: http://www.programcreek.com/2014/01/java-serialization/ 什么是对象序列化 在 ...

  4. Java IO(Properties/对象序列化/打印流/commons-io)

    Java IO(Properties/对象序列化/打印流/commons-io) Properties Properties 类表示了一个持久的属性集.Properties 可保存在流中或从流中加载. ...

  5. Java将对象写入文件读出——序列化与反序列化

    Java类中对象的序列化工作是通过ObjectOutputStream和ObjectInputStream来完成的. 写入: File aFile=new File("e:\\c.txt&q ...

  6. 序列化+fastjson和java各种数据对象相互转化

    序列化的定义 序列化就是一种用来处理对象流的机制 所谓对象流也就是将对象的内容进行流化.可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间. 序列化是将对象转换为容易传输的格式的过程 例 ...

  7. Java基础之对象序列化

    1. 什么是Java对象序列化 Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长.但在现 ...

  8. 【JAVA 其它流对象】

    一.PrintStream类. 该流是字节流. public class PrintStream extends FilterOutputStream implements Appendable, C ...

  9. Java提高篇——对象克隆(复制)

    假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short, ...

随机推荐

  1. Realm 处理List<String> 问题 Type parameter 'java.lang.String' is not within its bound; should implement 'io.realm.RealmModel

    public class InitAppBean extends RealmObject { private String sapling; private String logistics; pri ...

  2. idea 优先引用项目代码,而非jar包

    1.现有项目,和jar包源码在同一层目录 2.idea直接打开ccccc,右键a,b两个文件中的pom.xml加入maven project,如下: 执行1,2 步骤,在b中引入a,刚引入a在最下方, ...

  3. C#添加VisionPro控件问题

    安装完VisionPro8.2版本以后,用VS2013调用时,没有默认在工具箱内,那么就需要手动添加了,选择菜单栏---->工具----->选择工具箱项,如下图所示:           ...

  4. SSM框架指的是什么

    SSM(Spring+SpringMVC+MyBatis)框架集由Spring.SpringMVC.MyBatis三个开源框架整合而成, 常作为数据源较简单的web项目的框架. 其中spring是一个 ...

  5. hive 表新增字段后更新分区无法显示数据

    解决方案: 1.删除分区后重新跑数据 alter table drop partition(分区字段=“”): 2.新增字段运行程序后其实数据已经有了,只是查询hive的时候无法显示出来, 这个时候只 ...

  6. logstash同步mysql数据失败

      问题描述 前提: 项目采用Elasticsearch提供搜索服务,Mysql提供存储服务,通过Logstash将Mysql中数据同步到Elasticsearch. 问题: 使用logstash-j ...

  7. efcore migration

    实体------->migration------->db ---------------------------------------------------------------- ...

  8. 学习excel的使用技巧复制一列文本成新列去重

    学习excel的使用技巧复制一列文本成新列去重 其实比较简单的技巧  知道了就会  不知道就比较麻烦 直接复制到一列 找到 数据选项 删除重复项

  9. JS 数字相加

    var a = 1var b = 2var c = a + b 这样c得出来的解果是12, var c = Number(a)+Number(b) 这样c得出来的解果是3

  10. opencv常用api

    opencv常用api 一.图像及视频读写 img=cv2.imread('jiang.jpg',1)    cv2.imwrite('jiang.jpg',img)   cv2.imshow('ji ...