Java克隆方式避免频繁创建对象优化方案

简介: Java克隆方式避免频繁创建对象优化方案

1 克隆介绍

直接使用new关键字创建的对象,是一个新的对象,没有任何数据(初始化的默认值)

使用克隆创建的对象,可以复制对象的数据

Java中数据类型有值类型(八大基本数据类型)和引用类型(类,数组,接口)

基本类型复制值,引用类型复制引用地址而不是对象本身


浅克隆、深克隆区别在于是否支持引用类型的成员变量的复制


1.1 浅克隆

如果对象的成员变量是基本类型,克隆对象会拿到成员的值

如果对象的成员变量是引用类型,克隆对象会拿到成员的引用地址

在浅克隆中:源对象和克隆对象的成员变量指向相同的内存地址


1.1.1 浅克隆实现:

1.1.2 浅拷贝

1、实现Cloneable接口,重写Object中的clone()方法

谈此之前,我们先看一个例子,定义一个名为 Company 的类,并添加一个类型为 User 的成员变量:

public class Company implements Cloneable{
    private User user;
    private String address;
    public Company(User user, String address) {
        super();
        this.user = user;
        this.address = address;
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public boolean equals(Object obj) {
        Company company = (Company) obj;
        if (user.equals(company.getUser()) && address.equals(company.address)) {
            return true;
        }
        return false;
    }
}

测试代码及测试结果如下:

    public static void main(String[] args) throws CloneNotSupportedException{
        Company companyOne, companyTwo, companyThree;
        companyOne = new Company(new User("username", "password"), "上海市");
        companyTwo = companyOne;
        companyThree = (Company) companyOne.clone();
        System.out.println(companyTwo==companyOne);                //true
        System.out.println(companyTwo.equals(companyOne));        //true
        System.out.println(companyThree==companyOne);            //false
        System.out.println(companyThree.equals(companyOne));    //true
        System.out.println(companyThree.getUser()==companyOne.getUser());            //true ? 这里为什么不是false呢
        System.out.println(companyThree.getUser().equals(companyOne.getUser()));    //true
    }

问题来了,companyThree 与 companyOne 中的 User 是同一个对象!也就是说 companyThree 只是克隆了 companyOne 的基本数据类型的数据,而对于引用类型的数据没有进行深度的克隆。也就是俗称的浅克隆。


浅克隆:顾名思义,就是很表层的克隆,只克隆对象自身的引用地址;


深克隆:也称“N层克隆”,克隆对象自身以及对象所包含的引用类型对象的引用地址。


这里需要注意的是,对于基本数据类型(primitive)和使用常量池方式创建的String 类型,都会针对原值克隆,所以不存在引用地址一说。当然不包括他们对应的包装类。


1.2 深克隆

1.2.1 递归调用clone()方法

所以使用深克隆就可以解决上述 Company 对象克隆过后两个 user 对象的引用地址相同的问题。我们修改一下 Company 类的 clone() 函数。

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Company company = (Company) super.clone();
        company.user = (User) company.getUser().clone();
        return company;
    }

再运行测试代码,就能得到 companyThree.getUser()==companyOne.getUser() 为 false 的结果了。从实现过程来说,递归克隆存在克隆过程多且复杂的缺点

1.2.2 通过序列化方式

public class Text implements Serializable{
    private static final long serialVersionUID = 8723901148964L;
    private int age;
    private Name name;
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Name getName() {
        return name;
    }
    public void setName(Name name) {
        this.name = name;
    }
    public Object myClone(){
        Text text=null;
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos=new ObjectOutputStream(bos);
            oos.writeObject(this);
            ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois=new ObjectInputStream(bis);
            text=(Text)ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return text;
    }
}
class Name implements Serializable {
    private static final long serialVersionUID = 872390113109L;
    private String name;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public String toString() {
        return name;
    }
}

结果分析:


采用深克隆能有效隔离源对象与克隆对象的联系。


从实现过程来说,递归克隆存在克隆过程多且复杂的缺点,所以建议采用序列化的方式进行深克隆。


总结:


浅克隆中,克隆对象的引用对象和原对象使用的同一个对象

深克隆中,克隆对象的引用对象都是重新创建的对象,也就是完全独立的两个对象

3 Java频繁创建对象优化方案

目前在项目中,需要频繁的创建对象,导致程序比较慢。


3.1 优化思路

假如创建的对象,需要在 if 判断条件中使用,则在 if 判断条件中new新对象,这样可以减少对象的创建。

使用浅拷贝方案解决

Cloneable


对于将会频繁创建的对象,我们要让这个类实现Cloneable接口,因为这个优化的核心,就是利用clone。

