IO框架(四)对象流、序列化和反序列化

IO框架(四)对象流、序列化和反序列化

对象流的内容和目的

  • 对象流:ObjectInputStream/ObjectOutputStream
  • 目的:
    • 增强了缓冲区功能
    • 增强了读写8种基本数据类型和字符串功能
    • 能够将内存中的对象写入硬盘,或读取结果为一个对象
    • 增强了读写对象的功能:
      • readObject() 从流中读取一个对象
      • writeObject(Object obj) 向流中写入一个对象
  • 使用流传输对象的过程称为序列化、反序列化

ObjectOutputStream(序列化)

ObjectOutputStream方法

Modifier and Type Method and Description
protected void annotateClass(类<?> cl) 子类可以实现此方法,以允许类数据存储在流中。
protected void annotateProxyClass(类<?> cl) 子类可以实现这种方法来存储流中的自定义数据以及动态代理类的描述符。
void close() 关闭流。
void defaultWriteObject() 将当前类的非静态和非瞬态字段写入此流。
protected void drain() 排除ObjectOutputStream中的缓冲数据。
protected boolean enableReplaceObject(boolean enable) 启用流来替换流中的对象。
void **flush() ** 刷新流。
ObjectOutputStream.PutField putFields() 检索用于缓冲要写入流的持久性字段的对象。
protected Object replaceObject(Object obj) 该方法将允许ObjectOutputStream的可信子类在序列化期间将一个对象替换为另一个对象。
void reset() 复位将忽略已写入流的任何对象的状态。
void useProtocolVersion(int version) 指定在编写流时使用的流协议版本。
void write(byte[] buf) 写入一个字节数组。
void write(byte[] buf, int off, int len) 写入一个子字节数组。
void write(int val) 写一个字节。
void writeBoolean(boolean val) 写一个布尔值。
void writeByte(int val) 写入一个8位字节。
void writeBytes(String str) 写一个字符串作为字节序列。
void writeChar(int val) 写一个16位的字符。
void writeChars(String str) 写一个字符串作为一系列的字符。
protected void writeClassDescriptor(ObjectStreamClass desc) 将指定的类描述符写入ObjectOutputStream。
void writeDouble(double val) 写一个64位的双倍。
void writeFields() 将缓冲的字段写入流。
void writeFloat(float val) 写一个32位浮点数。
void writeInt(int val) 写一个32位int。
void writeLong(long val) 写一个64位长
void **writeObject(Object obj) ** 将指定的对象写入ObjectOutputStream。
protected void writeObjectOverride(Object obj) 子类使用的方法来覆盖默认的writeObject方法。
void writeShort(int val) 写一个16位短。
protected void writeStreamHeader() 提供了writeStreamHeader方法,因此子类可以在流中附加或预先添加自己的头。
void writeUnshared(Object obj) 将“非共享”对象写入ObjectOutputStream。
void **writeUTF(String str) **此字符串的原始数据写入格式为 modified UTF-8

ObjectOutputStream构造方法

Modifier Constructor and Description
protected **ObjectOutputStream() **为完全重新实现ObjectOutputStream的子类提供一种方法,不必分配刚刚被ObjectOutputStream实现使用的私有数据。
**ObjectOutputStream(OutputStream out) **创建一个写入指定的OutputStream的ObjectOutputStream。

注意:

  • 对象要实现Serializable接口才能序列化
  • Serializable是一个标记接口,只用来标记该对象可以序列化

举例

类:

//序列化类必须要实现Serializable接口
public class Student implements Serializable {
    private String name;
    private int age;
    public Student() {
    }
    public Student(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;
    }
}

主函数(将对象序列化(写入硬盘)):

