Java序列化(Serializable)与反序列化详解
2018-01-08 19:50
197 查看
什么是序列化?
Java序列化是在JDK 1.1中引入的,是Java内核的重要特性之一。Java序列化API允许我们将一个对象转换为流,并通过网络发送,或将其存入文件或数据库以便未来使用,
反序列化则是将对象流转换为实际程序中使用的Java对象的过程。
序列化有啥用?
1.暂存大对象2.Java对象需要持久化的时候
3.需要在网络,例如socket中传输Java对象
因为数据只能够以二进制的形式在网络中进行传输,因此当把对象通过网络发送出去之前需要先序列化成二进制数据,在接收 端读到二进制数据之后反序列化成Java对象
4.深度克隆(复制)
5.跨虚拟机通信
代码怎么写?
1.序列化例子
实体类:Student.javapackage com.dylan.serialization; import java.io.Serializable; /** * 学生类 * * @author xusucheng * @create 2018-01-08 **/ public class Student implements Serializable { private int id; private String name; private String gender; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public Student(int id, String name, String gender) { this.id = id; this.name = name; this.gender = gender; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", gender='" + gender + '\'' + '}'; } }
package com.dylan.serialization; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.util.ArrayList; import java.util.List; /** * 序列化例子 * * @author xusucheng * @create 2018-01-08 **/ public class SerializationDemo { public static void main(String[] args) throws IOException{ //学生1 Student s1 = new Student(101,"Jack","Male"); //学生2 Student s2 = new Student(102,"Lily","Female"); List<Student> list = new ArrayList<>(); list.add(s1); list.add(s2); //创建文件流 FileOutputStream fos = new FileOutputStream("D:\\test\\student01.ser"); ObjectOutputStream os = new ObjectOutputStream(fos); os.writeObject(list); os.close(); System.out.println("序列化完成!"); } }
2.反序列化例子
package com.dylan.serialization; import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.util.ArrayList; import java.util.List; /** * 反序列化例子 * * @author xusucheng * @create 2018-01-08 **/ public class DeserializationDemo { public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("D:\\test\\student01.ser"); ObjectInputStream is = new ObjectInputStream(fis); Object obj = null; List<Student> list = new ArrayList<>(); try { list = (List<Student>)is.readObject(); } catch (ClassNotFoundException e) { e.printStackTrace(); } is.close(); //遍历list,输出 for (Student student:list){ System.out.println(student.toString()); } } }
3.serialVersionUID的作用
可以看到上面序列化例子中的学生实体类只是实现了Serializable接口,假如我现在接到客户需求,要调整Student类,增加学生年龄属性,调整后如下:public class Student implements Serializable { private int id; private String name; private String gender; private int age; ...此时,我在去执行反序列化程序,结果报错了:
啥玩意?就是说Stuent类调整后,其序列化版本ID发生了变化,java又是根据这个ID来判断是不是同一个类。ID不一样则无法反序列化!
事实上如果一个类没有定义serialVersionUID,它会自动计算出来并分配给该类。当发生以下情况,这个ID就会改变:
在类中添加一些新的变量。
将变量从transient转变为非tansient,对于序列化来说,就像是新加入了一个变量而已
将变量从静态的转变为非静态的,对于序列化来说,就也像是新加入了一个变量而已
这个时候就需要我们手动指定这个serialVersionUID了,它告诉jvm,嗨,这个学生类我更新了一下,反序列化的时候记得尽量向后兼容,别报错。通常我们有以下几种方式:
serialVersionUID生成方式:
1)直接写固定的1L;
2)利用JDK自带serialver命令:切到类路径下
3)IDEA生成serialVersionUID
Intellij IDEA 默认没启用这个功能
Preferences->Editor->Inspections->Serialization issues->Serializable class without serialVersionUID 勾上
应用之后就开启了检测功能
在你的class名前(一定是光标移动到类名前):按(mac:option+return)或者( win:Alt+Enter) 就会提示自动创建 serialVersionUID 了。
加上serialVersionUID后的实体类如下:
public class Student implements Serializable { private static final long serialVersionUID = -2110227637642817458L; private int id; private String name; private String gender; private int age; ...再次执行反序列化又恢复正常了:
Student{id=101, name='Jack', gender='Male'}
Student{id=102, name='Lily', gender='Female'}
在这里有一个建议,如果没有特殊需求,就是用默认的 1L 就可以,这样可以确保代码一致时反序列化成功。
那么随机生成的序列化 ID 有什么作用呢,有些时候,通过改变序列化 ID 可以用来限制某些用户的使用。
4.static和transient属性无法序列化
transient翻译过来就是短暂的,瞬时的。如果在可序列化的类中某个成员变量加了这个修饰,则说明它只在jvm运行时才保存值,序列化时不会保存的。为了证明这一点,我们调整一下Student类,加入1个静态变量mess和1个transient变量password。调整后的类如下:
package com.dylan.serialization; import java.io.Serializable; /** * 学生类 * * @author xusucheng * @create 2018-01-08 **/ public class Student implements Serializable { private static final long serialVersionUID = -2110227637642817458L; private static String mess="null"; private int id; private String name; private String gender; private int age; private transient String password; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public Student(int id, String name, String gender) { this.id = id; this.name = name; this.gender = gender; mess = "something"; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", gender='" + gender + '\'' + ", age=" + age + ", password='" + password + '\'' + ", mess='" + mess + '\'' + '}'; } }
再次执行序列化,反序列化后:
结论没问题。
5.序列化实战:Socket传输序列化对象例子
package com.dylan.serialization; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.ServerSocket; import java.net.Socket; /** * @author xusucheng * @create 2018-01-08 **/ public class Server { public static void main(String[] args) { //The client is used to handle connections with a client once a connection is //established. Socket client = null; //The following two objects handles our Serialization operations, ObjectOutputStream //writes an object to the stream. ObjectInputStream reads an object from the stream. ObjectOutputStream out = null; ObjectInputStream in = null; try { ServerSocket server = new ServerSocket(8888); client = server.accept(); out = new ObjectOutputStream(client.getOutputStream()); in = new ObjectInputStream(client.getInputStream()); Student student = (Student) in.readObject(); System.out.println(student); // close resources out.close(); in.close(); client.close(); server.close(); } catch (IOException e) { e.printStackTrace(); System.exit(-1); } catch (Exception e) { e.printStackTrace(); System.exit(-1); } } }
package com.dylan.serialization; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.net.Socket; import java.net.UnknownHostException; /** * @author xusucheng * @create 2018-01-08 **/ public class Client { public static void main(String[] args) { Socket client = null; ObjectOutputStream out = null; ObjectInputStream in = null; try { client = new Socket("127.0.0.1", 8888); out = new ObjectOutputStream(client.getOutputStream()); in = new ObjectInputStream(client.getInputStream()); Student student = new Student(991,"Dylan","Male"); out.writeObject(student); out.flush(); out.close(); in.close(); client.close(); } catch (UnknownHostException e) { e.printStackTrace(); System.exit(1); } catch (IOException e) { e.printStackTrace(); System.exit(1); } } }
说明:假如Student类没有实现序列化接口,在执行Client程序时会报错:
正如我们前面所讲,网络只能传输二进制数据,Java对象要想传输必须先序列化。
运行效果:
以上只是讲解了一下序列化的常用知识点,还有些深入的部分,以后有空再总结。
相关文章推荐
- Java 序列化Serializable详解(附详细例子)
- Java 序列化Serializable详解
- Java 序列化Serializable详解
- Java 序列化Serializable详解
- Java 序列化Serializable详解(附详细例子)
- Java 序列化Serializable详解
- Java 序列化Serializable详解(附详细例子)
- Java 序列化Serializable详解
- Java 序列化Serializable详解(附详细例子)
- java Serializable和Externalizable序列化反序列化详解
- Java 序列化Serializable详解(附详细例子)
- java Serializable和Externalizable序列化反序列化详解
- Java 序列化Serializable详解(附详细例子)
- Java 序列化Serializable详解
- java Serializable和Externalizable序列化反序列化详解--转
- Java 序列化Serializable详解(附详细例子)
- java 序列化 serializable 接口--详解!
- Java 序列化Serializable详解(附详细例子)
- java 序列化Serializable 详解
- Java 序列化Serializable详解(附详细例子)