Serializable的序列化与反序列化
来源:互联网 发布:windows版本号查询 编辑:程序博客网 时间:2024/05/16 19:43
使用Serializable序列化,只要实现Serializable接口即可。一般情况下都会显示设置静态成员变量serialVersionUID为固定值。序列化时使用ObjectOutputStream写入,反序列化时使用ObjectInputStream读出。
如此简单,谁都会。但这是我碰到复杂点的情况,特作以下总结:
1、Serializable可继承:父类实现了序列化,子类也会自动实现序列化
PersonBean.java:
public class PersonBean implements Serializable{ private static final long serialVersionUID = 1L; private String name; private int age; public PersonBean(String name, int age) { this.name = name; this.age = age; } 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 name + "$$$" + age; }}
ProgrammerBean.java:
public class ProgrammerBean extends PersonBean { private String language; public ProgrammerBean(String name, int age, String language) { super(name, age); this.language = language; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } @Override public String toString() { return getName() + "@@@" + getAge() + "@@@" + language; }}
SerialTest.java:
package com.example;import com.example.serialBean.PersonBean;import com.example.serialBean.ProgrammerBean;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;/** * Created by Ralap on 2016/7/11. */public class SerialTest { private static final String filePath = System.getProperty("user.dir"); private static final String fileName = "serialTest.st"; public static void test() { // 1、Serializable可继承 testExtends(); // 2、transient和static修饰变量,使其不参加序列化 // 3、含有不可序列化的对象,自定义writeObject()和readObject() // 4、集合序列化 } private static void testExtends() { PersonBean personBean = new PersonBean("Boss", 38); ProgrammerBean programmerBean = new ProgrammerBean("hacker", 29, "Java"); printfn("==========Before Serialize :=========="); printfn(personBean.toString()); printfn(programmerBean.toString()); File file = createFile(); if (file == null) { printfn("create file fail"); return; } // 序列化 serialize(file, personBean, programmerBean); // 反序列化 Object[] objs = new Object[2]; unserialize(file, objs); PersonBean perBean = (PersonBean) objs[0]; ProgrammerBean proBean = (ProgrammerBean) objs[1]; // 打印 printfn("==========After Unserialize:=========="); printfn(perBean.toString()); printfn(proBean.toString()); } /** * 序列化 * * @param file 序列化目标文件 * @param objs 序列化对象 */ private static void serialize(File file, Object... objs) { ObjectOutputStream objectOS = null; try { FileOutputStream fos = new FileOutputStream(file); objectOS = new ObjectOutputStream(fos); for (Object obj : objs) { objectOS.writeObject(obj); } objectOS.flush(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (objectOS != null) { try { objectOS.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 反序列化 * @param file 反序列化文件 * @return 反序列化出来的对象数组 */ private static void unserialize(File file, Object[] objs) { ObjectInputStream objectIS = null; try { FileInputStream fis = new FileInputStream(file); objectIS = new ObjectInputStream(fis); Object obj; int offset = 0; while ((obj = objectIS.readObject()) != null) { objs[offset++] = obj; if (offset >= objs.length) { return; } } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if (objectIS != null) { try { objectIS.close(); } catch (IOException e) { e.printStackTrace(); } } } } private static void printf(String s) { System.out.printf("%s", s); } private static void printfn(String s) { System.out.printf("%s\n", s); } /** * 创建文件 * 存在:删除后新建。不存在:新建 */ private static File createFile() { File file = new File(filePath, fileName); try { if (file.exists()) { if (!file.delete()) { return null; } } if (!file.createNewFile()) { return null; } } catch (IOException e) { e.printStackTrace(); } return file; }}
结果:
2、static和transient修饰的变量,将不参加序列化
序列化只对类中的filed字段属性进行序列化,且序列化的只是类的实例对象的属性类型及值,所以method方法和static修饰的属性将不被序列化。如果要让非static属性也不序列化,使用transient。
扩展:另一个修饰变量符volatile,易变的意思。一般用于多线程中,因为每个线程都会有自己独立的内存空间,共享变量会从主内存拷贝一份到自己的内存中,操作的是自己内存中的数值,在进入线程或退出同步代码块时,才与共享中的成员变量进行比对、同步,这样可能导致其他线程获取到的不是最新值。所以,用volatile来修饰共享成员变量,在每次使用变量时都会强迫从共享内存中重新读取共享变量的值。
static:静态的,被所有对象共享,非某个对象私有,不会被持久化
transient:代表瞬间的意思,表示不会被持久化
把上面ProgrammerBean和SerialTest进行修改
ProgrammerBean.java
public class ProgrammerBean extends PersonBean { private String language; private static String belongIndustry = "Linux"; // static修饰 transient private boolean hasGF; // transient修饰。boolean默认值是false,注意比较 public ProgrammerBean(String name, int age, String language) { super(name, age); this.language = language; } public String getLanguage() { return language; } public void setLanguage(String language) { this.language = language; } public static String getBelongIndustry() { return belongIndustry; } public static void setBelongIndustry(String belongIndustry) { ProgrammerBean.belongIndustry = belongIndustry; } public boolean isHasGF() { return hasGF; } public void setHasGF(boolean hasGF) { this.hasGF = hasGF; } @Override public String toString() { return getName() + "^^^" + getAge() + "^^^" + language + "^^^" + belongIndustry + "^^^" + hasGF; }}
SerialTest.java修改部分
public static void test() { // 1、Serializable可继承// testExtends(); // 2、transient和static修饰变量,使其不参加序列化 testNotSerial(); // 3、含有不可序列化的对象,自定义writeObject()和readObject() // 4、集合序列化 } private static void testNotSerial() { PersonBean personBean = new PersonBean("Boss", 38); ProgrammerBean programmerBean = new ProgrammerBean("hacker", 29, "Java"); programmerBean.setHasGF(true); printfn("==========Before Serialize :=========="); printfn(personBean.toString()); printfn(programmerBean.toString()); File file = createFile(); if (file == null) { printfn("create file fail"); return; } // 序列化 serialize(file, personBean, programmerBean); // 序列化后修改static属性值 ProgrammerBean.setBelongIndustry("Android"); // 反序列化 Object[] objs = new Object[2]; unserialize(file, objs); PersonBean perBean = (PersonBean) objs[0]; ProgrammerBean proBean = (ProgrammerBean) objs[1]; // 打印 printfn("==========After Unserialize:=========="); printfn(perBean.toString()); printfn(proBean.toString()); }
结果:
3、使用自定义序列化使不可序列化的类序列化
一些类没有实现Serializable,就不能序列化。如ProgrammerBean中包含了一个不可序列化的类InterestBean。
ProgrammerBean.java
public class ProgrammerBean extends PersonBean { private String languages; InterestBean interest; public ProgrammerBean(String name, int age, String languages) { super(name, age); this.languages = languages; } public String getLanguages() { return languages; } public void setLanguages(String languages) { this.languages = languages; } public InterestBean getInterest() { return interest; } public void setInterest(InterestBean interest) { this.interest = interest; } @Override public String toString() { return getName() + "^^^" + getAge() + "^^^" + languages + ":::" + interest.getType() + "^^^" + interest.getNames(); }}
InterestBean.java
public class InterestBean{ private String type; private String names; public InterestBean(String type, String names) { this.type = type; this.names = names; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getNames() { return names; } public void setNames(String names) { this.names = names; }}
SerialTest.java部分修改(从别人那学习到更漂亮的序列化代码,因此作了修改)
package com.example;import com.example.serialBean.InterestBean;import com.example.serialBean.ProgrammerBean;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;/** * Created by Ralap on 2016/7/11. */public class SerialTest { private static final String filePath = System.getProperty("user.dir"); private static final String fileName = "serialTest.st"; public static void test() { // 1、Serializable可继承// testExtends(); // 2、transient和static修饰变量,使其不参加序列化// testNotSerial(); // 3、含有不可序列化的对象,自定义writeObject()和readObject() testCustomSerial(); // 4、集合序列化 } private static void testCustomSerial() { ProgrammerBean programmerBean = new ProgrammerBean("hacker", 29, "Java"); InterestBean interest = new InterestBean("gril", "Gril God"); programmerBean.setInterest(interest); printfn("==========Before Serialize :=========="); printfn(programmerBean.toString()); File file = createFile(); if (file == null) { printfn("create file fail"); return; } // 序列化 serialize(file, programmerBean); // 反序列化 ProgrammerBean proBean = unserialize(file); // 打印 printfn("==========After Unserialize:=========="); printfn(proBean.toString()); } /** * 序列化 * * @param file 序列化目标文件 * @param object 序列化对象 */ private static <T> void serialize(final File file, final T object) { ObjectOutputStream objectOS = null; try { FileOutputStream fos = new FileOutputStream(file); objectOS = new ObjectOutputStream(fos); objectOS.writeObject(object); objectOS.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if (objectOS != null) { try { objectOS.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 反序列化 * @param file 反序列化文件 * @return 反序列化出来的对象数组 */ private static <T> T unserialize(final File file) { ObjectInputStream objectIS = null; T retObj = null; try { FileInputStream fis = new FileInputStream(file); objectIS = new ObjectInputStream(fis); retObj = (T)objectIS.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } finally { if (objectIS != null) { try { objectIS.close(); } catch (IOException e) { e.printStackTrace(); } } } return retObj; }}
上面在写入和读出的时候都会报不可序列化异常:NotSerializableException
如果这是自己的类,实现一下Serializable就OK了。但万一这是别人封装好的,不能修改,那怎么办?这时就可以使用自定义的序列化方法。在类中
【1】给该属性添加修饰符transient,为不可序列化。
【2】加上下面三个方法(一般前面两个就可以)并实现之:
// Serializable接口中没有抽象方法,这些方法不是重写接口的方法,且他们都是private,但序列化时会自动调用这里的方法。这就是机制private void writeObject(ObjectOutputStream out) throws IOException {}private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{}private void readObjectNoData() throws ObjectStreamException{}
修改后的ProgrammerBean.java
public class ProgrammerBean extends PersonBean { private String languages; transient private InterestBean interest; public ProgrammerBean(String name, int age, String languages) { super(name, age); this.languages = languages; } public String getLanguages() { return languages; } public void setLanguages(String languages) { this.languages = languages; } public InterestBean getInterest() { return interest; } public void setInterest(InterestBean interest) { this.interest = interest; } @Override public String toString() { return getName() + "^^^" + getAge() + "^^^" + languages + ":::" + interest.getType() + "^^^" + interest.getNames(); } private void writeObject(ObjectOutputStream out) throws IOException { // 先使用默认写入,会自动把可序列化的序列化 out.defaultWriteObject(); out.writeUTF(interest.getType()); out.writeUTF(interest.getNames()); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException{ // 先使用默认读出,会自动把可序列化的读出 in.defaultReadObject(); // interest默认是null,这里必须新建。且读出与写入顺序必须一致 interest = new InterestBean(in.readUTF(), in.readUTF()); } private void readObjectNoData() throws ObjectStreamException { throw new InvalidObjectException("No Stream Data"); }}
这样结果就出来:
4、集合的序列化
ArrayList实现了Serializable接口,完全可以序列化。这个完全当做是验证测试
SerialTest.java部分修改
public class SerialTest { private static final String filePath = System.getProperty("user.dir"); private static final String fileName = "serialTest.st"; public static void test() { // 1、Serializable可继承// testExtends(); // 2、transient和static修饰变量,使其不参加序列化// testNotSerial(); // 3、含有不可序列化的对象,自定义writeObject()和readObject()// testCustomSerial(); // 4、集合序列化 testListSerial(); } private static void testListSerial() { List<ProgrammerBean> list = new ArrayList<>(); for (int i = 0; i < 5000; i++) { ProgrammerBean programmerBean = new ProgrammerBean("hacker" + i, 29, "Java"); programmerBean.setInterest(new InterestBean("gril", "Gril God" + i)); list.add(programmerBean); }// printfn("==========Before Serialize :==========");// printfn(programmerBean.toString()); File file = createFile(); if (file == null) { printfn("create file fail"); return; } // 序列化 serialize(file, list); // 反序列化 List<ProgrammerBean> programmerList = unserialize(file); // 打印 printfn("==========After Unserialize:=========="); for (ProgrammerBean pro : programmerList) { printfn(pro.toString()); } }
结果:
……
serialTest.st文件的大小:247 KB (252,987 字节)
我们目前使用的是XML(json因故暂时不考虑),XML也可以实现序列化与反序列化,具体有什么区别,还没研究,但先来简单计算下文件大小:
①Serializable中,UTF-8编码格式,都是英文字母或数字,每个字母或数字占一个字节,一个bean中的有意义的数据(属性值)大概是33个字节,33*5000 = 165,000字节。其他的占空间的都是包名+类名,属性类型。
②XML中,有意义的数据不变,也是165,000字节,其他的主要是tag占空间。保守假设每个tag名称为5字节,格式是<tag01>value</tag01>
,也就是说每个属性值还要是15字节,每个bean共有6个属性。这样一个bean就是7*15 = 105字节,tag总空间:105*5000 = 525,000字节。
So,从大小上来说,还是Serializable节省空间。代码还不用写xml这样复杂的序列化与反序列化,看到那么多Bean,简单重复的操作,真想写个框架(反射+注解)改掉它。老大说xml执行效率高,也许吧,下次验证下就知道了。
下篇进攻XML……
补充(2016/7/12 18:00):
上面第一种情况是父类序列化,子类会自动实现序列化。如果父类不序列化,子类需要序列化。如上,PersonBean不实现Serializable,而ProgrammerBean实现Serializable,其他保持原样不变。这样,子类对象在序列化时正常,但反序列化时会报以下异常:
java.io.InvalidClassException: com.example.serialBean.ProgrammerBean; no valid constructor at java.io.ObjectStreamClass$ExceptionInfo.newInvalidClassException(ObjectStreamClass.java:150) at java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:768) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1772) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
这表示,在反序列化时检测的时候抛的异常,提示该类没有有效的构造方法。
解决办法:在父类中添加无参构造方法。具体原因,不详,百度也没找到满意的答案(希望哪位大神帮忙解惑下)。这让我想起了以前一位老师说过,永远给出无参构造方法。
- Serializable的序列化与反序列化
- Serializable的序列化与反序列化
- 序列化和反序列化C# [Serializable] 与[Serializable()]
- Serializable 接口与 Java 序列化与反序列化
- Java序列化与反序列化(Serializable)
- java 序列化 Serializable 与 反序列化 总结
- Serializable序列化与反序列化编码实例
- JAVA序列化与反序列化基础 Serializable
- Java序列化(Serializable)与反序列化
- Java序列化(Serializable)与反序列化
- Java序列化(Serializable)与反序列化
- Java序列化(Serializable)与反序列化
- 8 Serializable序列化与反序列化
- Java序列化(Serializable)与反序列化
- java 序列化Serializable与反序列化DeSerializable
- Java序列化与反序列化(Serializable&&Externalizable)
- 对象序列化与反序列化(Serializable、Externalizable )
- Serializable对象序列化与反序列化
- 分治法-数组中出现次数超过一半的数字
- Android 四大组件之BroadcastReceiver 广播
- ios-Cell 的层级关系
- redis 配置开机启动
- VMware下虚拟机ubuntu的联网配置
- Serializable的序列化与反序列化
- Eclipse Neon Java EE IDE 安装配置指南
- python学习——map/reduce
- C#写的工厂抽象模式
- Linux 服务器安装配置
- HUD 1285 确定比赛名次(拓扑排序)
- 关于Android studio2.2preview4找不到依赖项目中类的问题
- 记录最近用到的一个简单的正则表达式
- C++primer(5th)第三章总结(string)