• Java-02对象传递和返回


    Java-02对象传递和返回

    当你在“传递”一个对象的时候,你实际上是在传递它的引用

    1引用

    1.1传递引用

    当你将一个引用传给方法后,该引用指向的仍然是原来的对象:

    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 10:14
     * @PackageName:org.example.onjava.senior.example02ref
     * @ClassName: PassRefences
     * @Description: TODO 传递引用
     * @Version 1.0
     */
    public class PassRefences {
        /**
         * 当你将一个引用传给方法后,该引用指向的仍然是原来的对象
         */
        public static void f(PassRefences h){
            System.out.println("h inside f(): " + h);
        }
    
        public static void main(String[] args) {
            PassRefences p = new PassRefences();
            System.out.println("p inside main(): " + p);
            f(p);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    1.2引用别名

    引用别名指的是不止一个引用被绑定到了同一个对象上的情况。

    别名导致的问题主要发生在有人对对象进行操作时。如果该对象的其他引用的持有者并不希望对象发生变更,那么结果将使其大感意外。

    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 10:18
     * @PackageName:org.example.onjava.senior.example02ref
     * @ClassName: Alias1
     * @Description: TODO 引用别名
     * @Version 1.0
     */
    public class Alias1 {
        private int i;
    
        public Alias1(int ii) {
            this.i = ii;
        }
    
        public static void main(String[] args) {
            Alias1 x = new Alias1(7);
            Alias1 y = x;
            System.out.println("x.i = " + x.i);
            System.out.println("y.i = " + y.i);
            System.out.println("Incrementing x");
            x.i++;
            System.out.println("x.i = " + x.i);
            System.out.println("y.i = " + y.i);
        }
    }
    /**
     * put:
     * x.i = 7
     * y.i = 7
     * Incrementing x
     * x.i = 8
     * y.i = 8
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    为了避免上面的情况,解决方案如下:

    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 10:18
     * @PackageName:org.example.onjava.senior.example02ref
     * @ClassName: Alias1
     * @Description: TODO 引用别名,解决方案
     * @Version 1.0
     */
    public class Alias2 {
        private int i;
    
        public Alias2(int ii) {
            this.i = ii;
        }
    
        public static void f(Alias2 ref){
            ref.i++;
        }
    
        public static void main(String[] args) {
            Alias2 x = new Alias2(7);
            Alias2 y = x;
            System.out.println("x.i = " + x.i);
            System.out.println("y.i = " + y.i);
            System.out.println("Calling f(x_x)");
            f(y);
            System.out.println("x.i = " + x.i);
            System.out.println("y.i = " + y.i);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    2创建本地副本

    Java中所有参数传递都是通过传递引用实现的。也就是说,当你传递“一个对象”时,你实际上传递的是存活于方法外部的指向这个对象的一个引用。因此,如果你通过该引用执行了任何修改,你也将修改外部的对象。

    • 引用别名会在传递参数时自动修改
    • 并没有本地对象,只有本地引用
    • 引用是有作用域的,对象则没有
    • Java中的对象生命周期从来就不是个问题

    2.1值传递

    得到一个本地的副本。

    1. Java传递任何事物时,都是在传递该事物的值。
    2. Java在传递基本类型时,传递的是值,但在传递对象时,传递的则是引用。

    2.2克隆对象

    创建对象的本地副本,最可能的原因时你要修改该对象,但并不想修改调用者的原有对象。这个时候就可以使用clone()方法了。

    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 10:38
     * @PackageName:org.example.onjava.senior.example02ref
     * @ClassName: CloneArrayList
     * @Description: TODO clone()操作
     * @Version 1.0
     */
    class Int{
        private int i;
    
        public Int(int i) {
            this.i = i;
        }
        public void increment(){
            i++;
        }
    
        @Override
        public String toString() {
            return Integer.toString(i);
        }
    }
    public class CloneArrayList {
        public static void main(String[] args) {
            ArrayList<Int> v = IntStream.range(0, 10)
                    .mapToObj(Int::new)
                    .collect(Collectors.toCollection(ArrayList::new));
            System.out.println("v = " + v);
            @SuppressWarnings("unchecked")
            ArrayList<Int> v2 = (ArrayList<Int>)v.clone();
            // Increment all v2's elements:
            v2.forEach(Int::increment);// 对v2中的元素进行自增
            // See if it changed v's elements:
            System.out.println("v: " + v);// 这时会发现v中的元素都+1
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    clone()方法生成了一杯Object,然后该Object必须被转换为合适的类型。本例演示了ArrayList的clone()方法如何不去自动尝试克隆ArrayList中的每个对象——原有的ArrayList和克隆的ArrayList都是同一个对象的不同引用别名。这是一种浅拷贝(Shallow copy),因为只复制了对象的“表层”部分。实际对象的组成部分包括该“表层(引用)”、该引用指向的所有对象,以及所有这些对象所指向的所有对象,以此类推。

    这通常称为“对象网络”。创建所有这些内容的完整副本,称为**“深拷贝(deep copy)”**

    2.3为类增加可克隆能力

    并不是所有类自动具备克隆能力,为了让某个类具备克隆能力,就必须专门添加代码来使其具备克隆的能力:

    • 利用protected的技巧
      • 调用super.clone()
      • 将你的克隆操作设为public
    • 实现Cloneable接口

    2.4成功的克隆

    了解了实现clone()方法的细节,就能够创建出可轻松复制的类,以提供生成本地副本的能力

    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 11:00
     * @PackageName:org.example.onjava.senior.example02ref
     * @ClassName: LocalCopy
     * @Description: TODO 用clone()创建本地副本
     * @Version 1.0
     */
    class Duplo implements Cloneable{
        private int n;
    
        public Duplo(int n) {
            this.n = n;
        }
    
        @Override
        public Duplo clone() {// 为了使cloen能被访问,设置为public
            try {
                return (Duplo) super.clone();// 上面说到的调用super.clone(),协变返回类型。
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
        public int getValue(){
            return n;
        }
        public void setValue(int n){
            this.n = n;
        }
        public void increment(){
            n++;
        }
    
        @Override
        public String toString() {
            return Integer.toString(n);
        }
    }
    public class LocalCopy {
        public static Duplo g(Duplo v){
            // 传递引用,修改了外部的对象
            v.increment();
            return v;
        }
        public static Duplo f(Duplo v){
            v = v.clone(); //本地副本,由于方法进行了协变返回,此处就不需要转型
            v.increment();
            return v;
        }
    
        public static void main(String[] args) {
            Duplo a = new Duplo(11);
            Duplo b = g(a);
            // Reference equivalence, not object equivalence:
            // 引用相等,并不是对象相等
            System.out.println("a == b: " + (a == b) +
                    "\na = " + a + "\nb = " + b);
            System.out.println("a.equals(b) = " + a.equals(b));
            Duplo c = new Duplo(47);
            Duplo d = f(c);// 克隆c赋值给d
            System.out.println("c == d: " + (c == d) +
                    "\nc = " + c + "\nd = " + d);
            System.out.println("c.equals(d) = " + c.equals(d));
        }
    }
    /**
     * Output:
     * a == b: true
     * a = 12
     * b = 12
     * a.equals(b) = true
     * c == d: false
     * c = 47
     * d = 48
     * c.equals(d) = false
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    首先,为了使clone()能被访问,必须将它设为public的。其次,在你的clone()操作的开始部分,调用基类版本的clone()。这里调用的clone()即在Object中预先定义好的那个clone(),你可以调用该方法,是因为它是protected的,可以在子类中被访问。

    Object.clone()会检测出该对象有多大,并为新对象创建足够的内存空间,然后将旧对象所有的二进制都复制到新对象中。这称为按位复制。在这个方法执行之前,会先检查(在继承层次结构中)是否有类是Cloneable的,也就是它是否实现了Cloneable接口。如果没有,Object.clone()会抛出CloneNotSupportedException异常,表示无法进行克隆。

    2.5Object.clone()的效果

    根类中的clone方法负责创建正确大小的存储空间,并执行了从原始对象中的所有二进制位到新对象存储中的按位复制

    也就是说,该方法并不只是创建存储空间和复制一个Object,它实际上会判断要复制的实际对象(不只是基类对象,还包括派生对象)的大小,然后再进行复制。

    所有这些都依靠在根类中定义的clone()方法的代码来实现,而根类并不知道具体哪个对象会被继承,在这个过程中用到了反射来确定要克隆的实际对象。

    这样,clone()方法就可以创建大小合适的存储空间 ,并正确地对该类型执行按位复制

    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 11:25
     * @PackageName:org.example.onjava.senior.example02ref
     * @ClassName: Snake
     * @Description: TODO
     * @Version 1.0
     */
    public class Snake implements Cloneable{
        private Snake next;// 单向链表
        private char c;
    
        public Snake(int i, char x) {
            this.c = x;
            if (--i > 0){
                next = new Snake(i, (char)(x + 1));
            }
        }
        public void increment(){
            c++;
            if (next != null){
                next.increment();// 蛇还活着,自己变长
            }
        }
    
        @Override
        public String toString() {
            String s = ":" + c;
            if (next!= null){
                s += next.toString();
            }
            return s;
        }
    
        @Override
        public Snake clone() {
            try {
                Snake clone = (Snake) super.clone();
                // TODO: 在此处复制可变状态,因此克隆无法更改原始状态的内部
                // TODO: copy mutable state here, so the clone can't change the internals of the original
                return clone;
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    
        public static void main(String[] args) {
            Snake s = new Snake(5, 'a');
            System.out.println("s = " + s);
            Snake s2 = s.clone();
            System.out.println("s2 = " + s2);
            s.increment();
            System.out.println(
                    "after s.increment, s2 = " + s2);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    2.6克隆组合对象

    在你试图深拷贝组合对象时,会遇到一个问题。你必须假设所有成员对象中clone()方法都会按顺序对各自的引用执行深拷贝,并照此进行下去。这一点是必须确保的。它实际上意味着为了正确执行深拷贝,你要么控制所有类的所有代码,要么至少对深拷贝涉及的所有类都足够了解,以确定它们都能正确地执行各自的深拷贝。

    定义一个 DepthReading;

    package org.example.onjava.senior.example02ref.clone_combined;
    
    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 13:20
     * @PackageName:org.example.onjava.senior.example02ref.clone_combined
     * @ClassName: DepthReading
     * @Description: TODO 克隆组合对象01
     * @Version 1.0
     */
    public class DepthReading implements Cloneable{
        private double depth;
    
        public DepthReading(double depth) {
            this.depth = depth;
        }
    
        public double getDepth() {
            return depth;
        }
    
        public void setDepth(double depth) {
            this.depth = depth;
        }
    
        @Override
        public DepthReading clone() {
            try {
                DepthReading clone = (DepthReading) super.clone();
                // TODO: copy mutable state here, so the clone can't change the internals of the original
                return clone;
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    
        @Override
        public String toString() {
            return String.valueOf(depth);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    定义一个 TemperatureReading:

    package org.example.onjava.senior.example02ref.clone_combined;
    
    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 13:22
     * @PackageName:org.example.onjava.senior.example02ref.clone_combined
     * @ClassName: TemperatureReading
     * @Description: TODO 克隆组合对象02
     * @Version 1.0
     */
    public class TemperatureReading implements Cloneable{
        private long time;
        private double temperature;
    
        public TemperatureReading(double temperature) {
            time = System.currentTimeMillis();// 获取当前系统时间
            this.temperature = temperature;
        }
    
        @Override
        public TemperatureReading clone() {
            try {
                TemperatureReading clone = (TemperatureReading) super.clone();
                // TODO: copy mutable state here, so the clone can't change the internals of the original
                return clone;
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    
        public long getTime() {
            return time;
        }
    
        public void setTime(long time) {
            this.time = time;
        }
    
        @Override
        public String toString() {
            return String.valueOf(temperature);
        }
    
        public double getTemperature() {
            return temperature;
        }
    
        public void setTemperature(double temperature) {
            this.temperature = temperature;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    在定义一个拷贝上面两个对象的类,这个类在构造方法中初始化上面两个类

    package org.example.onjava.senior.example02ref.clone_combined;
    
    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 13:23
     * @PackageName:org.example.onjava.senior.example02ref.clone_combined
     * @ClassName: OceanReading
     * @Description: TODO
     * @Version 1.0
     */
    public class OceanReading implements Cloneable{
        private DepthReading depth;
        private TemperatureReading temperature;
    
        public OceanReading(double tdata, double ddata) {
            this.depth = new DepthReading(ddata);
            this.temperature = new TemperatureReading(tdata);
        }
    
        /**
         * 这里必须对 《构造方法》 中的克隆对象进行   克隆引用
         * @return
         */
        @Override
        public OceanReading clone() {
            OceanReading or = null;
            try {
                or = (OceanReading) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
            // 必须克隆引用:
            or.depth = (DepthReading)or.depth.clone();
            or.temperature = (TemperatureReading)or.temperature.clone();
            return or;
        }
    
        public DepthReading getDepth() {
            return depth;
        }
    
        @Override
        public String toString() {
            return "depth=" + depth +
                    ",temperature=" + temperature;
        }
    
        public void setDepth(DepthReading depth) {
            this.depth = depth;
        }
    
        public TemperatureReading getTemperature() {
            return temperature;
        }
    
        public void setTemperature(TemperatureReading temperature) {
            this.temperature = temperature;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    下面对拷贝组合对象进行测试:

    class DeepCopyTest {
    
        @Test
        public void testClone(){
            OceanReading reading = new OceanReading(33.9, 100.5);
            // 进行克隆
            OceanReading clone = reading.clone();
            TemperatureReading tr = clone.getTemperature();
            tr.setTemperature(tr.getTemperature() + 1);
            clone.setTemperature(tr);
    
            DepthReading dr = clone.getDepth();
            dr.setDepth(dr.getDepth() + 1);
            clone.setDepth(dr);
            assertEquals(reading.toString(),"depth=100.5,temperature=33.9");
            assertEquals(clone.toString(),"depth=101.5,temperature=34.9");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    DepthReading和TemperatureReading很相似,它们都只包含基本类型。因此,clone()方法可以很简单:调用super.clone(),然后返回结果。这两者的clone是完全相同的。

    OceanReading是由DepthReading和TemperatureReading对象组合而成的,因此,要实现深拷贝,OceanReading的clone()就必须克隆OceanReading内部的所有引用才行。要完成这项任务,super.clone()的结果必须转型为OceanReading对象(这样才可以访问depth和tempterature的引用)

    2.7深拷贝ArrayList

    对ArrayList进行深拷贝:

    package org.example.onjava.senior.example02ref.clone_arraylist;
    
    import java.util.ArrayList;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 13:54
     * @PackageName:org.example.onjava.senior.example02ref.clone_arraylist
     * @ClassName: AddingClone
     * @Description: TODO 深拷贝ArrayList
     * @Version 1.0
     */
    class Int2 implements Cloneable{
        private int i;
    
        public Int2(int i) {
            this.i = i;
        }
        public void increment(){
            i++;
        }
    
        @Override
        public String toString() {
            return Integer.toString(i);
        }
    
        @Override
        public Int2 clone() {
            try {
                Int2 clone = (Int2) super.clone();
                // TODO: copy mutable state here, so the clone can't change the internals of the original
                return clone;
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    }
    // 继承不会移除可克隆性
    class Int3 extends Int2{
        private int j;// 自动创建了副本
    
        public Int3(int i) {
            super(i);
        }
    }
    public class AddingClone {
        @SuppressWarnings("unchecked")
        public static void main(String[] args) {
            Int2 x = new Int2(10);
            Int2 x2 = x.clone();
            x2.increment();
            System.out.println(
                    "x = " + x + ", x2 = " + x2);
            // 继承出的任何事物同样也是可克隆的
            Int3 x3 = new Int3(7);
            x3 = (Int3) x3.clone();
            System.out.println("x3 = " + x3);
            Int2 clone = x3.clone();
            System.out.println("clone = " + clone);
    
            ArrayList<Int2> v = IntStream.range(0, 10)
                    .mapToObj(Int2::new)
                    .collect(Collectors.toCollection(ArrayList::new));
            System.out.println("v = " + v);
            ArrayList<Int2> v2 = (ArrayList<Int2>) v.clone();
            // 现在克隆每个元素
            IntStream.range(0, v.size())
                    .forEach(i -> v2.set(i, v.get(i).clone()));
            v2.forEach(Int2::increment);
            // 一旦克隆了一个对象,你就可以对副本进行修改,而原本对象不会受到影响
            System.out.println("v2 = " + v2);
            // 查看v中的元素是否发生改变
            System.out.println("v = " + v);// 没有改变,复制成功
        }
    }
    /**
     * output:
     * x = 10, x2 = 11
     * x3 = 7
     * clone = 7
     * v = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     * v2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
     * v = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
     */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87

    Int3继承自Int2,并增加了一个新的基本类型成员int j。在调用Int3的clone()时,其内部所调用的Int2的clone(),实际上调用的是Object.clone(),它检测到此处起作用的是Int3,并对Int3执行了按位复制。只要你不增加需要克隆的引用,对Object.clone()的一次调用便可以执行所有必要的复制,不论clone定义在继承层次结构中多么深的位置。

    要深拷贝ArrayList,需要这么做:在克隆ArrayList后,还需要进一步克隆ArrayList所指向的每一个对象。如果要深拷贝入HashMap这样的内容,你也需要执行类似的操作。

    一旦克隆了一个对象,你就可以对副本进行修改,而原本对象不会受到影响

    2.8通过序列化进行深拷贝

    如果一个对象先进行序列化,再将其反序列化,那么它实际上就是被克隆了。

    接下来就使用序列化来实现深拷贝:

    package org.example.onjava.senior.example02ref.clone_serializable;
    
    import org.example.onjava.onjavaUtils.Timer;
    
    import java.io.*;
    
    /**
     * @Author Coder_Pans
     * @Date 2022/11/20 14:27
     * @PackageName:org.example.onjava.senior.example02ref.clone_serializable
     * @ClassName: Compete
     * @Description: TODO 序列化实现深拷贝
     * @Version 1.0
     */
    class Thing1 implements Serializable {
    }
    
    class Thing2 implements Serializable {
        Thing1 t1 = new Thing1();
    }
    
    class Thing3 implements Cloneable {
    
        @Override
        public Thing3 clone() {
            try {
                return (Thing3) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    }
    
    class Thing4 implements Cloneable {
        private Thing3 t3 = new Thing3();
    
        @Override
        public Thing4 clone() {
            Thing4 t4 = null;
            try {
                t4 = (Thing4) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
            //TODO 对字段也要进行克隆
            t4.t3 = t3.clone();
            return t4;// Thing4对象所包含的所有内容克隆完之后才能够返回。
        }
    }
    
    public class Compete {
        public static final int SIZE = 10000;
    
        public static void main(String[] args) {
            Thing2[] a = new Thing2[SIZE];
            for (int i = 0; i < SIZE; i++) {
                a[i] = new Thing2();
            }
    
            Thing4[] b = new Thing4[SIZE];
            for (int i = 0; i < SIZE; i++) {
                b[i] = new Thing4();
            }
            Timer timer = new Timer();
            try (ByteArrayOutputStream buf = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(buf)) {
                for (Thing2 a1 : a) {
                    oos.writeObject(a1);
                }
                // Now get copies:
                try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()))) {
                    Thing2[] c = new Thing2[SIZE];
                    for (int i = 0; i < SIZE; i++)
                        c[i] = (Thing2) in.readObject();
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException(e);
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            System.out.println("Duplication via serialization: " + timer.duration() + " Milliseconds");
            // Now try cloning:
            timer = new Timer();
            Thing4[] d = new Thing4[SIZE];
            for (int i = 0; i < SIZE; i++)
                d[i] = b[i].clone();
            System.out.println("Duplication via cloning: " + timer.duration() + " Milliseconds");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    Thing2和Thing4包含了成员对象,所以需要进行一些深拷贝操作。Serializable类很容易构建,但是需要大量额外操作来复制它们。另外,在克隆所需要的操作中,类的构建工作更多,但是实际的对象复制操作相对简单

    序列化比克隆慢的多得多!

    Duplication via serialization: 115 Milliseconds
    Duplication via cloning: 3 Milliseconds
    
    • 1
    • 2
  • 相关阅读:
    2.链表--链表环相关问题
    MySQL事务管理
    目标检测YOLO实战应用案例100讲-基于改进YOLOv5的口罩人脸检测
    基于大数据的高校英语现状分析
    游戏源码编程软件,对于新手来说十分友好,纯中文的界面让所有功能都一目了然,操作相当简单
    MySQL-sql的优化
    代码随想录算法训练营第一天
    快速入门:如何使用HTTP代理进行网络请求
    【Linux-进程状态】
    查找算法【二叉查找树】 - 二叉查找树的查找
  • 原文地址:https://blog.csdn.net/weixin_45688141/article/details/127949044