由 System.arraycopy 引发的巩固:对象引用 与 对象 的区别

作者:林冠宏 / 指尖下的幽灵

腾讯云+社区:https://cloud.tencent.com/developer/user/1148436/activities

掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8

博客:http://www.cnblogs.com/linguanh/

GitHub : https://github.com/af913337456/

联系方式 / Contact:913337456@qq.com

了解这些术语:

  • 深复制又称深拷贝,两个变量的内存地址不一样,各自修改不影响对方。
  • 浅复制又称浅拷贝,两个变量的内存地址一样,既是同一个变量,仅仅是引用不同罢了,各自修改是会影响对方的,因为本身就是同一个。

这篇文文我要讲的有:

  • System.arraycopy 是深复制
  • System.arraycopy 的陷阱点
  • 对象引用对象 的区别
  • 简历不要写 精通java,写 熟练

首先明确一点,System.arraycopy 操作的是数组,效果是深复制。 是不是觉得怎么和你印象的中不一样? 重点来了,对于对象数组,例如: User[],这种数组,有一个注意点,这个点就是:对于数组内的对象是浅拷贝。

一句话:

  • System.arraycopy 对于数组是深拷贝,对于数组内的对象是浅拷贝。因为操作的传入参数是数组,那么回归本意,效果是深复制。

上面的含义一定要区分清楚! 因为现在网上很多观点是混淆,乱JB写的

来看个例子,下面的代码可以自己去运行验证。已经充分验证了上面我的观点。

public class Test {
    public static void main(String[] args) {

        User[] users=new User[]{
                new User("111"),new User("222"),new User("333")
        };//初始化对象数组
        User[] target=new User[users.length];//新建一个目标对象数组

        System.arraycopy(users, 0, target, 0, users.length); // 复制

        System.out.println("数组地址是否一样:"+(users == target?"浅复制":"深复制"));
        System.out.println("数组内对象地址是否一样:"+(users[0] == target[0]?"浅复制":"深复制"));

        target[0].setEmail("444");
        System.out.println("修改后输出 users[0] ,是否和 target[0]一样是444,users[0]:"+users[0].getEmail());

        users[0] = null; // -----------------①
        System.out.println("遍历 users");
        for (User user : users){
            System.out.println(user);
        }
        System.out.println("遍历 target");
        for (User user : target){
            System.out.println(user);
        }
        
        users = null;
        if(target == null){
            System.out.println("users = null 后是否 target 也变成了 null,是则证明是浅复制");
        }else{
            System.out.println("users = null 后是否 target 不受任何影响,是则再次证明数组是深复制");
        }
    }
}
class User{
    private String email;
    public User(String email) { this.email = email; }
    public String getEmail() { return email;}
    public void setEmail(String email) { this.email = email; }
    @Override
    public String toString() { return "User [email=" + email+ "]"; }
}

输出的结果如下:

数组地址是否一样:深复制
数组内对象地址是否一样:浅复制
修改后输出 users[0],是否和 target[0]一样是444,users[0]:444
遍历 users
null
User [email=222]
User [email=333]
遍历 target
User [email=444]
User [email=222]
User [email=333]
users = null 后是否 target 不受任何影响,是则再次证明数组是深复制

上面我的例子还留有一个经典的面试点,既是标号 ① 对应的行:

users[0] = null; // -----------------①

上面我们谈到了,数组内的对象是浅复制,那么在上面这行我把 users[0] 下标为0的对象弄为 null 后。后面再遍历输出:

System.out.println("遍历 users");
for (User user : users){
    System.out.println(user);
}
System.out.println("遍历 target");
for (User user : target){
    System.out.println(user);
}

明显地,我们可以容易知道,user[0] 此时输出的肯定是 null。那么 target[0] 呢?浅拷贝的话,target[0] 必然在内存地址和值上面全等于 users[0]

但是从 System.out.println("遍历 target"); 的结果来看。却发现 target 输出的是:

遍历 target
User [email=444]  // 第一个不是 null
User [email=222]
User [email=333]

这是为什么呢?其实这是最为基础的: 对象引用与对象的区别,一名合格,仅仅是合格的 Java 语言使用者,这个得知道。下面我们来谈谈它。

