java 之 向文件中输出对象类型数据的相关问题,可序列化,浅克隆与深克隆

如果想将一个对象存入文件中,就要让这个类实现标记接口: Serializable
并使用IO流中的ObjectInputStream/ObjectOutputStream过滤流进行输出

如果,将一个对象,向文件中重复输出,是放进了新的对象,还是同一个对象,用代码实践下:
Student类:

class Student implements Serializable{
    String name;
    int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

分别输出一次,两次:

public static void main(String[] args) throws Exception{
       Student s1=new Student("小明",30);
        ObjectOutputStream oos=new ObjectOutputStream(
                new FileOutputStream("d:\\copy\\Tos02.dat"));
        oos.writeObject(s1);
        oos.writeObject(s1);
    }

创建一个新对象,内容一样

public static void main(String[] args) throws Exception{
       Student s1=new Student("小明",30);
       Student s2=new Student("小明",30);
        ObjectOutputStream oos=new ObjectOutputStream(
                new FileOutputStream("d:\\copy\\Tos02.dat"));
        oos.writeObject(s1);
        oos.writeObject(s2);
    }

运行结果:
第一次Tos02.dat文件大小是110字节,第二次是115字节大小
第三次是125字节大小

可以很清楚的看出,第一次运行时,输出一个Student对象的文件大小是110字节,第二次比其大了5个字节,第三次比第一次大了15个字节,也就是说:一个Student对象大小是15字节
那么为什么第二次是5字节呢?原因就是:JVM在向文件输出时,检测到第二次输出时的对象是同一个对象,地址一样,直接在文件中写了一句话**:同上!**
当然事实上不是这么简略.
因为在序列化时,只看了地址一样,所以即使你在两次序列化间隔内,改变了对象的属性,实际上文件中存入的还是一句话:**同上!**所以输入打印时,两个打印的都是未改变前的属性

那么如何存入两个内容一样,地址不一样的对象呢?
1.在创建一个新对象即可,即new一个新对象
2.使用clone()方法

Student s2=new Student("小明",30);
//或者
Student s2=(Student) s1.clone();

使用clone()方法时,需要在自定义类中重写clone,并进行提权,即将其变为public修饰
并给自定义类实现接口Cloneable,这是一个标记接口,声明这个类可以被克隆

class Student implements Serializable,Cloneable{
//略
 @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
  }

但是,如果给Student中增加一个对象属性,clone()只会克隆地址,而不是对这个属性创建新的对象

	String name;
    int age;
    Adress adress;
class Adress{}
 		Adress adress=new Adress();
       Student s1=new Student("小明",30,adress);
       Student s2=(Student)s1.clone();
       System.out.println(s1.adress==s2.adress);

结果是true,clone方法只是浅克隆
所以,我们还需要另外的方法,来解决浅克隆
解决:
利用序列化解决,浅层克隆,序列化时,是将对象中的所有对象属性的内容存入,而不是存的地址
所以需要在Studnet类中重写的clone(),将s1输出文件后,在输入,赋给s2,这样经过序列化后,的对象属性就是一个新的对象,不是单纯的克隆地址

public Object clone() throws CloneNotSupportedException {
        Object o = null;
        try{
             ObjectOutputStream oos=new ObjectOutputStream(
                new FileOutputStream("d:\\copy\\Tos02.dat"));
        oos.writeObject(this);
        oos.close();

        ObjectInputStream ois=new ObjectInputStream(
                new FileInputStream("d:\\copy\\Tos02.dat"));
        o=ois.readObject();
        ois.close();
        }catch (Exception e){
            e.printStackTrace();
        }
        return o;
    }

当然,为了保证属性也可序列化,这个Address也要实现可序列化

class Adress implements Serializable{}

再运行

 System.out.println(s1.adress==s2.adress);

结果是false,这就是深克隆
当然,这样的深克隆,代码是十分麻烦的,有没有可替代品呢?


节点流
ByteArrayInputStream/ByteArrayOutputStream
向内存中的字节数组中输入输出
类似于File节点流,只不过一个是向文件中进行操作,一个向内存中进行操作

更改一下重写的clone()

class Child implements Serializable{
    String name;
    Toy toy;

    public Child() {
    }

    public Child(String name, Toy toy) {
        this.name = name;
        this.toy = toy;
    }

    @Override
    public String toString() {
        return "Child{" +
                "name='" + name + '\'' +
                ", toy=" + toy +
                '}';
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object o=null;
        try{
            ByteArrayOutputStream bos=new ByteArrayOutputStream();
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(this);//存入内存
            byte[] bytes=bos.toByteArray();//将当前对象装入数组
            oos.close();

            ByteArrayInputStream bis=new ByteArrayInputStream(bytes);//读字节数组
            ObjectInputStream ois=new ObjectInputStream(bis);
            o=ois.readObject();
            ois.close();

        }catch (Exception e){
            e.printStackTrace();
        }
        return o;
    }
}
class Toy implements Serializable{

}

在此克隆和属性的地址比较

		Child child=new Child("lcy",new Toy());
        Child child1=(Child) child.clone();
        System.out.println(child1.toy==child.toy);

结果是false

使用ByteArray流克隆较于向文件中克隆会更加迅速;

over

你可能感兴趣的:(java 之 向文件中输出对象类型数据的相关问题,可序列化,浅克隆与深克隆)