public class Demo07 {
    public static void main(String[] args) throws Exception{
        FileOutputStream fos=new FileOutputStream("D:\\stu.bin");//bin表示二进制文件
        ObjectOutputStream oos=new ObjectOutputStream(fos);
        //序列化
        Student zhangsan=new Student("张三",18);
        oos.writeObject(zhangsan);
        //关闭
        oos.close();
        System.out.println("序列化完毕");
    }
}
//输出:
序列化完毕

ObjectInputStream(反序列化)

ObjectInputStream方法

Modifier and Type Method and Description
int available() 返回可以读取而不阻塞的字节数。
void close() 关闭输入流。
void defaultReadObject() 从此流读取当前类的非静态和非瞬态字段。
protected boolean enableResolveObject(boolean enable) 启用流以允许从流中读取的对象被替换。
int read() 读取一个字节的数据。
int read(byte[] buf, int off, int len) 读入一个字节数组。
boolean readBoolean() 读取布尔值。
byte readByte() 读取一个8位字节。
char readChar() 读一个16位字符。
protected ObjectStreamClass readClassDescriptor() 从序列化流读取类描述符。
double readDouble() 读64位双倍。
ObjectInputStream.GetField readFields() 从流中读取持久性字段,并通过名称获取它们。
float readFloat() 读32位浮点数。
void readFully(byte[] buf) 读取字节,阻塞直到读取所有字节。
void readFully(byte[] buf, int off, int len) 读取字节,阻塞直到读取所有字节。
int readInt() 读取一个32位int。
String readLine() 已弃用 此方法无法将字节正确转换为字符。 有关详细信息和替代方案,请参阅DataInputStream。
long readLong() 读64位长。
Object readObject() 从ObjectInputStream读取一个对象。
protected Object readObjectOverride() 此方法由ObjectOutputStream的受信任子类调用,该子类使用受保护的无参构造函数构造ObjectOutputStream。
short readShort() 读取16位短。
protected void readStreamHeader() 提供了readStreamHeader方法来允许子类读取和验证自己的流标题。
Object readUnshared() 从ObjectInputStream读取一个“非共享”对象。
int readUnsignedByte() 读取一个无符号的8位字节。
int readUnsignedShort() 读取无符号16位短。
String readUTF()modified UTF-8格式读取字符串。
void registerValidation(ObjectInputValidation obj, int prio) 在返回图之前注册要验证的对象。
protected 类<?> resolveClass(ObjectStreamClass desc) 加载本地类等效的指定流类描述。
protected Object resolveObject(Object obj) 此方法将允许ObjectInputStream的受信任子类在反序列化期间将一个对象替换为另一个对象。
protected 类<?> resolveProxyClass(String[] interfaces) 返回一个代理类,它实现代理类描述符中命名的接口; 子类可以实现此方法从流中读取自定义数据以及动态代理类的描述符,从而允许它们为接口和代理类使用备用加载机制。
int skipBytes(int len) 跳过字节。

ObjectInputStream构造方法

Modifier Constructor and Description
protected **ObjectInputStream() ** 为完全重新实现ObjectInputStream的子类提供一种方法,不必分配刚刚被ObjectInputStream实现使用的私有数据。
**ObjectInputStream(InputStream in) ** 创建从指定的InputStream读取的ObjectInputStream。

举例

类:

public class Student implements Serializable {
    private String name;
    private int age;
    public Student() {
    }
    public Student(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;
    }
}

主函数(将对象反序列化):

public class Demo08 {
    public static void main(String[] args) throws Exception{
        FileInputStream fis=new FileInputStream("D:\\stu.bin");
        ObjectInputStream ois=new ObjectInputStream(fis);
        //反序列化(读取文件)
        //只能读一次,不能读多次
        Student s=(Student)ois.readObject();
        //关闭
        ois.close();
        //输出
        System.out.println("姓名:"+s.getName());
        System.out.println("年龄:"+s.getAge());
    }
}
//输出:
姓名:张三
年龄:18