clone的最大特点就是,不会去调用任何构造方法,所以,在我看来重点应该放在构造方法中。下面我们来实现。

3.2 具体实现

1.对于需要频繁创建的实体类,需要实现Serializable和Cloneable接口


2.在此实体类中写一个getInstance(),其中就是返回clone()

import lombok.Data;
import java.io.Serializable;
@Data
public class User implements Serializable,Cloneable{
    private static final long serialVersionUID = 1L;
    private static User  user = new User();
    /**
     * 用户id
     */
    private Long userId;
    /**
     * 用户名称
     */
    private String userName;
    /**
     * 调用对象创建优化
     * @return
     */
    public static User getInstance(){
        try {
            return (User) user.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return new User();
    }
}

3.在方法中新建对象的时候,直接getInstance()就可以。

这样既避免了单例设计模式的线程安全问题,保证每次创建的对象不一样。

if (1=1) {
                    // 将创建对象放到if中,不进入if则不创建,提高效率
//                    SysMenuManagerGrant grant = new SysMenuManagerGrant();
                    User user = User.getInstance();
                }

如果一时间内,频繁创建某对象时,这些平时不显眼的消耗一叠加起来,就变得很客观了。但是,当我们使用clone的话,就可以避免这个问题。

目录
相关文章
|
21天前
|
Java 流计算
利用java8 的 CompletableFuture 优化 Flink 程序
本文探讨了Flink使用avatorscript脚本语言时遇到的性能瓶颈,并通过CompletableFuture优化代码,显著提升了Flink的QPS。文中详细介绍了avatorscript的使用方法,包括自定义函数、从Map中取值、使用Java工具类及AviatorScript函数等,帮助读者更好地理解和应用avatorscript。
利用java8 的 CompletableFuture 优化 Flink 程序
|
20天前
|
缓存 算法 Java
Java中的内存管理:理解与优化
【10月更文挑战第6天】 在Java编程中,内存管理是一个至关重要的主题。本文将深入探讨Java内存模型及其垃圾回收机制,并分享一些优化内存使用的策略和最佳实践。通过掌握这些知识,您可以提高Java应用的性能和稳定性。
42 4
|
2天前
|
Java 数据库连接 数据库
优化之路:Java连接池技术助力数据库性能飞跃
在Java应用开发中,数据库操作常成为性能瓶颈。频繁的数据库连接建立和断开增加了系统开销,导致性能下降。本文通过问题解答形式,深入探讨Java连接池技术如何通过复用数据库连接,显著减少连接开销,提升系统性能。文章详细介绍了连接池的优势、选择标准、使用方法及优化策略,帮助开发者实现数据库性能的飞跃。
14 4
|
4天前
|
缓存 前端开发 JavaScript
9大高性能优化经验总结,Java高级岗必备技能,强烈建议收藏
关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。本文介绍了9种性能优化方法,涵盖代码优化、数据库优化、连接池调优、架构层面优化、分布式缓存、异步化、Web前端优化、服务化、硬件升级、搜索引擎和产品逻辑优化。欢迎留言交流。
|
4天前
|
存储 缓存 Java
Java应用瘦身记:Docker镜像从674MB优化至58MB的实践指南
【10月更文挑战第22天】 在容器化时代,Docker镜像的大小直接影响到应用的部署速度和运行效率。一个轻量级的Docker镜像可以减少存储成本、加快启动时间,并提高资源利用率。本文将分享如何将一个Java基础Docker镜像从674MB缩减到58MB的实践经验。
11 1
|
5天前
|
消息中间件 监控 算法
Java性能优化:策略与实践
【10月更文挑战第21】Java性能优化:策略与实践
|
5天前
|
SQL 监控 Java
Java性能优化:提升应用效率与响应速度的全面指南
【10月更文挑战第21】Java性能优化:提升应用效率与响应速度的全面指南
|
16天前
|
存储 算法 Java
深入理解Java虚拟机(JVM)及其优化策略
【10月更文挑战第10天】深入理解Java虚拟机(JVM)及其优化策略
31 1
|
28天前
|
监控 Java
Java的JVM如何优化?
Java的JVM如何优化?
51 3
|
10天前
|
缓存 Java 数据处理
java查询大量数据优化
通过结合的高性能云服务,如其提供的弹性计算资源与全球加速网络,可以进一步增强这些优化策略的效果,确保数据处理环节更加迅速、可靠。蓝易云不仅提供稳定的基础架构,还拥有强大的安全防护和灵活的服务选项,是优化大型数据处理项目不可或缺的合作伙伴。
21 0