java对象的序列化
来源:互联网 发布:java毕业设计题目大全 编辑:程序博客网 时间:2024/05/16 15:27
序列化概念
对象序列化:将Java实体对象转化成二进制字节流的过程。
对象反序列化:将二进制字节流恢复为Java实体对象的过程。
主要用途
1. 持久化对象到磁盘
2. 网络传输
3. 对象的拷贝
序列化的实现
- 通过实现Serializable接口,java有默认的实现算法,我们也可以自定义序列化算法(用于实现一些特殊的需求),在反序列化时,不会调用该序列化的任何构造方法。
- 通过实现Externalizable接口,需要我们自己写序列化规则。
实现Serializable接口
package com.zd.test.java.ds;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.io.Serializable;public class SerializableTest { //首先我们用这个类来检验,探究一下序列化的算法。 static class Person implements Serializable{ /** * 版本号 */ private static final long serialVersionUID = -1285437413613384110L; String name; Integer age; transient String address; Dog dog; public Person(String name, Integer age,String address,Dog dog) { super(); this.name = name; this.age = age; this.address = address; this.dog = dog; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + ", address=" + address + ", dog=" + dog + "]"; } } static class Dog implements Serializable{ private static final long serialVersionUID = 5581802748286331206L; String name; public Dog(String name) { super(); this.name = name; } } //测试自定义的序列化方案 static class Man implements Serializable{ /** * 版本号 */ private static final long serialVersionUID = -1285437413613384110L; String name; Integer age; transient String address; Dog dog; transient String password; public Man(String name, Integer age,String address,String password,Dog dog) { super(); this.name = name; this.age = age; this.address = address; this.dog = dog; this.password = password; } @Override public String toString() { return "Man [name=" + name + ", age=" + age + ", address=" + address + ", dog=" + dog+", password=" + password + "]"; } //重写持久化规则 private void writeObject(ObjectOutputStream out) throws IOException{ out.defaultWriteObject(); //执行默认的写对象方法,会把这当前中非静态成员变量和非transient成员变量写到流中 //在这里,我们把address这个属性先转换成大写,再持久化 if (address != null) { out.writeUTF(address.toUpperCase()); } if (password != null) { out.writeUTF(new StringBuffer(password).reverse().toString()); } //注意这里无需关闭流,否则会失败,这只是一个普通的回调方法,java底层调用完此方法之后还会处理其他的逻辑。 //out.close(); } private void readObject(ObjectInputStream in) throws ClassNotFoundException, IOException{ in.defaultReadObject(); Object obj = in.readUTF(); if (obj != null) { address = obj.toString(); } obj = in.readUTF(); if (obj != null) { password = new StringBuffer(obj.toString()).reverse().toString(); } } } public static void main(String[] args) throws ClassNotFoundException, IOException { //1.测试一般用法,顺便测试一下transient关键字 Person person = new Person("张三", 23, "北京市",new Dog("旺旺")); //如果person里面的Dog类没有实现Serializable接口,会报异常:java.io.NotSerializableException: com.zd.test.java.ds.SerializableTest$Dog Person p = deepCopy(person); System.out.println(p); //结果:Person [name=张三, age=23, address=null] //结论:被transient关键字修饰的对象默认是不能够持久化的,但是我们可以重写持久化规则。 //测试重写测试化规则,持久化被transient修饰的成员变量,加密重要隐私信息 //需要按自定义方式序列化的成员变量,是否定义为transient类型无关紧要。 Man man = new Man("张三", 23, "北京市", "admin",new Dog("旺旺")); Man m = deepCopy(man); System.out.println(m); } public static <T> T deepCopy(T t) throws IOException, ClassNotFoundException{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(t); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); @SuppressWarnings("unchecked") T clone = (T) objectInputStream.readObject(); return clone; }}
实现Externalizable接口
package com.zd.test.java.ds;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.Externalizable;import java.io.IOException;import java.io.ObjectInput;import java.io.ObjectInputStream;import java.io.ObjectOutput;import java.io.ObjectOutputStream;public class ExternalizableTest { static class Person implements Externalizable{ String name; transient String password; Integer age; @Override public void writeExternal(ObjectOutput out) throws IOException { if (name != null) { out.writeUTF(name); } if (password != null) { out.writeUTF(new StringBuffer(password).reverse().toString()); } if (age != null) { out.writeInt(age); } } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { Object obj = null; if ((obj = in.readUTF()) != null) { name = obj.toString(); } if ((obj = in.readUTF()) != null) { password = new StringBuffer(obj.toString()).reverse().toString(); } if ((obj = in.readInt()) != null) { age = (Integer) obj; } } public Person(String name, String password, Integer age) { super(); this.name = name; this.password = password; this.age = age; } @Override public String toString() { return "Person [name=" + name + ", password=" + password + ", age=" + age + "]"; } //java.io.InvalidClassException: com.zd.test.java.ds.ExternalizableTest$Person; no valid constructor public Person() { //给一个无参的构造器 } } public static void main(String[] args) throws Exception{ Person person = new Person("张三", "admin", 23); Person person2 = deepCopy(person); System.out.println(person2); } public static <T> T deepCopy(T t) throws IOException, ClassNotFoundException{ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(t); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream); @SuppressWarnings("unchecked") T clone = (T) objectInputStream.readObject(); return clone; }}
补充:
默认序列化方式的不足
- 不宜对对象中对安全要求比较高的敏感数据进行序列化,安全性极低。
- 不会检查对象的成员变量是否合乎正确的约束条件。
- 默认的序列化方式需要对对象图(继承等关系)进行递归遍历,如果对象图很复杂,会消耗很多空间和时间,甚至引起Java虚拟机堆栈溢出。
总结
一般我们需要掌握的自定义序列化方式:
1. 实现Serializable接口,并且提供private的writeObject()和readObject()。
2. 实现Externalizable接口,实现writeExternal()和readExternal()方法,必须提供public无参的构造方法,否则会报异常,上面案例有涉及到。
3. 关于默认的序列化方案和自定义序列化方案,根据需求而定。
另外关于serialVersionUID,主要是为了兼容序列化类的变化,便于项目后期对序列化类的扩充和修改。
当然,还有很多的序列化方案,比如我们在java业务中前台和后台使用的json形式就是序列化的一种,它有着可以跨语言的优势,做过JavaEE的都知道怎么使用。
更多的序列化方案可以可以下https://github.com/eishay/jvm-serializers/wiki里面的,里面有详细比较。
0 0
- JAVA的对象序列化
- JAVA的对象序列化
- java的对象序列化
- Java对象的序列化
- Java 的对象序列化
- Java对象的序列化
- Java对象的序列化
- Java对象的序列化
- Java:对象的序列化
- Java对象的序列化
- Java对象的序列化
- Java对象的序列化
- Java对象的序列化
- java对象的序列化
- JAVA对象的序列化
- Java--对象的序列化
- Java的对象序列化
- Java 对象的序列化
- msql quote()函数
- 冒泡排序
- 89GrayCode
- 强制 code review:reviewboard+svn 的方案
- JQuery学习笔记之自定义动画效果
- java对象的序列化
- 学习注水-20160901-ccna-ProblemRemain
- js写当前系统的时间代码案例分享
- OpenCV split函数 merge函数 及示例
- 解决grid拖拽功能后,复选框多选后取消不掉的问题
- 如何使用上aliyun的maven镜像
- jquery mobile 链接
- java删除文件夹及其子文件
- lightoj 1079 - Just another Robbery(01背包)