JAVA--数据字节流,对象流与序列化

数据字节流

DataOutputStream与DataInputStream

父类与缓冲流一样都是过滤字节流:FilterOutputStreamFilterInputStream,这两个类可以写基本数据类型.

DataOutputStream

这里只写了代码,方法的介绍可以看注释:

import java.io.DataOutputStream;
import java.io.FileOutputStream;

/**
 * 数据字节流--可以用来读写基本数据类型.
 * 本实例会按顺序写入所有基本数据类型,再按顺序读出做展示.
 * */
public class DataStreamTest {
    public static void main(String[] args) {
        try {
            DataOutputStream dos=new DataOutputStream(new FileOutputStream("data.txt",true));
            //写入字节型数据,写入量为1字节.
            byte by=65;
            dos.writeByte(by);
            //写入短整型数据,写入量为2字节.
            short s=20013;
            dos.writeShort(s);
            //写入整形数据,写入量为4字节.
            int i=99999;
            dos.writeInt(i);
            //写入长整型数据,写入量为8字节.
            long l=1234567890;
            dos.writeLong(l);
            //写入单精度浮点型数据,注意后面应该加上f,写入量为4字节.
            float f=3.14f;
            dos.writeFloat(f);
            //写入双精度浮点型数据,写入量为8字节.
            double d=3.1415926;
            dos.writeDouble(d);
            //写入字符型数据,写入量为2字节.
            char c='Z';
            dos.writeChar(c);
            //写入布尔型数据,写入量为1字节.
            boolean bo=false;
            dos.writeBoolean(bo);
            //写入字符串,使用UTF编码写入多少字节取决于传入了多少个字~
            String str="你好";
            dos.writeUTF(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

单精度数据后面不加f的话,会提示错误:

写出来的都是乱码,不过读出之后是正常的数据.

控制台没有消息,就是最好的状况了.

DataInputStream

也是只提供了代码,读出顺序与上面写入的顺序一样.

import java.io.DataInputStream;
import java.io.FileInputStream;

/**
 * 数据字节流--可以用来读写基本数据类型.
 * 本实例会按顺序写入所有基本数据类型,再按顺序读出做展示.
 * */
public class DataStreamTest {
    public static void main(String[] args) {
        try {
            DataInputStream dis=new DataInputStream(new FileInputStream("data.txt"));
            //读出字节型数据
            byte by=dis.readByte();
            //读出短整型数据
            short s=dis.readShort();
            //读出整形数据
            int i=dis.readInt();
            //读出长整型数据
            long l=dis.readLong();
            //读出单精度浮点型数据
            float f=dis.readFloat();
            //读出双精度浮点型数据
            double d=dis.readDouble();
            //读出字符型数据
            char c=dis.readChar();
            //读出布尔型数据
            boolean bo=dis.readBoolean();
            //读出字符串数据
            String str=dis.readUTF();
            //按顺序输出以上数据
            System.out.println("读出的字节型数据:"+by);
            System.out.println("读出的短整型数据:"+s);
            System.out.println("读出的整型数据:"+i);
            System.out.println("读出的长整型数据:"+l);
            System.out.println("读出的单精度浮点型数据:"+f);
            System.out.println("读出的双精度浮点型数据:"+d);
            System.out.println("读出的字符型数据:"+c);
            System.out.println("读出的布尔型数据:"+bo);
            System.out.println("读出的字符串数据:"+str);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果 

读出的字节型数据:65
读出的短整型数据:20013
读出的整型数据:99999
读出的长整型数据:1234567890
读出的单精度浮点型数据:3.14
读出的双精度浮点型数据:3.1415926
读出的字符型数据:Z
读出的布尔型数据:false
读出的字符串数据:你好

 

对象流

序列化:是对象转换成一个字节序列的过程,是一个写操作.

需要用到ObjectOutputStream

反序列化:字节序列转换为对象的过程,是一个读操作.

需要用到ObjectInputStream

ObjectOutputStream

构造器:

ObjectOutputStream(OutputStream out):创建一个指定字节输出流的对象输出流对象.

常用方法:

     除了提供一些基本数据类型的写方法之外,还提供了如下方法:

void writeObject(Object obj):将内存中的对象持久化(保存)到硬盘上.

ObjectInputStream

构造器

ObjectInputStream(InputStream out):创建一个指定字节输出流的对象输出流对象.

常用方法

 除了提供一些基本数据类型的读方法之外,还提供了如下方法:

void readObject():从硬盘上读取一个字节序列,转换为对象.

 

首先定义一个Person类,提供手动定义的序列号:

import java.io.Serializable;
/**
 * 实现序列化接口
 */
public class Person implements Serializable {
    //手动定义序列号
    private static final long serialVersionUID=1l;
    private String name;
    private int age;

    //根据JavaBean规范定义各种方法.
    //不过由于这里不需要equals与hashCode方法,所以没有定义.
    public Person(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 String toString(){
        return "["+name+","+age+"]";
    }
}

定义对象流读写对象: 

import java.io.ObjectOutputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class SerializableAndObjectStreamTest {
    public static void main(String[] args) {
        try {
            //写入对象,即序列化过程
            ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("object.txt",true));
            oos.writeObject(new Person("A",21));
            oos.writeObject(new Person("B",19));
            oos.writeObject(new Person("C",23));
            oos.writeObject(new Person("D",20));
            //读出对象,即反序列化过程.
            ObjectInputStream ois=new ObjectInputStream(new FileInputStream("object.txt"));
            Object o1=ois.readObject();
            System.out.println("读出第一个对象:"+o1);
            Object o2=ois.readObject();
            System.out.println("读出第二个对象:"+o2);
            Object o3=ois.readObject();
            System.out.println("读出第三个对象:"+o3);
            Object o4=ois.readObject();
            System.out.println("读出第四个对象:"+o4);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:

读出第一个对象:[A,21]
读出第二个对象:[B,19]
读出第三个对象:[C,23]
读出第四个对象:[D,20]

之后更改Person类的序列号: 

private static final long serialVersionUID=2l;

再次尝试读出数据:,会出现如下结果:

这里就引出了序列化的相关问题: 

序列化

interface Serializable

如果想将对象序列化,那么该对象必须实现此接口.此接口内什么都没有,只是一个序列化标识.

serialVersionUID

每个能序列化的对象,在序列化时,系统会默认给此对象的类计算一个序列化版本号.不同的平台默认提供的序列化版本号多数情况下不会相同.因此当我们反序列化时,如果硬盘里存储的对象的版本号与当前设计的类型的版本号不一致会出现运行时异常

InvalidClassException不兼容问题

如果想解决此问题,我们应该手动提供版本号(serialVersionUID),尽可能的相同,这样来解决不兼容问题.

另一种情况:序列化过后,可能会修改类型,如果使用系统默认的版本号,在反序列化时会出现异常.如果手动提供不出现异常,多出来的成员变量以类型的默认值的形式赋值给反序列化后的对象.

猜你喜欢

转载自blog.csdn.net/bujiujie8/article/details/82349553