有一个类:

public class Demo {  
    //默认构造方法  
    public Demo{  
    }  
}  

我们用它创建个对象

Demo fuck = new Demo();  

这一条语句,其实包括了四个动作:

  • 右边的“new Demo”,是以Demo类为模板,在堆空间里创建一个Demo对象。
  • 末尾的()意味着,在对象创建后,立即调用Demo类的构造函数,对刚生成的对象进行初始化。
  • 左边的“Demo fuck”创建了一个Demo类引用变量,它存放在栈空间中。也就是用来指向Demo对象的对象引用。
  • “=”操作符使对象引用指向刚创建的那个Demo对象。对象引用的名字叫做 fuck
Demo fuck;//一个对象引用  
fuck = new Demo();//一个对象引用指向一个对象  

一个对象可以被多个对象引用同时引用。它们操作的始终是同一个。

Demo fuck,fuck2;//创建多个对象引用  
fuck = new Demo();  
fuck2 = fuck;  

好了,回答之前的坑, users[0] = new User("111")users[0] = nulltarget[0] = users[0],users[0] = null 只是把栈中的 users[0] 对象引用弄为了 null,对象 new User("111") 与它无关,自然没影响,target[0] 是另外一个对象引用,也是指向了 new User("111")。

根据 大 Jvm 的 内存回收算法之根搜索,引用链存在、强引用、when 当前应用内存不够了,强制抛出 OOM。那么当 target[0] = nullnew User("111") 对应的这块内存就会进入被回收的队列中,“死去”。

最后这段是不是有点看不懂 ?那证明你要继续努力了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏C语言及其他语言

【编程经验】关于数组指针与指针数组的解释

啦啦啦啦,小编又来了呢,今天给大家讲讲数组指针与指针数组,依旧废话不多说,直接步入正题。 关于数组指针和 指针数组,相信狠很多同学对此疑惑过,今...

2615
来自专栏林冠宏的技术文章

由 System.arraycopy 引发的巩固:对象引用 与 对象 的区别

首先明确一点,System.arraycopy 操作的是数组,效果是深复制。 是不是觉得怎么和你印象的中不一样? 重点来了,对于对象数组,例如: User[]...

1044
来自专栏liulun

Nim教程【十】

openarray类型 注意:openarray类型只能用于参数 固定大小的数组虽然性能不错,但过于呆板,使用取来不是很方便 对于一个方法来说,传入参数如果是一...

2428
来自专栏程序员互动联盟

【编程基础】Java Comparator接口的使用

在实际编程中我们经常会用到集合或者数组,有的时候你需要对这个集合中的元素就行排序,那这个时候就用到了Comparator接口,先看一下接口的原型: public...

3289
来自专栏机器学习从入门到成神

关于Java中==与equals的解析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_35512245/articl...

540
来自专栏Python小屋

1000道Python题库系列分享四(40道)

热烈庆祝2018年2月董付国老师《Python程序设计(第2版)》出版18个月第5次印刷,《Python可以这样学》出版12个月第5次印刷,系列教材《Pytho...

6407
来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础(01)-15总结对象数组,集合Collection,集合List

1:对象数组(掌握) (1)数组既可以存储基本数据类型,也可以存储引用类型。它存储引用类型的时候的数组就叫对象数组。 (2)案例: 用数组存储5个学生对象...

4006
来自专栏Java帮帮-微信公众号-技术文章全总结

13(01)总结StringBuffer,StringBuilder,数组高级,Arrays,Integer,Character

1:StringBuffer(掌握) (1)用字符串做拼接,比较耗时并且也耗内存,而这种拼接操作又是比较常见的,为了解决这个问题,Java就提供了 一...

3665
来自专栏IT可乐

Java关键字(四)——final

  对于Java中的 final 关键字,我们首先可以从字面意思上去理解,百度翻译显示如下:

713
来自专栏深度学习思考者

Python学习(一)函数定义、使用与嵌套

一.函数的定义 Python编程中对于某些需要重复调用的程序,可以使用函数进行定义,基本形式为: def 函数名(参数1, 参数2, ……, 参数N): 其代码...

1777

扫码关注云+社区