Java 值傳遞、引用傳遞 以及clone

從一個問題說起:有一個產品列表界面,用戶可以選中想要的產品,對於選中的產品進行高亮顯示。adapter 中有兩個數據集集合list,一個是全部數據,一個是選中的數據(默認全選中),當我在構建這個adapter的時候,把這個集合也初始化了,當時代碼是這樣寫的?

 public KpNewUserProductsAdapter(List<KpNewUserProduct> kpNewUserProducts) {
        mKpNewUserProducts = kpNewUserProducts;
        mKpNewUserSelectedProducts = mKpNewUserProducts;     
        //mKpNewUserSelectedProducts.addAll(mKpNewUserProducts);
    }

我把全部的數據集合直接賦值給了選中的數據集合,後面用戶可以進行自己想要的產品,這個時候選中的產品list 會變化(因爲用戶可能只選擇了部分產品),這時候就產生了bug,發現總的產品數量會隨着選中的產品list改變?

這時候我想到了java中的賦值操作的含義,重新梳理了java的值傳遞、引用傳遞;還聯想到了clone,這個幾個知識點其實是一致的。下面來一一說解吧。

先說上面提到的問題原因:對象的賦值操作是引用傳遞,它們會指向同一個地址,任意一個改變會引起另一個的變化;

在這裏我顯然是想要一個全部產品list的副本,但賦值操作是不行的,這時候就需要clone。clone區分深淺clone,它們的區別就是被clone的對象其字段是否包含除基本類型之外的對象,如果我們只想要clone 基本類型,則是淺拷貝,如果想要連包含的對象也拷貝就是深拷貝。

參考文章:https://blog.csdn.net/qq_33314107/article/details/80271963

問題:大家有沒有注意到爲什麼 list 集合類在clone 時,其泛型對象爲什麼不需要實現cloneable接口?來,研究一下。

首先集合接口類是沒有繼承cloneable 接口的,但是其實現類都是實現的,比如ArrayList,不信你打開源碼看看。

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

文章參考https://blog.csdn.net/qiumengchen12/article/details/45022919

然後我們在看看它的clone 實現
 

    /**
     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this <tt>ArrayList</tt> instance
     */
    public Object clone() {
        try {
            ArrayList<?> v = (ArrayList<?>) super.clone();
            v.elementData = Arrays.copyOf(elementData, size);
            v.modCount = 0;
            return v;
        } catch (CloneNotSupportedException e) {
            // this shouldn't happen, since we are Cloneable
            throw new InternalError(e);
        }
    }

關鍵代碼是 arrays.copyof(elementData, size) 實現的,而arrays.copyof 最終會調用native的copy 方法,

System.arraycopy(original, 0, copy, 0,
                 Math.min(original.length, newLength));

這個方法其實是數組的複製方法,而ArrayList 的底層數據結構也是數組,我推定這個方法不是內存引用,應該是值的複製,所以arrayList 容器內的類不需要實現cloneable 接口。這就是根本原因,之前我一直以爲在clone arraylist容器時,對容器類的對象也是遍歷調用clone ,顯然不是的。還可以繼續深追System.arraycopy()方法,探究其原理,可以到得

  • 該方法針對數據類型爲基本類型的一維數組進行的是值複製;
  • 二維數組或數據爲引用類型的一維數據來說,進行的是引用複製

參考文章:https://blog.csdn.net/a734797702/article/details/7604847

值傳遞 引用傳遞

傳遞:指的是方法進行參數傳遞。見名知意,方法的形參接受到的是實參的值的拷貝(值)還是地址(引用)來區分。值傳遞一般針對基本類型,對象是引用傳遞。當然引用傳遞會影響之前的值,其效率高;值傳遞和之前的參數沒有關係,效率低一點。

明白這個了,我們就要注意在方法的參數傳遞中,應該避免使用對象,因爲方法裏的行爲可能改變之前的值,一般這是不願我們看到的,所以方法一般都是傳遞基本類型。eg:

public class Person {

    public String mString;

    public Person(String string) {
        mString = string;
    }
}

private void change(Person person){
        person.mString = "changeTo123";
}


測試

Test test = new Test();
        Person person = new Person("person");
        System.out.println("person String1:" + person.mString);
        test.change(person);
        System.out.println("person String2:" + person.mString);

打印
person String1:persion
person String2:changeTo123

值傳遞 引用傳遞其實就是對應的clone 深淺拷貝,兩個話題可以聯繫到一起,這樣理解更到位,更深刻,好了。明白了這個,有時候會解決你難以解決的bug。


參考資料:
clone:https://blog.csdn.net/qq_33314107/article/details/80271963 
值傳遞引用傳遞 https://blog.csdn.net/Norte_L/article/details/80250057

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章