一:对象的序列化是指把对象写入到各种介质中的过程。这样可以不用拷贝整个类文件回家,只需要拷贝这个序列化之后的文件就可以。可以序列化的实体类必须实现implements Serializable接口,这样才不会抛出java.io.NotSerializableException不可序列化异常。特别注意的几点:
◆transient修饰的属性,在文件中没写进去,控制台输出类型的默认值
◆static修饰的属性,在文件中没写进去,控制台仍可以输出
◆和实体类相关联的类也必须implements Serializable接口
二:ObjectOutputStream/ObjectInputStream对象输入输出流
他和前面一节我们介绍的包装流的用法一致,他可以用来读操作对象至文件中。
三:具体实现
实体Address类:此类为和Person类相关联的类,因此必须实现序列化接口
public class Address implements Serializable{ private String city; private String street; //其他方法省略,请自行添加 } |
实体Person类:要实现对象序列化,所以也应该实现序列化接口
public class Person implements Serializable{ /*static*/ private String name;//static修饰的文件中没写进去,控制台可以输出 /*transient*/ private int age;//transient修饰的文件中没写进去,控制台输出类型的默认值 private Address address; //其他方法省略,请自行添加 } |
1 单个对象的可序列化的测试类:
public class TestOOSOIS { public static void main(String[] args) throws Exception { //单个对象的可序列化 File file = new File("d:\\a.txt");//创建文件 FileOutputStream os = new FileOutputStream(file);//创建基础输出流 ObjectOutputStream oos = new ObjectOutputStream(os);//创建包装基础输出流的流 Person obj = new Person("张三",22);//定义单个对象 Address address = new Address("哈尔滨","和兴路"); obj.setAddress(address); oos.writeObject(obj);//把单个对象写进文件里 FileInputStream fis = new FileInputStream(file);//创建基础输入流 ObjectInputStream ois = new ObjectInputStream(fis);//创建包装基础输入流的流 Person p = (Person) ois.readObject();//读出一个Person对象 System.out.println("姓名:"+p.getName()); System.out.println("年龄:"+p.getAge()); } } |
结果:
姓名:张三 年龄:22 |
2 用异常实现多个对象的序列化。多个对象的序列化会抛出“EOFException”这种异常, 那么在catch中可以给出提示信息,以便处理掉,但是有点影响代码的阅读。
测试类如下:
//用异常实现多个对象的序列化 public class TestSer { public static void main(String[] args) throws Exception { Person p1 = new Person("张三",22);//定义多个对象 Person p2 = new Person("李四",32); File file = new File("d:\\a.txt"); FileOutputStream os = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(os); oos.writeObject(p1);//写入多个对象 oos.writeObject(p2); FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis); while(true){//死循环 Person p; try { p = (Person) ois.readObject(); System.out.println(p); } catch (IOException e) { System.out.println("读到了文件的末尾。。。"); break;//用异常作为循环判定条件 } } oos.close(); ois.close(); } } |
结果2:
Person [name=张三, age=22, addressnull] Person [name=李四, age=32, addressnull] 读到了文件的末尾。。。 |
3运用集合实现多个对象的序列化。他所采用的方法是:把多个对象存入一个ArrayList中,让后把这个集合写入到文件里的过程,然后读取是以集合的形式去读。
具体的测试类:
//运用集合实现多个对象的序列化,推荐使用 public class TestSerCol { public static void main(String[] args) throws Exception { Person p1 = new Person("张三",22);//定义多个对象 Person p2 = new Person("李四",32); File file = new File("d:\\a.txt"); FileOutputStream os = new FileOutputStream(file); ObjectOutputStream oos = new ObjectOutputStream(os); ArrayList<Person> list = new ArrayList<Person>();//定义集合存放Person类 list.add(p1); list.add(p2); oos.writeObject(list);//写list到文件中 FileInputStream fis = new FileInputStream(file); ObjectInputStream ois = new ObjectInputStream(fis); ArrayList<Person> perlist = (ArrayList<Person>) ois.readObject(); for(Person p:perlist){//根据list输出 System.out.println(p); } } } |
结果:
Person [name=张三, age=22, addressnull] Person [name=李四, age=32, addressnull] |
4:一个对象的多次序列化
同一个流对同一个对象的多次序列化,不关心属性的变化,存入的仍是这个对象的引用。比如有一个对象姓名张三,年龄23,当把张三变为李四时,读出的仍然是“张三23”,所以说两者的地址相同。
public class TestManyOBJ { public static void main(String[] args) throws Exception { Person p1 = new Person("张三",22);//定义一个对象 Address add = new Address(); add.setCity("哈尔滨"); add.setStreet("和兴路"); p1.setAddress(add); File file = new File("d:\\a.txt"); FileOutputStream os = new FileOutputStream(file);//基本输入流 ObjectOutputStream oos = new ObjectOutputStream(os);//包装输入流 oos.writeObject(p1);//写入一个对象 p1.setName("李四"); oos.writeObject(p1);//再次写入这个对象 FileInputStream fis = new FileInputStream(file);//基本输出流 ObjectInputStream ois = new ObjectInputStream(fis);//基本包装流 Person p = (Person) ois.readObject();//读第一个 System.out.println(p); Person p2 = (Person) ois.readObject();//读第二个 System.out.println(p2); System.out.println(p2==p);//由于再次写入的是p1对象的引用,所以二者地址相同 oos.close(); ois.close(); } } |
结果:
Person [name=张三, age=22] Person [name=张三, age=22] true |
如何解决上述的问题呢?有两种办法
(1)重新定义一个流:这样的话,在另一个新流中相当于全新的,当然可以写的进去。
public class TestManyOBJ { public static void main(String[] args) throws Exception { Person p1 = new Person("张三",22);//定义一个对象 Address add = new Address(); add.setCity("哈尔滨"); add.setStreet("和兴路"); p1.setAddress(add); File file = new File("d:\\a.txt");//基本文件 FileOutputStream os = new FileOutputStream(file);//基本输入流 ObjectOutputStream oos = new ObjectOutputStream(os);//包装输入流 oos.writeObject(p1);//写入一个对象 p1.setName("李四"); //重新定义一个流,实现对一个对象的多次序列化 FileOutputStream os1 = new FileOutputStream(file);//新的基本输入流 ObjectOutputStream oos1 = new ObjectOutputStream(os1);//新的包装输入流 oos1.writeObject(p1);//再次写入这个对象 FileInputStream fis = new FileInputStream(file);//基本输出流 ObjectInputStream ois = new ObjectInputStream(fis);//基本包装流 Person p = (Person) ois.readObject();//读的是新写进去的这个 System.out.println(p); oos.close(); ois.close(); } } |
结果:
Person [name=李四, age=22] |
(2)对象的Clone化:克隆出的对象和源对象不是同一个,所以也仍然可以写的进去。这时应该注意:实体类应该实现Cloneable接口,并且重写clone()方法。
具体的测试如下:
public class TestManyOBJ { public static void main(String[] args) throws Exception { Person p1 = new Person("张三",22);//定义一个对象 Address add = new Address(); add.setCity("哈尔滨"); add.setStreet("和兴路"); p1.setAddress(add); File file = new File("d:\\a.txt");//基本文件 FileOutputStream os = new FileOutputStream(file);//基本输入流 ObjectOutputStream oos = new ObjectOutputStream(os);//包装输入流 oos.writeObject(p1);//写入一个对象 p1.setName("李四"); //运用克隆,实现对一个对象的多次序列化 oos.writeObject(p1.clone());//运用再次写入这个对象 FileInputStream fis = new FileInputStream(file);//基本输出流 ObjectInputStream ois = new ObjectInputStream(fis);//基本包装流 Person p = (Person) ois.readObject();//读的是原来进去的这个 System.out.println(p); Person p2 = (Person) ois.readObject();//读的是新写进去的这个 System.out.println(p2); oos.close(); ois.close(); } } |
结果:
Person [name=张三, age=22] Person [name=李四, age=22] |
5:细心的你可能会发现,在实体类中会出现一个×××三角的小图标,点进去会让你添加一个序列号,这个可以作为版本信息号,方便于以后的交流。(了解一下就可以)