java对象流(二)
对象流,可以将java中的对象转为字节进行输出。将对象写入文件时。文件输出流是将字节写入到文件中。
对象流是将给定的对象转化为一组字节。writeObject()方法就是将对象转为字节。
对象流,读的文件肯定是对象进行转换以后的字节。只有实现了Serializable可序列号接口,我们才可以把一个对象进行序列化。
对象流的作用就是:将对象进行序列化和反序列化。将对象转化为字节,然后将字节转化为对象。
将对象转化为字节的时候,会记录变量的属性。转化后文件的大小比对象成员变量大小相加更大。
Serializable接口里是空的。Java自带的空接口也称为签名接口。还有一个接口Cloneable,也是类似的。
这个接口是给java编译器看的。Java编译器在编译的时候,发现如果一个类实现了Serializable,会隐含地给它加一个方法,不会在源代码中体现。
当一个类希望被对象流进行读写,那么该类必须实现接口:
* java.io.Serializable
可以方便在网络中相互传输java对象。
序列化版本号是直接影响一个序列化对象能否反序列化成功。一个类实现了序列化接口,就一定会有序列化版本号。
如果没有直接创建版本号,java编译器会自动生成一个版本号。只要这个类的结构不变,版本号是不会变的。
但是只要类发生了改变,版本号都会发生改变。在反序列化的时候,对象输入流会看反序列化对象的版本号和现在这个类的版本号是否一样,只要不一样,反序列就失败。
如果让系统自动生成版本号,那么类是一点都不能改的。应该明确指定版本号,那么版本号就是可控的。只要类改一下,版本号会变。
自己控制版本号,就算类改了,只要版本号没有变,还是可以反序列化的。版本号变了,反序列化会失败。
对象流在将来读写对象中可以使用。
Person序列化如下所示:
/** * 使用该类测试对象流的读写操作 * */ import java.io.Serializable; import java.util.Arrays; public class Person implements Serializable{ /** * 当一个类实现了Serialiazable接口后,编译器会提示 * 我们应当添加一个常量:serialVersionUID * 序列化版本号影响着反序列化的结果,当对象输入流在反序列化 * 一个实例时会检查该实例与其所属的类的版本是否一致, * 不一致则反序列化会抛出版本号不一致的异常。若一致则成功进行反序列化 * * 自行维护版本号可以主动确定反序列化结果。但是若不指定序列化版本号, * 编译器在编译当前类时会根据当前类的结构生成一个版本号,但是只要当前类发生改变, * 则版本号一定会改变。 * * * * 当一个类希望被对象流进行读写,那么该类必须实现接口: * java.io.Serializable * 该接口没有任何抽象方法,这种接口称为:签名接口。 * 虽然在源代码中不需要重写方法,但实际上编译器在编译该类为class文件时 * 会根据当前类结构添加一个方法,用于将当前类实例转换为一组字节。 * */ private static final long serialVersionUID = 1L; private String name; private int age; private String gender; /** * transient关键字 * 当一个属性被该关键字修饰后,那么该类的某个实例被序列化时,这个值会被忽略。那么在对象反序列化 */ private transient String[] otherInfo; //给一个变量添加transient修饰时,进行序列化时不被保留,反序列化时也不被转化。 //序列化了,这个修饰才会有作用。 public Person(String name, int age, String gender, String[] otherInfo) { super(); this.name = name; this.age = age; this.gender = gender; this.otherInfo = otherInfo; } 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; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String[] getOtherInfo() { return otherInfo; } public void setOtherInfo(String[] otherInfo) { this.otherInfo = otherInfo; } @Override public String toString() { // TODO Auto-generated method stub return name + ", " + age + ", " + gender + ","+ Arrays.toString(otherInfo); } }
写入对象的过程如下所示:
import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; /** * 对象流 * java.io.ObjectOutputStream * java.io.ObjectInputStream * * 对象流是一对高级流,使用它们可以很方便进行java中任何对象的读写操作 * @author 小明 * */ public class OOSDemo { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub String name = "苍老师"; int age = 18; String gender = "女"; String []otherInfo = {"是一名演员", "爱好是写大字", "长得显小", "促进交流", "演员"}; Person person = new Person(name, age, gender, otherInfo); //System.out.println(person); /** * 将person对象写入文件“person.obj" */ FileOutputStream fos = new FileOutputStream("person.obj"); /** * 为了方便写出对象,连接对象输出流 */ ObjectOutputStream obj = new ObjectOutputStream(fos); /** * 对象输出流提供的方法: * void writeObject(Object obj) * 该方法用于将给定的对象转换为一组字节后写出。 * 需要注意,写出的对象必须实现接口:Serializable * 否则会抛出异常。 * *下面的操作经历了两个过程 *1:对象输出流将对象按照结构转化为了一组字节 *这个过程称为:对象序列化 *2:文件输出流将这组字节写入到文件中(硬盘上)做长久保存的过程称为:数据持久化。 * * * */ obj.writeObject(person); System.out.println("写出完毕"); obj.close(); } }
读出对象的过程如下所示:
import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; public class OISDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { // TODO Auto-generated method stub FileInputStream fis = new FileInputStream("person.obj"); ObjectInputStream ois = new ObjectInputStream(fis); /** * 对象流读取字节必须是对象输出流将一个对象 * 转换的一组字节,否则读取过程会抛出类没有找到的 * 异常:ClassNotFoundException * * 将一组字节还原为对象的过程称为:对象反序列化。 */ Person person = (Person)ois.readObject(); System.out.println(person); ois.close(); } }
输出结果如下所示: