Io流---对象流与随机流
对象流:
ObjectInputStream和OjbectOutputSteam
对象流的作用
用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可 以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
序列化和反序列化
序列化:用ObjectOutputStream类保存基本类型数据或对象的机制
反序列化:用ObjectInputStream类读取基本类型数据或对象的机制
对象流的注意点:
ObjectOutputStream和ObjectInputStream不能序列化static和transient修 饰的成员变量
对象的序列化
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从 而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传
输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原 来的Java对象 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据, 使其在保存和传输时可被还原 序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返 回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可 序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。 否则,会抛出NotSerializableException异常 Serializable Externalizable
对象序列化的版本标识符
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量: private static final long serialVersionUID; serialVersionUID用来表明类的不同版本间的兼容性。简言之,其目的是以序列化对象 进行版本控制,有关各版本反序列化时是否兼容。 如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自 动生成的。若类的实例变量做了修改,
serialVersionUID 可能发生变化。故建议, 显式声明。
简单来说,Java的序列化机制是通过在运行时判断类的serialVersionUID来验 证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的
serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同 就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异 常。(InvalidCastException)
简单的说为何实现了Serializable接口的对象要声明一个版本标识符
序列化的SerialVersionId如果不自定义的一个的话,在创建的时候也会自动生成一个,如果我们定义了一个类
没有自定义SerialVersionId然后序列化之后会自动生成一个id值,但是在我们反序列化之后也会自动生成一个值,这两个标识的Id值
不同,可能会导致对象的值不同,有可能反序列化后的类的对象和之前序列化的对象值不同,所以还是我们自定义一个确定的值好点
Static 和transient 修饰的变量是不可以被序列化的,修饰的变量序列化之后是不显示的 所以不能被序列化
使用对象流序列化对象
若某个类实现了 Serializable 接口,该类的对象就是可序列化的:
创建一个 ObjectOutputStream
调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象
注意写出一次,操作flush()一次
反序列化
创建一个 ObjectInputStream
调用 readObject() 方法读取流中的对象
强调:如果某个类的属性不是基本数据类型或 String 类型,而是另一个 引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化
序列化写入
序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
使用ObjectOutputStream实现
@Test public void TestOne(){ ObjectOutputStream ops = null; try { // 造出对象流,操作字节流 ops = new ObjectOutputStream(new FileOutputStream("object.dba")); // 造流 // 操作数据 // 写入两个String对象 ops.writeObject(new String("老王")); ops.flush(); // 每一次的写出要操作一次flush操作刷新内存 ops.writeObject(new String("在你家")); ops.flush(); } catch (IOException e) { e.printStackTrace(); } finally { // 关闭流 if(ops != null){ try { ops.close(); } catch (IOException e) { e.printStackTrace(); } } } }
反序列化读出
/* 反序列化将磁盘文件中的对象还原为内存中的一个java对象 使用ObjectInputStream来实现 */ @Test public void TestTwo(){ ObjectInputStream ois = null; try { ois = new ObjectInputStream(new FileInputStream("object.dba")); Object obj = ois.readObject(); // 因为是使用一个对象流操作对象数据 所以用readObject String str = (String) obj; Object obj1 = ois.readObject(); // 因为文件写入了两个String对象那么我们想取几个就要写几个对象来接收 String str1 = (String) obj1; System.out.println(str); System.out.println(str1); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if(ois != null){ try { ois.close(); } catch (IOException e) { e.printStackTrace(); } } } }
反序列化读取的时候要一一对象序列化进去的值,当序列化写入3个String那么反序列化读取的时候想要读取第3个也要把前两个先读取了
自定义序列化类:
* 1.需要实现接口:Serializable * 2.当前类提供一个全局常量:serialVersionUID * 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性 * 也必须是可序列化的。(默认情况下,基本数据类型可序列化)
SerialVersionUId 是一个序列化类的唯一标识符,在自定义序列化类的时候必须要自定义一个SerialVersionUID标识符,为了确保反序列化后的对象和序列化写进去的是同一个
序列化Person类

package June.JuneSixteen.JuneSixteenAfternoon; import java.io.Serializable; /** * * Person需要满足如下的要求,方可序列化 * * 1.需要实现接口:Serializable * * 2.当前类提供一个全局常量:serialVersionUID * * 3.除了当前Person类需要实现Serializable接口之外,还必须保证其内部所有属性 * * 也必须是可序列化的。(默认情况下,基本数据类型可序列化) * * * * * * 补充:ObjectOutputStream和ObjectInputStream不能序列化static和transient修饰的成员变量 */ public class JuneSixteenAfternoonPerson implements Serializable { // public static final long serialVersionUID = 4742422L; // 建立唯一的序列标识符 public static final long serialVersionUID = 475463534532L; private String name; private int age; private int id; @Override public String toString() { return "JuneSixteenAfternoonPerson{" + "name='" + name + '\'' + ", age=" + age + ", id=" + id + '}'; } public String getName() { return name; } public JuneSixteenAfternoonPerson(String name, int age, int id) { this.name = name; this.age = age; this.id = id; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
将序列化Person类序列化存储

@Test public void TestFour(){ ObjectOutputStream os = null; try { os = new ObjectOutputStream(new FileOutputStream("object.cc")); // 指定存储文件 os.writeObject(new JuneSixteenAfternoonPerson("老李头",17,001)); //创建序列化对象并存储 os.flush(); // 每一次存储都要刷新 os.writeObject(new String("老王不在")); os.flush(); } catch (IOException e) { e.printStackTrace(); } finally { if(os != null){ try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } }
反序列化读取

@Test public void TestFive(){ ObjectInputStream oi = null; try { oi = new ObjectInputStream(new FileInputStream("object.cc")); JuneSixteenAfternoonPerson person = (JuneSixteenAfternoonPerson) oi.readObject(); //指定读取的内容 Object object = oi.readObject(); System.out.println(object); System.out.println(person); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } finally { if(oi != null){ try { oi.close(); } catch (IOException e) { e.printStackTrace(); } } } }
切记
序列化类要实现接口Serialiable并定义 public static final long serialVersionUID 的值 如果内部有其它的类 也要实现序列化接口 Serialiable
.