由 System.arraycopy 引起的鞏固:對象引用 與 對象 的區別

做者:林冠宏 / 指尖下的幽靈java

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

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

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

聯繫方式 / Contact:913337456@qq.com算法


瞭解這些術語:數組

  • 深複製又稱深拷貝,兩個變量的內存地址不同,各自修改不影響對方。
  • 淺複製又稱淺拷貝,兩個變量的內存地址同樣,既是同一個變量,僅僅是引用不一樣罷了,各自修改是會影響對方的,由於自己就是同一個。

這篇文文我要講的有:

  • System.arraycopy 是深複製
  • System.arraycopy 的陷阱點
  • 對象引用對象 的區別
  • 簡歷不要寫 精通java,寫 熟練

首先明確一點,System.arraycopy 操做的是數組,效果是深複製。 是否是以爲怎麼和你印象的中不同?
重點來了,對於對象數組,例如: User[],這種數組,有一個注意點,這個點就是:對於數組內的對象是淺拷貝。ide

一句話:函數

  • System.arraycopy 對於數組是深拷貝,對於數組內的對象是淺拷貝。由於操做的傳入參數是數組,那麼迴歸本意,效果是深複製。

上面的含義必定要區分清楚! 由於如今網上不少觀點是混淆,亂JB寫的this

來看個例子,下面的代碼能夠本身去運行驗證。已經充分驗證了上面個人觀點。code

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") 對應的這塊內存就會進入被回收的隊列中,「死去」。

最後這段是否是有點看不懂 ?那證實你要繼續努力了。

相關文章
相關標籤/搜索