java 序列化机制应用场景_java序列化机制

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类:

Java代码

456f08de0bc2

publicclassMale {

privateString name;

privateintage;

publicMale() {

}

publicString getName() {

returnname;

}

publicvoidsetName(String name) {

this.name = name;

}

publicintgetAge() {

returnage;

}

publicvoidsetAge(intage) {

this.age = age;

}

@Override

publicString toString() {

return"Male [name="+ name +", age="+ age +"]";

}

}

Male的子类MaleStudent则实现了Serializable接口:

Java代码

456f08de0bc2

publicclassMaleStudentextendsMaleimplementsSerializable {

privateintstudentID;

publicMaleStudent(intstudentID) {

super();

this.studentID = studentID;

}

publicintgetStudentID() {

returnstudentID;

}

publicvoidsetStudentID(intstudentID) {

this.studentID = studentID;

}

@Override

publicString toString() {

return"MaleStudent [studentID="+ studentID +"]";

}

}

则反序列化MaleStudent对象后, 其name和age属性都发生了改变:

Java代码

456f08de0bc2

MaleStudent student =newMaleStudent(1);

student.setName("coolxing");

student.setAge(24);

ObjectOutputStream out =newObjectOutputStream(newFileOutputStream(

"male.obj"));

out.writeObject(student);

out.close();

ObjectInputStream in =newObjectInputStream(newFileInputStream(

"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()方法即可, 典型的应用场景是对密码之类的敏感成员进行加密:

Java代码

456f08de0bc2

publicclassUserimplementsSerializable {

privatestaticfinallongserialVersionUID = 1L;

privateString userName;

privateString passWord;

publicUser(String userName, String passWord) {

this.userName = userName;

this.passWord = passWord;

}

publicUser() {

}

privatevoidwriteObject(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();

}

}

privatevoidreadObject(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. 重复存储问题. 如果将同一个对象多次写入文件, 会有怎样的结果?

Java代码

456f08de0bc2

File file =newFile("user.obj");

User user =newUser("coolxing","1987810");

ObjectOutputStream out =newObjectOutputStream(newFileOutputStream("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 =newObjectInputStream(newFileInputStream("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接口呢? 这是我疑惑的地方, 文档中并没有说明.可以确定的是,

只要集合中的元素是可序列化的, 序列化过程就不会出错.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java序列化和反序列化有以下几个应用场景: 1. 网络传输:将Java对象序列化后通过网络传输到另一台机器上,实现分布式系统中的远程调用。 2. 缓存:将Java对象序列化后存储到缓存中,提高系统性能。 3. 消息队列:将Java对象序列化后发送到消息队列中,实现异步处理。 4. 持久化存储:将Java对象序列化后存储到磁盘中,实现数据持久化。 以下是Java序列化和反序列化的示例代码: ```java import java.io.*; public class SerializationDemo { public static void main(String[] args) { // 序列化对象 try { // 创建一个Person对象 Person person = new Person("Tom", 18); // 创建一个ObjectOutputStream对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.txt")); // 将person对象写入到文件中 oos.writeObject(person); // 关闭流 oos.close(); } catch (IOException e) { e.printStackTrace(); } // 反序列化对象 try { // 创建一个ObjectInputStream对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.txt")); // 从文件中读取Person对象 Person person = (Person) ois.readObject(); // 输出Person对象的信息 System.out.println(person.getName() + " " + person.getAge()); // 关闭流 ois.close(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } } // Person类需要实现Serializable接口才能被序列化 class Person implements Serializable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public int getAge() { return age; } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值