1. 程式人生 > >由 System.arraycopy 引發的鞏固:物件引用 與 物件 的區別

由 System.arraycopy 引發的鞏固:物件引用 與 物件 的區別

作者:林冠巨集 / 指尖下的幽靈

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

部落格:http://www.cnblogs.com/linguanh/

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

聯絡方式 / Contact:[email protected]

瞭解這些術語:

  • 深複製又稱深拷貝,兩個變數的記憶體地址不一樣,各自修改不影響對方。
  • 淺複製又稱淺拷貝,兩個變數的記憶體地址一樣,既是同一個變數,僅僅是引用不同罷了,各自修改是會影響對方的,因為本身就是同一個。

這篇文文我要講的有:

  • 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") 對應的這塊記憶體就會進入被回收的佇列中,“死去”。

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

相關推薦

System.arraycopy 引發鞏固物件引用 物件區別

作者:林冠巨集 / 指尖下的幽靈 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 部落格:http://www.cnblogs.com/linguanh/ GitHub : https://github.com/af913337456/

物件引用物件區別

先定義一個簡單的類: public class phone{ int length; int wide; double money; String color; } 如果要建立這個類的一個物件,一般的寫法是 phone p1 = new phon

java--物件引用物件區別

一、前言        在java學習中,理解物件以及物件的引用是萬里長征的第一步。但是,現在大量的java學習書籍都將物件以及物件的引用混為一談,然而實際上它們有著本質的區別。為了幫助更多的java學者更好的理解,我將自己的學習理解記錄下來。如有理解不全或者錯誤的地方,歡

python資料記憶體形式-引用物件的認識

這篇文章主要是對python中的資料進行認識,對於很多初學者來講,其實資料的認識是最重要的,也是最容易出錯的。本文結合資料與記憶體形態講解python中的資料,內容包括: 引用與物件 可變資料型別與不可變資料型別 引用傳遞與值傳遞 深拷貝與淺拷貝

jsp內建物件中pagepageContextel內建物件pageScopepageContext區別

原文地址:jsp內建物件中page與pageContext與el內建物件pageScope與pageContext區別 首先說明一下jsp9大內建物件 (1)HttpSession類的session物件作用:主要用於來分別儲存每個使用者資訊,與請求關聯的會話;      &n

Why Java Sucks and C# Rocks(2)基礎型別物件

既然已經談過這次語言比較的意義與目的,而完整的幻燈片和錄音也已經放出,那麼接下來自然是詳細討論了。在這篇文章中,我會對兩個語言的基本特徵進行簡單描述,並主要討論兩者對於基礎型別的處理方式。在我看來,Java語言對於基礎型別的處理方式,並不如C#中值型別般妥當。如果您有任何覺得不妥或是想要補充的意見,請不吝回覆

js中值的基本型別引用型別,以及物件引用物件的淺拷貝深拷貝

js有兩種型別的值:棧:原始資料型別(undefinen,null,boolead,number,string)堆:引用資料型別(物件,函式和陣列)兩種型別的區別是:儲存位置不同,原始資料型別直接儲存在棧(stack)中的簡單資料段,佔據空間小,大小固定,屬於被頻繁使用的資料,所以放入棧中儲存;引用資料型別儲

解析Java物件引用JVM自動記憶體管理

象引用應用程式設計介面是JDKTM1.2中新定義的。該應用程式設計介面允許應用程式以物件引用的方式與JVM的記憶體管理器進行互動。當應用程式需管理大量記憶體物件或者在新的Java物件建立之前需刪除原有物件時,Java物件引用應用程式設計介面具有相當大的用途,例如:   

【Java】簡單區分物件引用物件

以下純屬個人的理解,如果有不全或者錯誤的地方,歡迎大家批評指正。 初學 Java 的時候,博主很容易將兩者混淆在一起,這裡就做個簡單的區分。 為了方便說明,博主先建立一個 People 類(類是具有相同屬性和行為的一類實體,物件是類的例項化) public class

物件引用copy,deepcopy之間的區別

import numpy as np 賦值(物件引用) a = np.array([1, 2, 3, 4]) a array([1, 2, 3, 4]) b = a b array([1, 2, 3, 4]) a[2] = 0 a

java核心技術卷一(三)物件引用建立,LocalDate的用法

Date now = new Date();當我們使用上面這個語法之時,實際上是實現了兩個過程,第一個是使用new關鍵字建立了一個物件,另外一個過程是將這個建立的物件與變數now關聯到了一起。在java中任何變數的值都是對儲存在另一個地方的一個物件的引用,這一點很關鍵。Sys

物件引用物件賦值

關鍵字: java物件 引用Java物件及其引用關於物件與引用之間的一些基本概念。 初學Java時,在很長一段時間裡,總覺得基本概念很模糊。後來才知道,在許多Java書中,把物件和物件的引用混為一談。可是,如果我分不清物件與物件引用, 那實在沒法很好地理解下面的面向物件技術。把自己的一點認識寫下來,或許能讓初

解惑真小白的苦惱 | 類的物件物件引用物件例項,引用變數

對於剛接觸不久面向物件的真小白童鞋來說,類的物件,物件的例項,物件的引用,引用變數的問題以及莫過於沒得物件~雖然博主也沒得物件~,本文將逐個逐個講解! 1.何謂物件?   在Java中有一句比較流行的話,叫做“萬物皆物件”,這是Java語言設計之初的理念之一。要理解什麼是物件,需要跟類一起結合起來理解。下面這

筆記MyBatis中$#的區別

首先MyBatis中 $與#都是動態傳參 # 將傳入的資料都當成一個字串,會對自動傳入的資料加一個雙引號 $ 將傳入的資料直接顯示生成在sql中 # 佔位符號,能夠很大程度防止sql注入「語句的拼接」 $ sql拼接符號,無法防止Sql注入 如果使用在order by中就需要使用 $

Java 面向物件-類物件

Java面向物件-類與物件   類與物件的關係 我們通俗的舉個例子,比如人類是一種類,張三這個人就是人類的具體的一個個體,也就是java中的物件;這就是一個類與物件的關係;   類的定義 下面看例項   類的建立和使用 package com.xuyiga

引用指標區別

區別: 引用不能為空,當引用被建立時,它必須被初始化;指標可以為空值,可以在任何時候被初始化 一旦一個引用被初始化為指向一個物件,它就不能改變為另一個物件的引用。指標則可以在任何時候指向另一個物件 不可能有NULL引用,必須確保引用是和一塊的合法的儲存單元關聯

面向物件之類物件

面向物件(Object Oriented)是一種新興的程式設計方法,或者是一種新的程式設計規範(paradigm),其基本思想是使用物件、類、繼承、封裝、多型等基本概念來進行程式設計。從現實世界中客觀存在的事物(即物件)出發來構造軟體系統,並且在系統構造中儘可能運用人類的自然思維方式。

函式返回指標,引用指標區別

1,函式不可以返回棧記憶體的指標,但是可以返回堆記憶體的指標,可以free掉兩次指向空的指標,但是不能連續free兩次指向記憶體的指標,會產生已放棄   int main() { int *a; a = (int *)malloc(10); a[0] = 1

面向物件風格物件關聯風格

對相關聯: Foo={ init:function(name){ this.name=name; }, sayName:function(){

面線物件之類物件

一、面向物件: 面向物件是一種程式設計思想,即編寫程式碼的方式方法。 OOP(Object Oriented Programing)將程式看做是一堆物件的集合,實現功能的方式就是物件之間互動來實現。   二、類與物件: 1.面向物件思想中最核心的概念就是:物件與類。 定義: 什麼是物件