转自:http://coolxing.iteye.com/blog/1222783#bc2350923
博客分类:
[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类:
- publicclass Male {
- private String name;
- privateint age;
- public Male() {
- }
- public String getName() {
- return name;
- }
- publicvoid setName(String name) {
- this.name = name;
- }
- publicint getAge() {
- return age;
- }
- publicvoid setAge(int age) {
- this.age = age;
- }
- @Override
- public String toString() {
- return"Male [name=" + name + ", age=" + age + "]";
- }
- }
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接口:
- publicclass MaleStudent extends Male implements Serializable {
- privateint studentID;
- public MaleStudent(int studentID) {
- super();
- this.studentID = studentID;
- }
- publicint getStudentID() {
- return studentID;
- }
- publicvoid setStudentID(int studentID) {
- this.studentID = studentID;
- }
- @Override
- public String toString() {
- return"MaleStudent [studentID=" + studentID + "]";
- }
- }
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());
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
- publicclass User implements Serializable {
- privatestaticfinallong serialVersionUID = 1L;
- private String userName;
- private String passWord;
- public User(String userName, String passWord) {
- this.userName = userName;
- this.passWord = passWord;
- }
- public User() {
- }
- privatevoid 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();
- }
- }
- privatevoid 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();
- }
- }
- }
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(); } } }
- 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());
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时积累的宝贵经验,旨在帮助初学者或有经验的程序员巩固基础,提升技能。 笔记内容可能涵盖以下几个主要部分: 1. **Java基础**:这部分通常包括Java的...
3. **输入输出与文件操作**:学习Java I/O流,理解流的分类及如何进行文件操作,包括读写文件、序列化和反序列化。 4. **多线程**:理解线程的概念,学习如何创建和管理线程,包括同步机制(如synchronized关键字和...
6. **I/O流**:Java I/O流用于处理输入输出,包括字节流和字符流,以及对象序列化。掌握InputStream、OutputStream、Reader、Writer及其子类,以及缓冲流、转换流和文件流的使用。 7. **数据库操作**:Java通过JDBC...
8. **IO流**:掌握输入输出流的概念,包括文件读写、对象序列化和反序列化,以及使用BufferedReader和PrintWriter进行文本处理。 9. **多线程**:了解线程的基本概念,如何创建和管理线程,同步机制(synchronized...
- 数据流操作,如ObjectInputStream和ObjectOutputStream用于对象的序列化和反序列化。 8. **多线程** - 理解线程的基本概念,如何创建和控制线程(Thread类和Runnable接口)。 - 线程同步机制,如synchronized...
这些笔记是作者在深入学习Java过程中积累的经验总结,旨在帮助初学者快速理解和掌握Java编程。 1. **Java基础** - **数据类型**: Java分为基本数据类型(如整型、浮点型、字符型、布尔型)和引用数据类型(类、...
7. **IO流**:掌握字节流和字符流的区别,以及缓冲流、对象序列化、文件操作、管道流等。 8. **反射**:理解反射的概念,如何动态获取类信息,创建对象,调用方法,修改字段值等。 9. **泛型**:泛型的引入是为了...
Java的核心知识涵盖了集合框架、序列化、流处理、网络编程、多线程、反射、事件处理、NIO、国际化等内容。这些技术是构建复杂应用的基础,对于提升开发效率至关重要。 #### 3. 理解JVM工作原理 深入了解JVM的工作...
3. **IO流**:学习输入输出流的使用,包括文件操作、对象序列化和网络通信。 4. **多线程**:掌握线程的创建、同步、通信和生命周期管理,理解线程池的原理。 5. **反射机制**:通过反射可以动态地获取类的信息并...
这在插件系统、序列化、动态代理等方面有广泛应用。 9. **Java标准库**:掌握Java标准库中的常用类和API,如Math类、Date和Calendar类、Collections工具类等,可以极大地提高编程效率。 10. **JDBC数据库编程**:...
6. **IO流**:理解输入输出流的概念,包括字节流和字符流,以及缓冲流、对象序列化和文件操作。 7. **多线程**:学习如何创建和管理线程,掌握同步机制(synchronized关键字、wait/notify、锁对象)以解决并发问题...
内容概要:本文为 Java 学习路线图,涵盖从基本语法到高级主题的内容,包括数据类型与变量、条件语句、循环结构、面向对象编程、内存管理、集合框架、序列化机制以及使用 Gradle/Maven/Ant 进行项目构建等方面的基础...
7. **输入/输出流**:介绍I/O流的概念,包括文件操作、字节流和字符流,以及缓冲区和对象序列化。 8. **集合框架**:Java集合框架是处理对象组的重要工具,包括ArrayList、LinkedList、HashSet、HashMap等容器的...
2.2 输入/输出(I/O):Java提供了丰富的I/O流类库,支持文件操作、网络通信、序列化等多种功能。流是数据传输的通道,分为字节流和字符流两大类。 2.3 多线程:Java内置了对多线程的支持,可以同时执行多个任务,...
8. **输入输出流**:Java的IO流系统用于读写数据,包括文件操作、字符流和字节流、缓冲流、对象序列化等。 9. **集合框架**:Java集合框架提供了一系列接口和类,如List、Set、Map,用于存储和管理对象。理解它们的...
通过实践,你可以更好地理解Java编程的细节,积累经验,并学会解决实际问题。 总之,这个"java课件完整版"涵盖了从入门到进阶的全面Java知识,无论是初学者还是有经验的开发者,都能从中受益。通过深入学习和实践,...
5. **IO流**:讲解了Java的输入/输出流系统,包括文件流、字节流、字符流、对象序列化以及缓冲流的使用,以及NIO(非阻塞I/O)的简介。 6. **多线程**:Java提供了丰富的多线程支持,笔记中会详细介绍线程的创建、...
7. **第11周**:十一周可能介绍I/O流,包括文件读写、字符流、字节流、缓冲流以及对象序列化,这些都是处理数据传输的关键技术。 8. **第12周**:十二周可能涉及Java的反射API,这是一个强大的工具,允许程序在运行...
掌握字节流和字符流的区别,以及缓冲流、对象序列化等高级IO概念,可以提高程序的效率和功能。 6. **多线程**:Java内置对多线程的支持,通过Thread类和Runnable接口可以实现并发执行。了解同步机制(如...