java序列化
来源:互联网 发布:陈奕迅评价 知乎 编辑:程序博客网 时间:2024/04/29 13:13
在JDK或者其他地方,相信各位读者都经常遇到Serializable这个词,这就是java中的序列化接口,定义在java.IO.*下面。
1.证明Serializable确实可以使对象保存状态信息。
SerializabeTest.java
package
froest.serializable;
import
java.io.ByteArrayInputStream;
import
java.io.ByteArrayOutputStream;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutputStream;
import
java.io.Serializable;
class
Data
implements
Serializable{
/**
*
*/
private
static
final
long
serialVersionUID = 2018026507498303285L;
private
int
i;
Data(
int
x) {
i = x;
}
public
String toString() {
return
Integer.toString(i);
}
}
public
class
SerializableTest
implements
Serializable {
/**
*
*/
private
static
final
long
serialVersionUID = 1303931195327594338L;
/**
* 产生随机整数
* @return
*/
private
static
int
r() {
return
(
int
) (Math.random() *
10
);
}
/**
* 设置随机数数组,保证每次生成的数都是不相同的
*/
private
Data[] d = {
new
Data(r()),
new
Data(r()),
new
Data(r())
};
/**
* 用于连接到下一个SerializableTest
*/
private
SerializableTest next;
private
char
c;
/**
* 用于设置一个链表,把多个SerializableTest连接起来
* @param i 需要生成i个SerializableTest对象
* @param x 用于表现每个SerializableTest对象的属性的区别
*/
SerializableTest(
int
i,
char
x){
System.out.println(
"SerializableTest.Constructor:"
+i);
c = x;
if
(--i >
0
){
next =
new
SerializableTest(i,(
char
)(x+
1
));
}
}
SerializableTest(){
System.out.println(
"Default constructor"
);
}
/**
* 把SerializableTest中的Data数组组织成String字符串,用于打印输出
*/
public
String toString(){
String s =
":"
+ c +
"("
;
for
(
int
i =
0
; i < d.length; i++){
s += d[i].toString();
}
s +=
")"
;
if
(
null
!= next){
s += next.toString();
}
return
s;
}
/**
* @param args
*/
public
static
void
main(String[] args) {
// TODO Auto-generated method stub
SerializableTest st =
new
SerializableTest(
6
,
'a'
);
System.out.println(
"st = "
+ st);
System.out.println(
"===================================="
);
/**
* 用文件的方式实现序列化
*/
try
{
ObjectOutputStream out =
new
ObjectOutputStream(
new
FileOutputStream(
"SerializableTestFile.out"
));
out.writeObject(
"st"
);
out.writeObject(st);
out.close();
ObjectInputStream in =
new
ObjectInputStream(
new
FileInputStream(
"SerializableTestFile.out"
));
String name1 = (String)in.readObject();
SerializableTest st1 = (SerializableTest)in.readObject();
System.out.println(
"name = "
+ name1 +
",st1 = "
+ st1);
}
catch
(Exception e){
e.printStackTrace();
}
System.out.println(
"==================================="
);
/**
* 用字节流的方式实现序列化
*/
try
{
ByteArrayOutputStream bout =
new
ByteArrayOutputStream();
ObjectOutputStream out =
new
ObjectOutputStream(bout);
out.writeObject(
"st"
);
out.writeObject(st);
out.flush();
ObjectInputStream in =
new
ObjectInputStream(
new
ByteArrayInputStream(bout.toByteArray()));
String name1 = (String)in.readObject();
SerializableTest st1 = (SerializableTest)in.readObject();
System.out.println(
"name = "
+ name1 +
",st = "
+ st1);
}
catch
(Exception e){
e.printStackTrace();
}
/**
* 获得的结果都是一样的,说明档对象序列化之后,可以通过文件或者字节流再次得到对象的状态信息
* 因为此对象的Data数组是用随机数创建的,结果相同说明没有调用对象的构建器,连默认构建器都不会调用
*/
}
}
2.关于序列化后的对象所处的文件位置
看例子:
Alien.java
此类不做任何事情,只用于实现序列化接口,可以被保存入IO流
package
froest.serializable.find;
import
java.io.Serializable;
public
class
Alien
implements
Serializable {
/**
* 序列化搜索类
*
* 若果这个文件在froest.serializable.xfile下面,把Alien.java,FreezeAlien.java,ThawAlien.java都编译一下
* 运行可以运行的
* 但是把Alien.java移动到froest.serializable.find目录下面
* FreezeAlien.java不编译,直接运行ThawAlien.java
* 发现抛出异常ClassNotFoundException:froest.serializable.xfiles.Alien,发生在in.readObject()处
* 但是Alien.class已经存在在bin.froest.serializable.find目录下面,所以问题不是出在Alien.class上面,而是在files上面
* files文件是Alien.java在froest.serializable.xfile下面编译产生的,所以files中的对象所在的位置是在froest.serializable.xfile中,
* 而现在froest.serializable.xfile包中并不能找到Alien.class文件,所以报ClassNotFoundException
*/
}
FreezeAlien.java
package
froest.serializable.find;
import
java.io.FileOutputStream;
import
java.io.ObjectOutputStream;
public
class
FreezeAlien {
/**
* @param args
*/
public
static
void
main(String[] args)
throws
Exception {
// TODO Auto-generated method stub
ObjectOutputStream out =
new
ObjectOutputStream(
new
FileOutputStream(
"files"
));
Alien alien =
new
Alien();
out.writeObject(alien);
//out.close();
}
}
ThawAlien.java
package
froest.serializable.xfiles;
import
java.io.FileInputStream;
import
java.io.ObjectInputStream;
import
froest.serializable.find.Alien;
public
class
ThawAlien {
/**
* @param args
*/
public
static
void
main(String[] args)
throws
Exception{
// TODO Auto-generated method stub
ObjectInputStream in =
new
ObjectInputStream(
new
FileInputStream(
"files"
));
Alien alien1 = (Alien)in.readObject();
//System.out.println("alien = "+alien1);
System.out.println(
"alien = "
+alien1.getClass().toString());
}
}
3.除了用Serializable实现序列化,还可以用Externalizable来实现实现序列化不会调,但是有一点区别:Serializable用默认的构造器,而Externalizable实现序列化会调用默认的构造器。
如果是用实现Serializable接口的序列化,那么如果不对属性做处理的话,所有的属性都会被保存下来,如果有敏感的信息不希望被保存的话,可以使用transient关键字,这样保存序列化对象的时候就不会把这个属性的状态信息保存起来了;另外如果这个属性字段用了transient关键字,而特殊情况下又需要保存这个属性的状态信息,那么可以在需要序列化的类中添加私有的writeObject()和readObject()方法即可。
如果用实现Externalizable接口的序列化,那么可以控制哪些字段做保存,不需要使用transient关键字,但是需要在需要实现序列化的类中添加readExternal()和writeExternal()方法来实现
下面看下我写的一些例子,在程序里面有时碰到问题就会去验证,所以加上了一些说明性文字:
Blip1.java
package
froest.serializable.externalizable;
import
java.io.Externalizable;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.ObjectInput;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutput;
import
java.io.ObjectOutputStream;
/**
* 此例说明了实现Externalizable方法后,当恢复对象的时候会调用相应类的构造器,所以如果在构造器中没有初始化,
* 那么会得到0或者null
* @author froest
*
*/
class
Blip1
implements
Externalizable {
private
int
i;
public
Blip1() {
System.out.println(
"Blip1 constructor"
);
}
public
void
readExternal(ObjectInput in)
throws
IOException,
ClassNotFoundException {
System.out.println(
"Blip1.readExternal"
);
}
public
void
writeExternal(ObjectOutput out)
throws
IOException {
System.out.println(
"Blip1.writeExternal"
);
}
public
void
setI(
int
i) {
this
.i = i;
}
public
int
get() {
return
i;
}
}
class
Blip2
implements
Externalizable {
public
Blip2() {
System.out.println(
"Blip2.constructor"
);
}
public
void
readExternal(ObjectInput in)
throws
IOException,
ClassNotFoundException {
System.out.println(
"Blip2.readExternal"
);
}
public
void
writeExternal(ObjectOutput out)
throws
IOException {
System.out.println(
"Blip2.writeExternal"
);
}
}
public
class
Blips {
public
static
void
main(String[] args) {
System.out.println(
"constructor Object"
);
Blip1 b1 =
new
Blip1();
b1.setI(
100
);
Blip2 b2 =
new
Blip2();
try
{
ObjectOutputStream out =
new
ObjectOutputStream(
new
FileOutputStream(
"Blip.x"
));
System.out.println(
"saving object"
);
out.writeObject(b1);
out.writeObject(b2);
out.close();
ObjectInputStream in =
new
ObjectInputStream(
new
FileInputStream(
"Blip.x"
));
System.out.println(
"getting object"
);
System.out.println(
"recovering b1"
);
b1 = (Blip1) in.readObject();
System.out.println(b1.get());
System.out.println(
"recovering b2"
);
b2 = (Blip2) in.readObject();
}
catch
(Exception e) {
e.printStackTrace();
}
}
}
Blip3.java
package
froest.serializable.externalizable;
import
java.io.Externalizable;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.ObjectInput;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutput;
import
java.io.ObjectOutputStream;
/**
* 用Externalizable实现对象的序列化
* 1.需要在readObject中对需要恢复的对象进行读取,否则数据都是取自默认构造器中
* 2.如果readObject中读取了需要的数据,那么即使默认构造器对该数据进行了改变,
* 也是无效的,保存什么数据,就输出什么数据,
* 3.如果在构造器(非默认)中某个属性未初始化,那么这个属性会赋值为0或者null序列化,
* 当读取数据的时候即使默认构造器对这个数据进行了初始化,也是没用的,属性的值依旧是
* 读取出来的0或者null
* @author froest
*
*/
public
class
Blip3
implements
Externalizable {
private
int
i;
private
String s;
public
Blip3(){
this
.i =
3
;
System.out.println(
"Blip3 constructor"
);
}
public
Blip3(
int
i,String s){
System.out.println(
"Blip3(int ,String)"
);
this
.i = i+
1
;
this
.s = s;
}
public
String toString(){
return
s +
" "
+ i;
}
public
void
readExternal(ObjectInput in)
throws
IOException,
ClassNotFoundException {
System.out.println(
"readExternal"
);
/**
* 1.如果把in.readInt()注释掉,那么程序运行时将报错java.io.OptionalDataException
* 而去掉注释,那么又是可以的,说明序列化后的对象属性必须按顺序读取,第一个属性取完了
* 才能取第二个属性,而且如果第一个属性未被读取,第二个属性也是不可以被读取的
* 2.如果i的值需要使用默认构造器来重新初始化,那么可以用in.readInt()来去除第一个值,但是不赋值给i
* 这样既可以取得序列化对象中得下一个属性值,又可以用默认构造器来重新给i赋值
*/
i = in.readInt();
s = (String)in.readObject();
}
public
void
writeExternal(ObjectOutput out)
throws
IOException {
System.out.println(
"writeExternal"
);
out.writeInt(i);
out.writeObject(s);
}
/**
* @param args
*/
public
static
void
main(String[] args) {
System.out.println(
"constructor objects"
);
Blip3 blip3 =
new
Blip3(
47
,
"A string"
);
System.out.println(blip3);
try
{
System.out.println(
"save object"
);
ObjectOutputStream out =
new
ObjectOutputStream(
new
FileOutputStream(
"Blip3.out"
));
out.writeObject(
"save blip3"
);
out.writeObject(blip3);
out.close();
System.out.println(
"read object"
);
ObjectInputStream in =
new
ObjectInputStream(
new
FileInputStream(
"Blip3.out"
));
/**
*String ss = (String)in.readObject();这行代码必须存在,如果我注释
*或者放到Blip3 b = (Blip3)in.readObject();之后,那么都将报错,由此可以判断
*序列化到文件是有顺序的,必须什么顺序存入,就什么顺序取出,如果之前那个不取出,
*下一个也不能被取出
*/
String ss = (String)in.readObject();
Blip3 b = (Blip3)in.readObject();
System.out.println(ss);
System.out.println(b);
}
catch
(Exception e){
e.printStackTrace();
}
}
}
SerialCtr.java
package
froest.serializable;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutputStream;
import
java.io.Serializable;
public
class
SerialCtr
implements
Serializable {
private
static
final
long
serialVersionUID = 5442274055003105383L;
private
String name;
private
transient
String password;
public
SerialCtr(String a,String b){
this
.name = a;
this
.password = b;
}
public
String toString(){
return
"Not transient : "
+ name +
"\n transient : "
+ password;
}
private
void
writeObject(ObjectOutputStream out)
throws
IOException{
/**
* 1.如果对象实现Serializable,但是又在自己的对象中定义了writeObject()和readObject()方法
* 那么编译器就会放弃Serializable接口的默认的序列化机制,但是可以在对象的writeObject()和readObject()中
* 调用out.defaultWriteObject()和in.defaultReadObject()方法来开启默认的序列化机制;如果对于transient字段
* 我们可以调用对象的writeObject()和readObject()方法来序列化,这就实现了transient属性不能序列化的缺点,因为
* 有的时候需要让transient属性页得到序列化
* 2.在这里的readObject()方法可以只取出一个属性,因为这是在序列化对象的内部,而序列化对象的外部是
* 把这整个序列化对象取出来,所以这里与顺序和取多少个属性无关(其他例子有说到顺序的问题,这是一点区别)
*/
out.defaultWriteObject();
out.writeObject(password);
}
private
void
readObject(ObjectInputStream in)
throws
IOException,ClassNotFoundException{
in.defaultReadObject();
password = (String)in.readObject();
}
/**
* @param args
*/
public
static
void
main(String[] args) {
SerialCtr s =
new
SerialCtr(
"LH"
,
"1qaz"
);
System.out.println(
"Before Serial: \n"
+ s);
//ByteArrayOutputStream buf = new ByteArrayOutputStream();
try
{
//ObjectOutputStream out = new ObjectOutputStream(buf);
ObjectOutputStream out =
new
ObjectOutputStream(
new
FileOutputStream(
"SerialExternal.out"
));
out.writeObject(s);
out.close();
//ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()));
ObjectInputStream in =
new
ObjectInputStream(
new
FileInputStream(
"SerialExternal.out"
));
SerialCtr s1 = (SerialCtr)in.readObject();
System.out.println(
"After Serial: \n"
+ s1);
}
catch
(Exception e){
e.printStackTrace();
}
}
}
4.若果一个类A中有类B和类C的引用,而类B中有类D的数组的引用,类C中有类E的引用,那么序列化机制是否可以正确的还原呢?
还是看下书上的例子吧:
package
froest.serializable;
import
java.io.ByteArrayInputStream;
import
java.io.ByteArrayOutputStream;
import
java.io.ObjectInputStream;
import
java.io.ObjectOutputStream;
import
java.io.Serializable;
import
java.util.Vector;
class
House
implements
Serializable{
private
static
final
long
serialVersionUID = 7726379233155420025L;
}
class
Animal
implements
Serializable{
private
static
final
long
serialVersionUID = 8278386775550453403L;
private
String name;
private
House house;
public
Animal(String name,House house){
this
.name = name;
this
.house = house;
}
public
String toString(){
return
name +
"["
+
super
.getClass() +
"], "
+ house +
"\n"
;
}
}
@SuppressWarnings
(
"unchecked"
)
public
class
MyWorld {
public
static
void
main(String[] args) {
House house =
new
House();
Vector animals =
new
Vector();
animals.add(
new
Animal(
"dog"
, house));
animals.add(
new
Animal(
"cat"
, house));
animals.add(
new
Animal(
"mouse"
, house));
System.out.println(animals);
try
{
ByteArrayOutputStream buf1 =
new
ByteArrayOutputStream();
ObjectOutputStream out1 =
new
ObjectOutputStream(buf1);
out1.writeObject(animals);
out1.writeObject(animals);
ByteArrayOutputStream buf2 =
new
ByteArrayOutputStream();
ObjectOutputStream out2 =
new
ObjectOutputStream(buf2);
out2.writeObject(animals);
out1.close();
out2.close();
ObjectInputStream in1 =
new
ObjectInputStream(
new
ByteArrayInputStream(buf1.toByteArray()));
ObjectInputStream in2 =
new
ObjectInputStream(
new
ByteArrayInputStream(buf2.toByteArray()));
Vector animal1 = (Vector)in1.readObject();
Vector animal2 = (Vector)in1.readObject();
Vector animal3 = (Vector)in2.readObject();
/**
* 1.animal1和animal2是同一个数据流中的对象,animal3是另一个数据流的对象,当对象的被恢复的时候可以看到
* house地址的区别:animal1和animal2中是相同的,而他们跟animal3是不同的,所以在同一个数据流中,相同的
* 对象不会重复出现。
* 2.当数据流不同的时候,所生成的对象网也会不同
*/
System.out.println(animal1);
System.out.println(animal2);
System.out.println(animal3);
System.out.println(animals);
}
catch
(Exception e){
e.printStackTrace();
}
}
}
只要将对象序列化到单独一个数据流里面,就能恢复获得与以前一样的对象网,不会不慎造成对象的重复。要想保存系统状态,最安全的方法是将构成系统状态的所有对象都置入单个集合内,并在一次操作力完成那个集合的写入,这样就只需一次方法调用便可恢复。
这是我在序列化学习中学到的一些知识,若有不正确之处,望大家斧正,小菜拜谢。
------>froest
- Serializable java序列化
- Java对象序列化
- java序列化-Serializable
- Serializable java序列化
- Serializable java序列化
- Java对象序列化
- Java对象序列化
- Java对象序列化
- Serializable java序列化
- JAVA序列化Serializable
- java对象序列化
- Java 对象序列化
- DEMO-JAVA序列化
- Java 对象序列化
- java 序列化
- Java对象序列化
- Serializable java序列化
- java序列化介绍
- npm常用命令
- C#winform程序安装时自动卸载新版本覆盖旧版本
- Console命令详解,让调试js代码变得更简单
- How to Tune Java Garbage Collection
- C语言插入排序
- java序列化
- 预定义超全局数组$_GET
- Hibernate使用sql语句实现多表关联查询
- 【HDU3341】AC自动机状态压缩DP,或者说hash枚举DP,-------出题人卡常数都是狗!!!!!
- How to Monitor Java Garbage Collection
- C++ 临时变量做函数参数时的生命周期
- Java中super的几种用法并与this的区别
- java 正则 replace和replaceAll
- Linux常用命令(3)-文件和目录管理