JAVA高级技术-对象克隆(几种深克隆效率比较)

回城传送–》《JAVA筑基100例》

一、题目描述

题目:对象的克隆是Java一项高级技术,可以根据给定的对象,获得与其完全相同的另一个对象。

前面介绍了三种实现深克隆的方式,现在对这三种方式克隆10万个对象,并输出花费的时间。这样大家就可以有个直观的认识。

二、解题思路-序列化克隆

再创建一个员工类Employee

定义三个成员变量表示:员工名字,年龄

使用构造方法对它们进行赋值。

并提供对应的get方法和set方法。

重写toString()方法和clone()方法。

三、代码详解

员工类:

public class Employee implements Cloneable, Serializable {
    private static final long serialVersionUID = 5022956767440380940L;
    private String name; // 表示员工的姓名
    private int age; // 表示员工的年龄

    public Employee(String name, int age) {// 利用构造方法初始化各个域
        this.name = name;
        this.age = age;
    }

    public Employee() {// 利用构造方法初始化各个域
        super();
    }

    @Override
    public String toString() {// 重写toString()方法
        StringBuilder sb = new StringBuilder();
        sb.append("姓名:" + name + ", ");
        sb.append("年龄:" + age + "\n");
        return sb.toString();
    }

    @Override
    protected Employee clone() {// 使用父类的clone()方法实现深克隆
        Employee employee = null;
        try {
            employee = (Employee) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return employee;
    }
}

测试类

public class Test {

    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<Employee>();// 创建列表保存对象
        Employee employee = new Employee("小虚竹", 25);// 创建Employee类的对象
        long currentTime = System.currentTimeMillis();// 获得当前系统时间
        for (int i = 0; i < 100000; i++) {
            employees.add(employee.clone());// 使用克隆方式获得对象
        }
        System.out.println("clone()方法克隆花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");
        currentTime = System.currentTimeMillis();// 获得当前时间

