Java對象引用傳遞探索


一直認為自己對對象傳遞理解的頗為深刻,沒想到最近一次的編碼中,就犯下了這樣的錯誤,令自己排查了很久才找到問題的根源, 輔以小case記錄以自省。

代碼如下:

public class ObjReference {
    String name = "ObjectReference";
    String id = UUID.randomUUID().toString();
    
    public ObjReference(){}
    
    public ObjReference(String name, String id){
        this.name = name;
        this.id = id;
    }
    
    public String toSelfAttr(){
        return "name = " + name + ", id = " + id;
    }
    
    public void fillSelf(ObjReference obj){
        /*System.out.println("old address: " + obj);*/
        obj = cloneSelf();
        /*System.out.println("after clone,it's address: " + obj);*/
    }
    
    public ObjReference cloneSelf(){
        ObjReference obj = new ObjReference(
                "cloneSelf",UUID.randomUUID().toString());
        /*System.out.println("clone ObjReference's address: " + obj.toString()
                + ", and its selfAttr: " + obj.toSelfAttr());*/
        return obj;        
    }
    
    public static void main(String[]  args){
        ObjReference obj = new ObjReference();
        System.out.println("old ObjReference's address: " + obj.toString()
                + ", and its selfAttr: " + obj.toSelfAttr());
        obj.fillSelf(obj);
        System.out.println("after filled, ObjReference's address: " + obj.toString()
                + ", and its selfAttr: " + obj.toSelfAttr());
    }
    
}

各位看官,運行結果會是如何? fillSelf()之后,對象本身屬性改變是否會生效?  來看運行結果:

old ObjReference's address: com.chq.study.ObjReference@bb494b, and its selfAttr: name = ObjectReference, id = 91f17723-9227-461e-878e-51f7a3eedb0f
after filled, ObjReference's address: com.chq.study.ObjReference@bb494b, and its selfAttr: name = ObjectReference, id = 91f17723-9227-461e-878e-51f7a3eedb0f

我們會發現,對象地址沒有改變(這個好理解,對象是按引用傳遞的),但出乎我預料的,對象屬性也沒有任何變化.... why?

放開fillSelf() & cloneSelf()的注釋, 再次運行下,看看之間發生了什么。

old ObjReference's address: com.chq.study.ObjReference@1636e4e, and its selfAttr: name = ObjectReference, id = c10f9c98-8f15-4343-85db-7a85e21b22d7
old address: com.chq.study.ObjReference@1636e4e clone ObjReference's address: com.chq.study.ObjReference@df0438, and its selfAttr: name = cloneSelf, id = eb117f7a-3463-4371-b723-4f43a041018d after clone,it's address: com.chq.study.ObjReference@df0438
after filled, ObjReference's address: com.chq.study.ObjReference@1636e4e, and its selfAttr: name = ObjectReference, id = c10f9c98-8f15-4343-85db-7a85e21b22d7

橘黃色背景的,說明了最終結果沒有變化。 青色背景的,說明對象在fill過程中,實際是有變化的,不僅是對象屬性,其address也是發生了變化的。

既然address都已經變化了,那為何最終結果並沒有體現出這種變化呢?這個說明了什么? 

大家都知道的:對象傳參時,是按引用傳的,這個引用,指的是指向內存堆heap中實際對象的地址,所有對此對象的改變,實際是發生在heap中的那個實際對象體塊上。

可這個解釋不了示例中的現象,因為對象地址也是改變了的,雖然new了新對象,但我們確實將新對象的address返回並覆蓋原對象地址了,那為何沒有得到預期的結果?

大家未必知道的:對象引用傳遞時,對象引用本身是按值(by-value)傳遞的,是保存在thread stack上的,即copy了一份出來進行傳遞的,如同基本類型的傳遞。

所以雖然我們明確改變了對象引用指向的heap地址,以及傳遞對象本身的地址(是對象本身地址的copy,如同指針),但實際對象本身地址並未改變,所以最終結果不會有變化。

這同時也是我所犯下的錯誤。所以如果想使用類似此種實現,有兩種辦法:

1、原對象不要先指向任何對象(無論new還是null),僅聲明並直接指向待構造新對象的方法即可(如: ObjReference obj2 = test.cloneSelf())  

2、改變對象的方法中,還使用原來對象,不要new新的對象出來(確保對象引用本身沒有變化,變化的僅是heap中的)

我們可以在main中屏蔽掉之前的代碼,增加如下代碼,進行方式1的驗證:

    public static void main(String[]  args){
        /*ObjReference obj = new ObjReference();
        System.out.println("old ObjReference's address: " + obj.toString()
                + ", and its selfAttr: " + obj.toSelfAttr());
        obj.fillSelf(obj);
        System.out.println("after filled, ObjReference's address: " + obj.toString()
                + ", and its selfAttr: " + obj.toSelfAttr());*/
        ObjReference test = new ObjReference();
        ObjReference obj1 = null;
        test.fillSelf(obj1);
        System.out.println(null == obj1 ? "obj1 is null." : 
                "obj1 is : " + obj1.toSelfAttr());
        ObjReference obj2 = test.cloneSelf();
        System.out.println("obj2 is : " + obj2.toSelfAttr());
    }

運行結果:

old address: null
clone ObjReference's address: com.chq.study.ObjReference@18e261d, and its selfAttr: name = cloneSelf, id = 37be891f-127c-4b70-a992-fa842d79ca2e
after clone,it's address: com.chq.study.ObjReference@18e261d
obj1 is null.
clone ObjReference's address: com.chq.study.ObjReference@1684706, and its selfAttr: name = cloneSelf, id = efc60431-d20a-4614-83ff-d3eaa018c41c
obj2 is : name = cloneSelf, id = efc60431-d20a-4614-83ff-d3eaa018c41c

 我的一個疑問,盼高人指點: java中有可以查看對象引用本身地址(引用本身的指針)的方法或者工具么? 如有,可對此做更加強有力的支撐驗證。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM