细说java_java barrier

细说java_java barrier今天来说说cloneable,java中创建对象除了使用new关键字,反射,还有一种方式就是clone。有一个设计模式叫原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,简单的说就是clone一个对象实例,使得clone出来的对象和原有的对象一模一样,两个对象相互不影响。什么时候使用clone呢,clone又存在哪些问题?  1.clone的使用场景…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

今天来说说cloneable,java中创建对象除了使用new关键字,反射,还有一种方式就是clone。有一个设计模式叫原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象,简单的说就是clone一个对象实例,使得clone出来的对象和原有的对象一模一样,两个对象相互不影响。

什么时候使用clone呢,clone又存在哪些问题?

 

 

1. clone的使用场景

我们都知道,java中对象的传递是引用传递,如果把一个对象传递给另一个类,但是我们不想让该对象被修改,就可以使用clone。当然,如果这个类是不可变类,使用clone就没有必要了。

 

 

2. clone分类

  • clone分为浅clone和深clone,浅clone(默认的)只能clone基本数据类型和不可变类型(如string,其实不是clone了而是无需clone),深clone(比如clone主对象中某个复杂对象)要自己实现。

我们平时开发中也用到过Serializable,它可以替代深clone,但是效率(走IO)和编码复杂度上比clone差,毕竟clone是java native实现的。

还有一种clone的替代方案,就是使用一个静态函数,手动赋值:

    private static class Person {
        
        String name;
        int age;

        static Person copyOf(Person person) {
            Person person1 = new Person();
            person1.name = person.name;
            person1.age = person.age;
            return person1;
        }
    }

 

 

3. clone的基本准则

clone后的对象和原型的内容一样,但又是彼此隔离的。即在clone之后,改变其中一个不影响另外一个。

 

 

4. Object的clone

    protected native Object clone() throws CloneNotSupportedException;

Object提供的clone方法是native实现的,对一个对象a的clone就是在堆上分配一个和a在堆上所占存储空间一样大的一块地方,然后把a的堆上内存的内容复制到这个新分配的内存空间上。默认的实现中基本数据类型是直接赋值,而对象则是复制了一下引用,也就是浅拷贝。

看一个例子:

    public static void main(String[] args) {
        Worker worker = new Worker();
        worker.name = "kuang";
        worker.age = 27;
        Manager manager = new Manager();
        manager.name = "zhao";
        manager.age = 30;
        manager.worker = worker;

        try {
            Manager clone = manager.clone();
            clone.name = "xue";
            clone.age = 31;
            clone.worker.age = 28;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println(manager.name);
        System.out.println(manager.age);
        System.out.println(manager.worker.age);
    }

    private static class Worker {

        String name;
        int age;
    }

    private static class Manager implements Cloneable {

        String name;
        int age;
        Worker worker;

        @Override
        protected Manager clone() throws CloneNotSupportedException {
            return (Manager) super.clone();
        }
    }

执行输出:

zhao
30
28

使用Object默认的clone方法,必须重写clone方法,即使是调用默认的实现,否则clone时会报错。可以看到clone后的对象,不能改变原有对象的基本类型属性(age),但是会改变原有对象的引用对象属性(worker)。注意,上面Manager类中的name类型是String,不是基本数据类型,属于对象,那它为什么也不会改变呢?

因为String是不可变类,没有方法可以改变它的内部状态。相关原理可以看看我前面写的一篇文章:《Java篇 – 从内到外剖析String类以及使用String时的性能优化》

https://blog.csdn.net/u014294681/article/details/85169691

 

 

5. 如何正确clone?

  • 1. 实现Cloneable接口。
  • 2. 调用super.clone拿到一个对象,如果父类的clone实现没有问题的话,在该对象的内存存储中,所有父类定义的属性都已经clone好了,该类中的基本数据类型和不可变类型引用也克隆好了,可变类型引用都是浅copy,复制引用。
  • 3. 把浅copy的引用指向原型对象新的克隆体。

改一下刚刚这个例子:

    private static class Worker implements Cloneable {

        String name;
        int age;

        @Override
        protected Worker clone() throws CloneNotSupportedException {
            return (Worker) super.clone();
        }
    }

    private static class Manager implements Cloneable {

        String name;
        int age;
        Worker worker;

        @Override
        protected Manager clone() throws CloneNotSupportedException {
            Manager manager = (Manager) super.clone();
            if (worker != null)
                manager.worker = worker.clone();
            return manager;
        }
    }

执行输出:

zhao
30
27

可以看到经过这样的clone方式,实现了对象属性的深clone,两个对象互不影响。但是如果Manager中的worker属性是final修饰的,比如只在构造器中赋值,那么第三步是走不下去的,所以使用clone时得依照情况来选择是否使用。

 

 

6. 几种clone方案的比较

前面讲了系统的clone方案和静态方法手动实现clone,还有一个没讲,就是Serializable。

我们用serializable来实现上面例子的clone功能:

    private static class Worker implements Serializable {
        private static final long serialVersionUID = 872390113109L;

        String name;
        int age;
    }

    private static class Manager implements Serializable {
        private static final long serialVersionUID = 369285298572941L;

        String name;
        int age;
        Worker worker;

        public Manager cloneManager() {
            Manager manager = null;
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(this);
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bais);
                manager = (Manager) ois.readObject();
            } catch (ClassNotFoundException | IOException e) {
                e.printStackTrace();
            }
            return manager;
        }
    }

    public static void main(String[] args) {
        Worker worker = new Worker();
        worker.name = "kuang";
        worker.age = 27;
        Manager manager = new Manager();
        manager.name = "zhao";
        manager.age = 30;
        manager.worker = worker;

        Manager clone = manager.cloneManager();
        clone.name = "xue";
        clone.age = 31;
        clone.worker.age = 28;
        
        System.out.println(manager.name);
        System.out.println(manager.age);
        System.out.println(manager.worker.age);
    }