        for (int i = 0; i < 100000; i++) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();// 创建字节数组输出流
            ObjectOutputStream out = null;
            try {
                out = new ObjectOutputStream(baos);// 创建对象输出流
                out.writeObject(employee);// 将对象写入到输出流中
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (out != null) {
                    try {
                        out.close();// 释放资源
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            // 获得字节数组输出流内容
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream in = null;
            try {
                in = new ObjectInputStream(bais);// 创建对象输入流
                employees.add((Employee) in.readObject());// 读取对象
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                if (in != null) {
                    try {
                        in.close();// 释放资源
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        System.out.println("序列化花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");
        currentTime = System.currentTimeMillis();// 获得当前时间
        
        for (int i = 0; i < 100000; i++) {
            Employee employee2 = new Employee();
            BeanUtils.copyProperties(employee,employee2);
        }
        System.out.println("BeanUtils.copyProperties花费时间:" + (System.currentTimeMillis() - currentTime) + "毫秒");
    }
}

如图

原生序列化和Kryo序列化性能比较

原生序列化:

package com.xiaoxuzhu;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * Description: 原生序列化
 *
 * @author xiaoxuzhu
 * @version 1.0
 *
 * <pre>
 * 修改记录:
 * 修改后版本	        修改人		修改日期			修改内容
 * 2022/5/20.1	    xiaoxuzhu		2022/5/20		    Create
 * </pre>
 * @date 2022/5/20
 */
public class OriginalSerializable {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        long start =  System.currentTimeMillis();
        setSerializableObject();
        System.out.println("java原生序列化时间:" + (System.currentTimeMillis() - start) + " ms" );
        start =  System.currentTimeMillis();
        getSerializableObject();
        System.out.println("java原生反序列化时间:" + (System.currentTimeMillis() - start) + " ms");
    }

    public static void setSerializableObject() throws IOException{

        FileOutputStream fo = new FileOutputStream("D:/file.bin");
        ObjectOutputStream so = new ObjectOutputStream(fo);
        Employee employee = null;
        for (int i = 0; i < 100000; i++) {
             employee = new Employee("小虚竹", 25);// 创建Employee类的对象
            so.writeObject(employee);
        }
        so.writeObject(null);//为了解决EOF异常
        so.flush();
        so.close();
    }

    public static void getSerializableObject(){
        FileInputStream fi;
        try {
            fi = new FileInputStream("D:/file.bin");
            ObjectInputStream si = new ObjectInputStream(fi);

            Employee employee =null;
            while((employee=(Employee)si.readObject()) != null){
            }
            fi.close();
            si.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();  
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

Kyro序列化:

package com.xiaoxuzhu;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.objenesis.strategy.StdInstantiatorStrategy;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
/**
 * Description: Kyro序列化
 *    kryo.KryoException: Buffer underflow.
 * @author xiaoxuzhu
 * @version 1.0
 *
 * <pre>
 * 修改记录:
 * 修改后版本	        修改人		修改日期			修改内容
 * 2022/5/20.1	    xiaoxuzhu		2022/5/20		    Create
 * </pre>
 * @date 2022/5/20
 */
public class KyroSerializable {
    public static void main(String[] args) throws IOException {
        long start =  System.currentTimeMillis();
        setSerializableObject();
        System.out.println("Kryo 序列化时间:" + (System.currentTimeMillis() - start) + " ms" );
        start =  System.currentTimeMillis();
        getSerializableObject();
        System.out.println("Kryo 反序列化时间:" + (System.currentTimeMillis() - start) + " ms");

    }

    public static void setSerializableObject() {
        Output output = null;
        try {
            Kryo kryo = new Kryo();
            kryo.setReferences(false);
            kryo.setRegistrationRequired(false);
            kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
            kryo.register(Employee.class);
             output = new Output(new FileOutputStream("D:/file1.bin"));
            Employee employee = null;
            for (int i = 0; i < 100000; i++) {
                employee = new Employee("小虚竹", 25);// 创建Employee类的对象
                kryo.writeObject(output, employee);
            }
            output.flush();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if(output !=null){
                output.close();
            }
        }
    }


    public static void getSerializableObject(){
        Kryo kryo = new Kryo();
        kryo.setReferences(false);
        kryo.setRegistrationRequired(false);
        kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
        Input input = null;
        try {
            input = new Input(new FileInputStream("D:/file1.bin"));
            Employee employee =null;
            while((employee=kryo.readObject(input, Employee.class)) != null){
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch(KryoException e){
        }finally {
            if(input !=null){
                input.close();
            }
        }
    }

}

10万条测试结果:

java原生序列化时间:2762 ms
java原生反序列化时间:3405 ms

Kryo 序列化时间:575 ms
Kryo 反序列化时间:156 ms

Kryo 的性能是原生的序列化性能二十几倍。

结论

1、使用clone方法克隆(不实用):

- 优点:最快的,10万条数据可以控制在100ms内
- 缺点:但这个的使用也是最麻烦的,类要重写clone方法,有引类类型的参数类也要重写clone方法

2、使用原生序列化克隆(不实用):

- 优点:序列化不需要每个对象都重写clone方法,同时支持类里的引用类型参数的深克隆
- 缺点:花费的时间最长

3、使用第三方工具方法 BeanUtils.copyProperties,克隆时间适中:

- 优点:使用简单方便
- 缺点:只能进行浅克隆,对于引用类型的参数是无法克隆的,只是复制引用,不是克隆值 ,所以要额外处理。

4、使用Kryo 序列化效果比用原生序列化好

企业级应用开发中:

  • 简单的对象克隆(浅克隆),可以用 BeanUtils.copyProperties

  • 复杂对象的序列化克隆,可以用Kryo 序列化处理。

四、推荐专栏

《JAVA从零到壹》

《JAVA从零到壹》第四讲:类与对象基础

第六讲:数组包及访问控制

《JAVA从零到壹》第七讲:面向对象高级特性

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论
Java语言中,我们可以使用两种方法来实现对象克隆。 1. 通过实现Cloneable接口并重写clone()方法: 首先,在需要克隆对象类中实现Cloneable接口,该接口是一个标记接口,表示该类支持克隆操作。然后,重写clone()方法,该方法是Object类中的一个非final方法,用于创建并返回一个新的对象。在这个方法内部,我们可以调用super.clone()方法来获得一个浅表克隆,也可以根据需要对属性进行深度克隆。最后,我们可以通过调用该对象的clone()方法来创建该对象的一个克隆副本。 2. 通过实现Serializable接口并使用序列化和反序列化: 首先,在需要克隆对象类中实现Serializable接口,该接口是一个标记接口,表示该类支持序列化。然后,我们可以通过将该对象序列化为字节流,并再将字节流反序列化为一个新的对象来实现克隆。这种方法实现了对象拷贝,即复制了原对象及其内部的所有引用对象。 这两种方法在使用上有一些不同。实现Cloneable接口并重写clone()方法的方式更为直接,但需要开发者自行处理属性深度克隆问题。而使用序列化和反序列化方式则相对更容易实现对象拷贝,但在性能上略逊于直接克隆。 需要注意的是,使用clone()方法进行对象克隆时,会调用对象的构造函数来创建新的对象,而使用序列化和反序列化方式则不会。所以在使用这两种方法时,需要确保对象类的构造函数是正确的和可访问的。此外,还需要确保被克隆对象类中的所有引用类型的成员变量都是可序列化的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小虚竹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值