[coolxing按: 转载请注明作者和出处, 如有谬误, 欢迎在评论中指正.]
java的序列化机制支持将对象序列化为本地文件或者通过网络传输至别处, 而反序列化则可以读取流中的数据, 并将其转换为java对象. 被序列化的类需要实现Serializable接口, 使用ObjectInputStream和ObjectOutputStream进行对象的读写操作.
当然, java的序列化机制并非如此简单, 以下是个人总结的一些知识点:
1. 对象读取的顺序应该和写入的顺序一致, 而且读取的次数不能超过已写入对象的个数. 比如文件中仅仅存在2个对象, 就不能连续调用3次readObject()方法, 除非调用了reset, skip等对流重新定位的方法.
2. java序列化机制针对的是对象, 而不是类. 因此只有非静态成员变量才会被序列化成二进制数据.
3. 使用transient关键字修饰的成员变量不会被序列化为二进制数据.
4. 将对象序列化为二进制数据, 将二进制数据反序列化为java对象, 这两个操作可能位于不同的应用中, 甚至也可能在不同的计算机上进行. 需要保证这两种场合下都有class文件, 在序列化处和反序列处的class文件需要完成一致, 包括包名.
5. 序列化ID的作用. 上面的Person类中定义了序列化ID: private static final long serialVersionUID = 1L;
这是一个非强制定义的静态成员, 如果不定义序列化ID, 那么eclipse会给出一个黄色的警告, 这个警告可以忽略.
考虑这样的情形: Person类定义了序列化ID, 且序列化对象时serialVersionUID的值为1, 而反序列化时serialVersionUID的值不为1, 那么此时将无法反序列化成功. 所以序列化ID可以用来限制某些用户的反序列化.
6. 父类的序列化问题. 根据java的对象实例化机制可知, 创建一个子类对象的过程中, 会创建其父类对象, 反序列化也不例外. 如果一个类实现了Serializable接口, 而其父类却没有实现Serializable接口, 那么在反序列化时, 虚拟机会调用父类的无参构造函数创建父类对象, 因此反序列化后父类成员变量的值为调用无参构造函数之后的值. 如果父类既没有实现Serializable接口, 也不存在无参构造函数, 那么在反序列化时将发生程序错误.
假设存在一个没有实现Serializable接口的Male类:
public class Male {
private String name;
private int age;
public Male() {
}
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;
}
@Override
public String toString() {
return "Male [name=" + name + ", age=" + age + "]";
}
}
Male的子类MaleStudent则实现了Serializable接口:
public class MaleStudent extends Male implements Serializable {
private int studentID;
public MaleStudent(int studentID) {
super();
this.studentID = studentID;
}
public int getStudentID() {
return studentID;
}
public void setStudentID(int studentID) {
this.studentID = studentID;
}
@Override
public String toString() {
return "MaleStudent [studentID=" + studentID + "]";
}
}
则反序列化MaleStudent对象后, 其name和age属性都发生了改变:
MaleStudent student = new MaleStudent(1);
student.setName("coolxing");
student.setAge(24);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
"male.obj"));
out.writeObject(student);
out.close();
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
"male.obj"));
MaleStudent maleStudent = (MaleStudent) in.readObject();
System.out.println(maleStudent.getName() + ", " + maleStudent.getAge()
+ ", " + maleStudent.getStudentID());
程序的输出为:
name = null, age = 0, studentID = 1
可见, 父类属性值都"丢失"了, name和age都是调用Male无参构造函数之后的值. 假设将Male类中的无参构造函数删除, 再加上一个有参的构造函数, 在反序列化时将发生程序错误.
7. 自定义序列化和反序列化操作. 如果为了某些特殊需求, 需要自定义序列化和反序列化操作, 只要重写实现了Serializable接口的类中的writeObject()和readObject()方法即可, 典型的应用场景是对密码之类的敏感成员进行加密:
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private String userName;
private String passWord;
public User(String userName, String passWord) {
this.userName = userName;
this.passWord = passWord;
}
public User() {
}
private void writeObject(ObjectOutputStream out) {
try {
PutField field = out.putFields();
field.put("userName", userName);
System.out.println("加密前: passWord = " + passWord);
// 模拟加密
passWord = passWord + "1";
System.out.println("加密后: passWord = " + passWord);
field.put("passWord", passWord);
out.writeFields();
} catch (IOException e) {
e.printStackTrace();
}
}
private void readObject(ObjectInputStream in) {
try {
GetField field = in.readFields();
userName = (String) field.get("userName", "");
passWord = (String) field.get("passWord", "");
System.out.println("读取的原始passWord = " + passWord);
// 模拟解密
passWord = passWord.substring(0, passWord.length() - 1);
System.out.println("解密后的passWord = " + passWord);
} catch (Exception e) {
e.printStackTrace();
}
}
}
程序的输出为:
加密前: passWord = 1987810
加密后: passWord = 19878101
读取的原始passWord = 19878101
解密后的passWord = 1987810
User [userName=coolxing, passWord=1987810]
8. 重复存储问题. 如果将同一个对象多次写入文件, 会有怎样的结果?
File file = new File("user.obj");
User user = new User("coolxing", "1987810");
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.obj"));
out.writeObject(user);
System.out.println(file.length());
// 改变userName的值后再次将user对象存入文件
user.setUserName("min");
out.writeObject(user);
System.out.println(file.length());
ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.obj"));
User userFromFile1 = (User) in.readObject();
User userFromFile2 = (User) in.readObject();
System.out.println(userFromFile1.toString());
System.out.println(userFromFile2.toString());
程序的输出结果为:
109
114
User [userName=coolxing, passWord=1987810]
User [userName=coolxing, passWord=1987810]
程序中调用了2次readObject()方法, 且没有出现程序错误, 由此可知确实向文件中写入了2个对象. 第二次写入user对象时, 系统发现文件中已经存在user对象, 将不再存入user对象的内容, 只写入一个引用和一些控制信息, 所以第二次写入user对象后文件的大小增加的很少, 而且userName也没有发生改变.
9. 如果一个类中包含非基本数据类型的成员变量, 那么不仅类本身需要实现Serializable接口, 类中的非基本数据类型也需要实现Serializable接口. java的一些核心类, 如String,
基本数据类型的包装类等都已经实现了Serializable接口, 使用的时候可以查看文档.
10. 对于包含集合型成员的类来说, 不仅类本身需要现Serializable接口, 集合中所存储的元素也要实现Serializable接口.
那么集合类(List, Set, Map)到底有没有实现Serializable接口呢? 这是我疑惑的地方, 文档中并没有说明. 可以确定的是,
只要集合中的元素是可序列化的, 序列化过程就不会出错.
关于java序列化机制的一些其他方面的叙述, 请参见http://www.ibm.com/developerworks/cn/java/j-5things1/index.html?ca=drs-cn-0504
分享到:
相关推荐
Java序列化机制是Java平台提供的一种标准方法,用于将对象的状态转换为字节序列,以便在网络中传输或存储在磁盘、数据库等持久化介质中。这一机制在分布式环境、远程通信、持久化存储和数据交换等多个场景下具有广泛...
Java序列化机制就是为了满足这种需求而产生的。通过序列化,我们可以将对象转换为字节流,从而使得对象可以通过网络传输或者持久化存储。 #### 三、实现序列化的步骤 1. **实现Serializable接口**:为了使一个对象...
首先,我们需要创建一个实现了`Serializable`接口的类`TestSerial`,这样Java序列化机制才能处理这个类的对象。 接下来,我们编写代码将对象序列化并输出为字节流,存储在一个临时文件`temp.out`中。 ```java ...
在给定的链接"Java序列化机制(2)- serialVersionUID 实验"中,博主通过一个实验详细解释了`serialVersionUID`的作用和重要性。实验可能包括以下步骤: 1. 创建一个实现`Serializable`接口的简单类,并运行序列化...
Java 序列化机制 Java 序列化机制是一种将对象状态写入 Byte 流里的技术,并可以从其它地方把该 Byte 流里的数据读出来,重新构造一个相同的对象。这种机制允许将对象通过网络进行传播,并可以随时把对象持久化到...
序列化ID,即`serialVersionUID`,是Java序列化机制中一个关键的概念。它是一个类的唯一标识符,用于在序列化和反序列化过程中确定类的版本一致性。如果序列化对象和反序列化对象的`serialVersionUID`不匹配,将会抛...
通过实例深入了解 Java 序列化,可以帮助开发人员更好地理解 Java 序列化机制,避免一些常见的问题,提高开发效率。本文通过分析一些真实情境,帮助读者轻松牢记 Java 序列化中的一些高级认识。
Java 序列化和反序列化是 Java 语言中的一种机制,用于将对象转换为字节流,以便在网络上传输或存储。序列化是将对象转换为字节流的过程,而反序列化是将字节流转换回对象的过程。 在 Java 中,序列化和反序列化是...
Java序列化是Java平台中的一种持久化机制,它允许对象的状态被转换成字节流,以便存储、网络传输或在不同时间点恢复...理解和熟练掌握序列化机制,以及知道如何避免其潜在问题,对于任何Java开发者来说都是至关重要的。
1. **Java序列化机制**:Java对象序列化是通过实现`Serializable`接口来标记一个类可被序列化。`ObjectOutputStream`用于将对象写入流,`ObjectInputStream`用于从流中读取并反序列化对象。 2. **易受攻击的库**:...
Java 序列化机制是通过实现 java.io.Serializable 接口来实现的。该接口没有任何方法,但它标志着该类的对象可以被序列化。如果一个类实现了 Serializable 接口,那么该类的所有属性必须是可序列化的。如果有一个...
通过实例了解java序列化机制 知识点1: 序列化机制的定义 Java 序列化机制是指对象通过写出描述自己状态的数值来记录自己的过程,即将对象表示成一系列有序字节。序列化机制主要用于对象的存储和传输。 知识点2: ...
Protocol Buffer(简称PB)是Google开发的一种高效的数据序列化协议,而Java序列化是Java平台内置的一种序列化机制。两者的主要目标都是将对象转化为字节数组,便于在网络传输、持久化存储等场景中使用。然而,它们...
- Java序列化是Java平台提供的一种持久化机制,它允许将对象转换为字节序列,这样就可以保存在文件中或在网络上传输。 - 序列化不仅可以用来持久化数据,还可以用于进程间通信(IPC)和远程方法调用(RMI)。 2. ...
Java序列化机制的优点在于它提供了一种标准的方式来处理对象的持久化和在网络间的传输。然而,序列化也存在安全风险,比如序列化可能导致远程代码执行攻击。因此,对于敏感信息或复杂对象结构,应谨慎使用序列化,并...
以下是关于Java序列化机制和原理的详细解释: 1. **序列化接口**: Java中的序列化是通过实现`Serializable`接口来实现的。这个接口没有任何方法,它的存在仅仅是为了标记一个类可以被序列化。如以下示例所示: `...
Java 序列化机制 Java 序列化是将一个对象的状态写入一个 Byte 流里,并且可以从其它地方把该 Byte 流里的数据读出来,重新构造一个相同的对象。这种机制允许你将对象通过网络进行传播,并可以随时把对象持久化到...
Java序列化是Java平台提供的一种持久化机制,它允许我们将一个Java对象转换为字节流,以便存储到磁盘上,或者通过网络进行传输。这使得我们可以保存和恢复对象的状态。实现序列化的类需要实现`Serializable`接口,...
- **重要性**:Java序列化机制依赖于`serialVersionUID`字段来验证序列化版本的一致性。 - **默认生成**:当类首次序列化时,JVM会根据类的结构自动生成一个`serialVersionUID`。 - **手动指定**:为了确保反序列...