Java I/O - 对象的输入输出与序列化
先说概念:
一、相关概念
序列化:把内存中的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 - 对象的输入输出与序列化的更多相关文章
- java中复制对象通过反射或序列化
在使用缓存读取数据后修改发现缓存被修改.于是找了下复制对象的方法. 关于对象克隆 按我的理解,对象是包含引用+数据.通常变量复制都是将引用传递过去.比如: Person p1 = new Person ...
- Java I/O流输入输出,序列化,NIO,NIO.2
Java IO流 File类: File类是java.io包下代表和平台无关的文件和目录,File不能访问文件内容本身. File类基本操作: System.out.println("判断文 ...
- 【译】Java中的对象序列化
前言 好久没翻译simple java了,睡前来一篇. 译文链接: http://www.programcreek.com/2014/01/java-serialization/ 什么是对象序列化 在 ...
- Java IO(Properties/对象序列化/打印流/commons-io)
Java IO(Properties/对象序列化/打印流/commons-io) Properties Properties 类表示了一个持久的属性集.Properties 可保存在流中或从流中加载. ...
- Java将对象写入文件读出——序列化与反序列化
Java类中对象的序列化工作是通过ObjectOutputStream和ObjectInputStream来完成的. 写入: File aFile=new File("e:\\c.txt&q ...
- 序列化+fastjson和java各种数据对象相互转化
序列化的定义 序列化就是一种用来处理对象流的机制 所谓对象流也就是将对象的内容进行流化.可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间. 序列化是将对象转换为容易传输的格式的过程 例 ...
- Java基础之对象序列化
1. 什么是Java对象序列化 Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长.但在现 ...
- 【JAVA 其它流对象】
一.PrintStream类. 该流是字节流. public class PrintStream extends FilterOutputStream implements Appendable, C ...
- Java提高篇——对象克隆(复制)
假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(boolean,char,byte,short, ...
随机推荐
- JavaScript 正则表达式学习笔记
定义规则让计算机去处理字符串正则表达式写法: //第一种 var reg = new RegExp('ab'); //第二种 var reg = /ab/; 量词:{} [a-z] 从小写a-z [A ...
- 解决idea spring boot项目中target中没有同步更新最新目录文件及资源
idea不像eclipse那样自动将新保存的文件或目录及其他资源更新到target目录中,必须在pom.xml中设置 <build> <resources> <resou ...
- 【转载】Google 程序员消灭 Bug 的 5 大法宝!
遇到问题怎么办?还能怎么办,解决呗.那到底怎么解决呢?你是有什么惯用的逻辑模式.解决策略,还是全靠直觉手感? 本文中,一位 Google 程序员将“现场”演示其解决编程问题的始末,看看有套路的问题解决 ...
- 解决TensorFlow程序无限制占用GPU
今天遇到一个奇怪的现象,使用tensorflow-gpu的时候,出现内存超额~~如果我训练什么大型数据也就算了,关键我就写了一个y=W*x.......显示如下图所示: 程序如下: import te ...
- 基于KMP算法的字符串模式匹配问题
基于KMP算法的字符匹配问题 反正整个清明都在纠结这玩意...差点我以为下个清明要给自己过了. 至于大体的理解,我就不再多说了(还要画图多麻烦鸭),我参考了以下两个博客,写的真的不错,我放了超链接,点 ...
- Linux修改hostname与免密码登录
修改hostname [root@centos7 ~]$ hostnamectl set-hostname hadoop001 # 使用这个命令会立即生效且重启也生效 [root@centos7 ~] ...
- python 列表生成式,生成器&迭代器
列表生成式: 需求:要对列表 [0,1,2,3,4,5,6,7,8,9]的每个元素加1 用列表生成式一步搞定: li = [i+1 for i in range(10)] # 这种写法就叫列表生成式 ...
- DEDECMS 多站用一个站图片
function replaceurl($newurl) { $newurl=str_replace('src="/uploads/allimg/','src="xxx.com/u ...
- 43_redux_counter应用_使用redux调试用具
1.要在chrome中安装插件 redux-devtools_2_12_1.crx 2.在开发工具注入 npm install --save-dev redux-devtools-extension ...
- 自然语言推断(NLI)、文本相似度相关开源项目推荐(Pytorch 实现)
Awesome-Repositories-for-NLI-and-Semantic-Similarity mainly record pytorch implementations for NLI a ...