输出结果和深clone一样:

zhao
30
27

 

那么如何选择clone方案呢?

使用Serializable同样可以做到对象的clone,但是Cloneable本身就是为clone设计的,虽然有一些缺点,但是如果可以的话无疑用它来做clone比较合适。如果不行的话用原型构造函数,或者静态copy方法也可以。现在JVM越来越快了,系统默认的native实现性能还是不错的,clone一个有100个元素的int数组,用系统默认的clone比静态copy方法快2倍左右。

Serializable制作clone的话,添加了太多其它的东西,增加了复杂性。

  • 1. 所有的相关的类都得支持Serializable,这个工作量和cloneable差不多。
  • 2. Serializable添加了更多的意义,除了提供一个方法用Serializable制作Clone,该类等于也添加了其它的public API,如果一个类实现了Serializable,等于它的二进制形式就已经是其API的一部分了,不便于该类以后内部的改动。
  • 3. 用Serializable来实现clone时,保存了一个老版本的对象二进制,如果该类更新了,那么在clone时还得考虑类版本兼容性的问题。
  • 4. Serializable需要走IO,性能上存在很大问题。

 

上面提到了一些Serializable的东西,下一篇文章我将带大家来理解下Serializable。

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/181951.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Tensorflow数据读取之tfrecord

    Tensorflow数据读取之tfrecord文章目录tfrecordtfrecord的使用流程写入tfrecord文件读取tfrecord文件tfrecord中的数据格式tfrecord中对于变长数据和定长数据的处理tfrecord中生成batch_data的方法插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数…

  • 如何利用python读excel数据_python在excel应用实例

    如何利用python读excel数据_python在excel应用实例文章目录python读取excel表数据的方法:完整的程序代码python读取excel表数据的方法:首先安装Excel读取数据的库xlrd;然后获取Excel文件的位置并且读取进来;接着读取指定的行和列的内容,并将内容存储在列表中;最后运行程序即可。python读取excel表数据的方法:安装Excel读取数据的库—–xlrd直接pipinstallxlrd安装xlrd库#引入Excel库的xlrdimportxlrd获取Excel文件的位置并且读取进来#导入需要读取Exc

  • mybatisplus多源配置报错:Failed to determine a suitable driver class

    mybatisplus多源配置报错:Failed to determine a suitable driver class解决方式:启动类注解@SpringBootApplication(exclude=DruidDataSourceAutoConfigure.class)

    2022年10月26日
  • C++ 数组求和_excel数组求和公式

    C++ 数组求和_excel数组求和公式C++数组求和用自带的库超级方便,而且也达到代码更加简洁的目的。你只需要做2步:1、导入库文件include2、添加空间名usingnamespacestd;然后就可以直接使用:accumulate(arrayName,arrayName+数组长度,数组起始位)demo:intarray[6]={1,2,3,4,5,6};in…

  • OpenCV繪圖的實作-cvCircle,cvEllipse,cvEllipseBox

    OpenCV繪圖的實作-cvCircle,cvEllipse,cvEllipseBox转自:http://www.90in.net/space-4-do-blog-id-10218.htmlOpenCV繪圖的實作-cvCircle,cvEllipse,cvEllipseBox圓的製作,要給他目標圖片,圓心座標,半徑,其他參數等.cvCircle()函式的實作#include<cv.h>#include<highgui.h>IplImag…

  • 京东数据库泄露事件分析报告_某网站的用户数据库泄露

    京东数据库泄露事件分析报告_某网站的用户数据库泄露猪猪侠·2014/03/0112:020×00背景昨天一张截图在QQ群里疯传,并说京东数据库泄露,里面有钱的尽快转。有人问“我挨个试了一下,只有3个可以正常登陆,而且网上也并没有完整的数据库流出,所以我妄猜是否是支付宝或者是其他竞争对手的营销手段?”这件事件到底是真的假的呢?经过我的研究,京东的数据库没有被脱裤,只是无聊黑客的恶作剧,他们通过收集互联网已泄露的用户+密码信息,生成对应的字典…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号