序列化和反序列化注意事项

  • 序列化类必须要实现Serializable接口

  • 序列化类中对象属性要求实现Serializable接口

    例:

    要将Student类序列化,但其中的属性有Person类型,就要把Person类也实现Serializable接口

    public class Student implements Serializable {
        private Person s1;//对象属性
        private int a;
    }
    
  • 被序列化的类中可以写上serialVersionUID也就是序列化版本号ID,保证序列化的类和反序列化的类是同一个类

    如果序列化和反序列化的类序列化版本号ID不同的话会出错

    例:

    private static final long serialVersionUID=100L;
    
  • 使用transient修饰属性,这个属性不能序列化(只是这个属性不能序列化

    transient代表瞬时的

  • 静态属性(static)不能被序列化

  • 序列化多个对象时

    1. 则可以反序列化多个对象(举例1)
    2. 可以借助集合实现(举例2)

    例如:序列化s1和s2(第一次序列化s1第二次序列化s2),则通过反序列化两次可以取得所有值

    例1:

    序列化:

    public class Demo07 {
        public static void main(String[] args) throws Exception{
            FileOutputStream fos=new FileOutputStream("D:\\stu.bin");//bin表示二进制文件
            ObjectOutputStream oos=new ObjectOutputStream(fos);
            //序列化
            Student zhangsan=new Student("张三",18);
            Student lisi=new Student("李四",19);
            oos.writeObject(zhangsan);
            oos.writeObject(lisi);
            //关闭
            oos.close();
            System.out.println("序列化完毕");
        }
    }
    

    反序列化:

    public class Demo08 {
        public static void main(String[] args) throws Exception{
            FileInputStream fis=new FileInputStream("D:\\stu.bin");
            ObjectInputStream ois=new ObjectInputStream(fis);
            //反序列化(读取文件)
            //序列化了两次,所以可以反序列化两次
            Student s=(Student)ois.readObject();
            Student s1=(Student)ois.readObject();
            //关闭
            ois.close();
            //输出
            System.out.println("姓名:"+s.getName());
            System.out.println("年龄:"+s.getAge());
            System.out.println("姓名:"+s1.getName());
            System.out.println("年龄:"+s1.getAge());
        }
    }
    //输出:
    姓名:张三
    年龄:18
    姓名:李四
    年龄:19
    

    例2:

    序列化:

    public class Demo07 {
        public static void main(String[] args) throws Exception{
            FileOutputStream fos=new FileOutputStream("D:\\stu.bin");//bin表示二进制文件
            ObjectOutputStream oos=new ObjectOutputStream(fos);
            //序列化
            Student zhangsan=new Student("张三",18);
            Student lisi=new Student("李四",19);
            ArrayList<Student> a=new ArrayList<>();
            a.add(zhangsan);
            a.add(lisi);
            oos.writeObject(zhangsan);
            oos.writeObject(lisi);
            oos.writeObject(a);
            //关闭
            oos.close();
            System.out.println("序列化完毕");
        }
    }
    

    反序列化:

    public class Demo08 {
        public static void main(String[] args) throws Exception{
            FileInputStream fis=new FileInputStream("D:\\stu.bin");
            ObjectInputStream ois=new ObjectInputStream(fis);
            //反序列化(读取文件)
            //序列化了三次,所以可以反序列化三次
            Student s=(Student)ois.readObject();
            Student s1=(Student)ois.readObject();
            ArrayList<Student> s2=(ArrayList<Student>) ois.readObject();
            //关闭
            ois.close();
            //输出
            System.out.println("姓名:"+s.getName());
            System.out.println("年龄:"+s.getAge());
            System.out.println("姓名:"+s1.getName());
            System.out.println("年龄:"+s1.getAge());
            System.out.println(s2.toString());
        }
    }
    //输出:
    姓名:张三
    年龄:18
    姓名:李四
    年龄:19
    [[张三 18], [李四 19]]
    
posted @ 2022-02-10 19:09  史小鹏  阅读(60)  评论(0)    收藏  举报