java创建对象方式之clone以及序列化(总)

之前两篇,我们讲解了创建对象方式中的反射和工厂模式的方式, 本文我们来学习一下其他方式。
在这里插入图片描述

上图中的和Class类和Constructor中的newInstance方法其实就是之前我们讲到的反射。

Constructor<Employee> constructor = Employee.class.getConstructor();
Employee emp3 = constructor.newInstance();

Class类中的newInstance方法

事实上Class的newInstance方法内部调用Constructor的newInstance方法。

Employee emp2 = (Employee) Class.forName("org.programming.mitra.exercises.Employee").newInstance();
或者
Employee emp2 = Employee.class.newInstance();

clone方法:

无论何时我们调用一个对象的clone方法,jvm就会创建一个新的对象,将前面对象的内容全部拷贝进去。

用clone方法创建对象并不会调用任何构造函数。

要使用clone方法,我们需要先实现Cloneable接口并实现其定义的clone方法。
也就是说我们在需要使用clone的类中,实现Cloneable接口,重写clone方法(就是实现super.clone();),那么在客户端中就可以直接使用一个对象调用clone方法实例化出一个新的对象。

例如,Student类实现了Cloneable接口,并重写了clone方法(注意:Student类中只包含基本数据类型的成员变量)。那么在客户端类中调用,就可以先new一个新的stu对象,然后
Student stu2=(Student)stu.clone();//这样就可以实现新对象的实例化操作
此为浅克隆

但是上述情况 只适用于没有其他类的引用的类中,假如Student类中有一个Teacher类的对象作为成员变量,那么我们在克隆的时候,需要在clone方法中将其他的引用对象一起clone出来,同时Teacher类也要实现Cloneable类的clone方法(此为深克隆):

public class Person implements Cloneable {
    private String name;
    private Integer age;
    private Address address;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object obj=super.clone();
        Address a=((Person)obj).getAddress();
        ((Person)obj).setAddress((Address) a.clone());
        return obj;
    }
}

clone方式深拷贝小结

① 如果有一个非原生成员,如自定义对象的成员,那么就需要:该成员实现Cloneable接口并覆盖clone()方法,不要忘记提升为public可见。同时,修改被复制类的clone()方法,增加成员的克隆逻辑。

② 如果被复制对象不是直接继承Object,中间还有其它继承层次,每一层super类都需要实现Cloneable接口并覆盖clone()方法。

与对象成员不同,继承关系中的clone不需要被复制类的clone()做多余的工作。

一句话来说,如果实现完整的深拷贝,需要被复制对象的继承链、引用链上的每一个对象都实现克隆机制。

可以看出,如果聚合或者继承出现多次或者多个,那么克隆的方式就比较麻烦了,所以我们较多的还是使用工厂类实现对象的创建。

反序列化

反序列化和序列化的概念

把对象转换为字节序列的过程称为对象的序列化;
把字节序列恢复为对象的过程叫做对象的反序列化。

对象的序列化主要有两种用途:
  1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  2) 在网络上传送对象的字节序列。

在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存*。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。*

当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。

java中的序列化和反序列化

java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
  java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

java.io.ObjectOutputStream对象输出流中的writeObject(Object obj)方法----序列化
	java.io.ObjectInputStream 对象输入流中的readOBject()方法---反序列化

只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。

对象序列化包括如下步骤:
  1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
  2) 通过对象输出流的writeObject()方法写对象。
  
  对象反序列化的步骤如下:
  1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
  2) 通过对象输入流的readObject()方法读取对象。

当我们序列化和反序列化一个对象,jvm会给我们创建一个单独的对象。在反序列化时,jvm创建对象并不会调用任何构造函数。为了反序列化一个对象,我们需要让我们的类实现Serializable接口
import java.io.*;
 
public class Demo2 {
    public static void main(String[] args) throws IOException {
 
        ObjectOutputStream obji = new ObjectOutputStream(new FileOutputStream("Object1.txt"));
        Teacher teacher = new Teacher();
        teacher.setName("张三");
        teacher.setAge(20);
 
        obji.writeObject(teacher);
        
    }
}
 
 
package org.westos.Demo;
 
import java.io.Serializable;
 
public class Teacher implements Serializable {
    private  String name;
 
    public Teacher() {
    }
 
    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    private  int 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;
    }
 
    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

如果没有实现Serializable接口,会出现NotSerializableException
1)要求对象中的所有属性也都是可以序列化
2)如果某个属性不想序列化,可以在属性上加transient关键字

反序列化:
1. 把字节内容读取进来,还原为java对象
2. ObjectInputStream用来读取字节内容,还原(反序列化)为java对象

package org.westos.Demo;
 
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
 
public class Demo4 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream Obji = new ObjectInputStream(new FileInputStream("Object1.txt"));
        Teacher t =(Teacher) Obji.readObject();
        System.out.println(t.getName()+t.getAge());
    }
}

除了可以写入和读取对象以外,还可以写入和读取基本类型(int,long,boolean…) ,读取和写入的顺序要保持一致
如果不一致,出现EOFException
如果没有更多内容,也会出现EOFException
建议在写入时最后一个对象使用null,这样读取时就可以根据null来判断是否读取完毕

               序列化和反序列化其实也是java中的一种数据传输的机制

部分转自:https://blog.csdn.net/qq_41942241/article/details/81606226

猜你喜欢

转载自blog.csdn.net/mulinsen77/article/details/84932540