1. 程式人生 > >JAVA中物件的賦值與引用

JAVA中物件的賦值與引用

前言

最近在學習紅黑樹,當我嘗試不使用遞迴來實現時,發現自己的大腦陷入了混亂。

究其原因,是對JAVA中的基本型別和引用型別有所誤解。

特地重新搜尋+實踐一番,漲個姿勢。

一番折騰

先找個物件過年

class Node {
        int value;
        Node left;
        Node right;
        Node(int value) {
            this.value = value;
        }
    }

物件賦值,引用還是複製值?

我出現的問題就在於弄混了下面的程式碼,沒有理解清楚傳值和傳址。

        Node root;
        //初始化
        root = new Node(8);
        root.left = new Node(6);
        root.right = new Node(10);
        //我弄混的地方
        root = root.left;
        root.left = new Node(6);

經過初始化後,如圖所示

LO1

然後我理解錯誤的地方出現了,就是這句

root = root.left;

我的理解出現了偏差,執行後,請看下圖

LO2

為啥呢?

LO3

發現,new出來的物件被存的都是地址,而地址指向實際的物件。

也就是說,物件是引用型別。

再看對已有的物件用“=”賦值

LO4

現在,可以確認了,JAVA中物件賦值是傳遞地址的!

真·引用型別。

之前,我一直以為是傳值。

如果將物件的地址想象成房號,變數root原本在463號。

root = root.left;

按我之前的理解,這行程式碼是告訴root把463號的物品都扔了。
再按照465號房間裡的物品,買一份一模一樣的放在463號。

實際呢

是告訴root,搬到465號去。
463號不再是它的房間了。

區別就在於

如果原本還住463號,只是複製的物品,那麼它跟464號還是鄰居,還可以互相串門。

但若搬到465號就不再與464號相鄰,沒辦法串門到464號去,只能剩下465號一個房間。

小結

對物件使用“=”賦值,賦的是地址而不是值。

這麼基礎的問題,赤果果暴露了我的基礎水平- -

還是某領導說得好啊,“實踐是檢驗真理的唯一標準”。
僅僅聽說是不夠的,還需要親身實踐。

如果是方法之間的引數呢?

上面我們說到物件之間賦值,都是地址。

那麼如果把物件當作引數傳進方法裡,也是傳物件的地址嗎?

答案是,是的。

不過此處有個坑,請看以下程式碼

public class TestObject {
    public static void main(String[] args) {
        TestObject o = new TestObject();
        int baseInt = 1;
        Integer objInt = 1;
        System.out.println("baseInt: " + baseInt);
        System.out.println("objInt: " + objInt);
        o.changeInt(baseInt);
        o.changeInteger(objInt);
        System.out.println("baseInt: " + baseInt);
        System.out.println("objInt: " + objInt);
    }

    public void changeInt(int baseInt) {
        baseInt = 3;
    }

    public void changeInteger(Integer objInt) {
        objInt = 3;
    }
}

結果:

baseInt: 1
objInt: 1

baseInt: 1
objInt: 1

不科學啊。如果傳的是地址進去,為什麼objInt沒有改變呢!
跟說好的不一樣是不是?
來看看到底發生了什麼

LO5

可以看到objInt確實是引用型別的物件,其地址為@464。
再看看進入到方法時,地址是什麼。

LO6

還是@464,說明的確按照地址傳去的。

但是執行完賦值語句後

LO7

地址變成了@468。。

我的猜測

objInt = 3;

的操作是

objInt = new Integer(3);

由於new關鍵字導致objInt改變了引用,從@464變成了@468。
所以改的不是@464裡的值。

執行完該方法後,回到主方法裡,地址變回了@464,也就看到了結果沒有發生改變。

那麼只要不建立新物件,改變是會生效的。

請看如下程式碼:

public class TestObject {
    public static void main(String[] args) {
        TestObject o = new TestObject();
        int baseInt = 1;
        Integer objInt = 1;
        MyInt myInt = new MyInt(1);
        System.out.println("baseInt: " + baseInt);
        System.out.println("objInt: " + objInt);
        System.out.println("myInt: " + myInt.v);
        o.changeInt(baseInt);
        o.changeInteger(objInt);
        o.changeMyInt(myInt);
        System.out.println("改變後....");
        System.out.println("baseInt: " + baseInt);
        System.out.println("objInt: " + objInt);
        System.out.println("myInt: " + myInt.v);
    }

    public void changeInt(int baseInt) {
        baseInt = 3;
    }

    public void changeInteger(Integer objInt) {
        objInt = 3;
    }

    public void changeMyInt(MyInt myInt) {
        myInt.setV(3);
    }
}
class MyInt {
    int v;
    MyInt(int v) {
        this.v = v;
    }

    public void setV(int v) {
        this.v = v;
    }
}

結果:

baseInt: 1
objInt: 1
myInt: 1
改變後....
baseInt: 1
objInt: 1
myInt: 3

生效了。
說明傳進方法裡的引數,只要不用new建立新物件,引用的就是傳進來的物件。

總結

物件之間使用=號進行賦值時,賦的是地址的值,擁有該地址的都是指向該地址對應的物件。
即多對一的關係。

如果用了new關鍵字,一定會有新物件產生,有新物件也肯定有新地址。

至於Integer這些包裝類,我猜它們的=號就是new新物件,同時其沒有提供對外的方法更改物件裡的值,所以可以看作基本型別。

我算是明白了一丟丟,繼續寫紅黑去- -

END