java中引用對比C++指針

前置知識地址:https://blog.csdn.net/wangfei8348/article/details/51383805
重點在後面的引用對比實驗(測試出內存地址,我很開心哈哈哈,客觀給個好評唄~~~)

java對象的聲明和初始化

java中,Object o 等價於C++中的 Obejct &o (改正:Object o等價於 Object* o),o本身是一個引用(其實是指針),在o未被初始化(對o進行賦值)前,o的引用爲空。也就是此時o爲null。進一步講,此時o僅是一個標識符,存在於java棧中,對象Obeject沒有被類加載器進行加載,也就不會有初始化的過程

Object o = new Object()是一個聲明引用並對引用賦值,把對象進行初始化的過程。此時,java類加載器加載Obejct,堆中存在Object的Class對象。

初識Java引用

下面是java引用對象的案例,可見B = A;後,B實質等價於A了。B修改對象的值A也會受到影響。此時A、B指向Java堆內同一個內存空間3594623912

class Goal {
	public int x ;
	public Goal(int x){
		this.x = x;
	}
	public int getX() {
		return x;
	}
	public void setX(int x) {
		this.x = x;
	}
}
public static void testQuote(){
		Goal A ;
		Goal B ;
		A = new Goal(1);
		B = A;
		B.setX(2);
        System.out.println(A.getX());//2
        System.out.println(B.getX());//2
        A.setX(1);
        System.out.println(A.getX());//1
        System.out.println(B.getX());//1
        System.out.println("Addess: " + Addresser.addressOf(A));//3594623912
        System.out.println("Addess: " + Addresser.addressOf(B));//3594623912
	}

~~~~~打個補丁,之前講java引用等同於C++引用是不嚴謹的。

java引用不同於C++引用

對比引用

java中的引用:“每種編程語言都有自己的數據處理方式。有些時候,程序員必須注意將要處理的數據是什麼類型。你是直接操縱元素,還是用某種基於特殊語法的間接表示(例如C/C++裏的指針)來操作對象。所有這些在 Java 裏都得到了簡化,一切都被視爲對象。因此,我們可採用一種統一的語法。儘管將一切都“看作”對象,但操縱的標識符實際是指向一個對象的“引用”(reference)。”這是java編程思想的原話。

c++中的引用:引用是已定義的變量的別名。

而觀察java引用於C++指針的工作流程

對於Java引用:先在棧中存對象的引用,再向堆中申請內存空間來存該類的對象,然後指向對象所在的內存。

對於c++的指針:先在棧中存指針,再向堆中申請內存空間存該指針所指向的元素,然後指向該地址。

發現java引用和C++指針非常相似。

案例分析

觀察下面的代碼1

 public static void testQuote() {
        Goal C = new Goal(3);
        Goal D = new Goal(4);
        swap(C, D);//交換沒有成功
        System.out.println(C.getX());//3
        System.out.println(D.getX());//4
    }
    public static void swap(Goal c, Goal d) {
        Goal temp = c;//temp指向c指向的對象
        c = d;//c指向d指向的對象
        d = temp;//d執行temp指向的對象
		// 但是c、d指向什麼東西與C、D沒有半毛錢關係。
    }

上述代碼發生了什麼呢?如果c、d是C、D的引用(C++,別名),那麼交換應該成功的,但是沒成功啊?
利用工具查看C和D的指向的堆內對象地址,發現他們的引用地址並未發生改變。但是,形參c和d的指向的地址發生改變,指向了與原來不同的對象。(注意,初學C++的同學可能混淆指針的地址改變還是指針指向的地址(即指針的值)發生改變)

    public static void testQuote() throws Exception {
        Goal C = new Goal(3);
        Goal D = new Goal(4);
        System.out.println("Addess: " + Addresser.addressOf(C));//Addess: 3589856168
        System.out.println("Addess: " + Addresser.addressOf(D));//Addess: 3589856184
        swap(C, D);
        System.out.println("Addess: " + Addresser.addressOf(C));//Addess: 3589856168
        System.out.println("Addess: " + Addresser.addressOf(D));//Addess: 3589856184
        System.out.println(C.getX());//3
        System.out.println(D.getX());//4
    }
    //////////////////////////////////////////////////////////////////////////
    public static void swap(Goal c, Goal d) throws Exception {
        System.out.println("---------------------");
        System.out.println("Addess: " + Addresser.addressOf(c));//Addess: 3589855840
        System.out.println("Addess: " + Addresser.addressOf(d));//Addess: 3589855856
        System.out.println(c.getX());//3
        System.out.println(d.getX());//4
        Goal temp = c;//temp指向c指向的對象
        c = d;//c指向d指向的對象
        d = temp;//d指向temp指向的對象
        System.out.println("Addess: " + Addresser.addressOf(c));//Addess: 3589855856,c指向的地址發生改變,所以引用的對象也發生了改變。
        System.out.println("Addess: " + Addresser.addressOf(d));//Addess: 3589855840,d指向的地址發生改變,所以引用的對象也發生了改變。
        System.out.println(c.getX());//4
        System.out.println(d.getX());//3
        System.out.println("---------------------");
    }

在這裏插入圖片描述

由結果可以看到,swap()方法體內,c、d確實交換了對方的引用的對象。
代碼實質是交換了臨時引用變量c和d的指向的堆內的地址”,c原來指向C即引用C後來引用D,d原來指向D即引用D後來引用C。
準確的講,Java只存在一種傳值方式便是值傳遞,c和d是形參並不能改變實參的值。形參是實參的複製,其引用實參指向的對象,但是其本身的修改不影響實參本身。實參沒有改變,而c、d的引用的地址值確實發生了交換,對應的引用的對象發生了改變證明了這種說法。
在這裏插入圖片描述

進一步探索Java“指針”

這個部分統一將Java引用稱之爲Java指針
其實,上面的交換的代碼等價於C++的這段代碼:

class Goal {
public:
    int x;
    Goal(int x) {
        this->x = x;
    }
    int getX() {
        return x;
    }
    void setX(int x) {
        this->x = x;
    }
};
void swap(Goal* c, Goal* d) {
    cout<<c->getX()<<endl;//3
    cout<<d->getX()<<endl;//4
    Goal* temp = c; //temp指向c指向的對象
    c = d;//c指向d指向的對象
    d = temp;//d指向temp指向的對象
    cout<<c->getX()<<endl;//4
    cout<<d->getX()<<endl;//3
    //但是c、d指向誰和C、D又沒什麼關係,所以C、D不變
}
int main() {
    Goal *C = new Goal(3);
    Goal *D = new Goal(4);
    swap(C, D);
    cout<<C->getX();//3
    cout<<D->getX();//4
    return 0;
}

由代碼結果知,上斷的結果與Java版本結果相同。以指針爲函數參數實現了與Java版本相同的效果。這給我們猜測Java引用是指針留下了依據。同時觀察對比C++的交換兩個數的實現:

void swap(int &a,int &b) //方法1改進 (引用傳遞) &a &b爲實參ab的地址,真交換 
{
	int temp=a;
	a=b;
	b=temp;
}
void swap(int *a,int *b) //方法1改進 (指針傳遞) 用指針,真交換 
{
	int temp;
	temp=*a;
	*a=*b;
	*b=temp;
 } 

依據第一個函數,Java傳入的對象參數是引用這個就不成立了。至少Java傳入對象不同於C++的引用(別名)。而上文我們講過Java實質只存在一種傳參方式即值傳遞。衆所周知,C++中存在三種傳值方式:值傳遞、引用傳遞、地址傳遞(指針)。
在C++中,指針是一種變量,可以作爲參數傳遞
指針最爲重要的作用是通過解引用”實現對變量的間接使用在Java中,函數傳入參數爲對象時,實參指向的對象可以被形參引用,形參通過這種方式也可以實現“間接使用
從這個角度上講,任何對象如果被當作函數參數進行傳遞,那麼其就可以被看作一種“指針“
所以,java引用並非C++中的引用
那麼又有一個問題,C++指針可以通過*實現解引用,Java”指針“怎麼使用呢?
因爲Java中指針式對象,可以直接使用對象的方法,以此實現”解引用“
例如,如果想修改對象的成員的值,可以利用傳入對象(指針)的set方法修改,這種修改並不會隨着函數彈出Java棧而消失,是Java指針實現的”間接使用“的方式,如最開始的例子。

Q:那麼C++指針與Java指針還有什麼不同呢?
A:我的理解是,Java所有對象都可以是指針們就像所有對象都有”鎖“一樣,這一點不同於C++的顯示聲明指針對象。

最後是一個形參指針指向不同對象的例子,由結果知,該形參integer原指向堆內位置爲3594611568的對象,即var2,var1;指向的對象,後來指向了新new出來的Integer(2);對象。
代碼如下:

@Test
	public void test3() throws Exception {
		Integer var1=new Integer(1);
		Integer var2=var1;
		System.out.println("Addess: " + Addresser.addressOf(var1));//3594611568
		System.out.println("Addess: " + Addresser.addressOf(var2));//3594611568
		doSomething(var2);
		System.out.println("Addess: " + Addresser.addressOf(var1));//3594611568
		System.out.println("Addess: " + Addresser.addressOf(var2));//3594611568
		System.out.println(var1.intValue());
		System.out.println(var2.intValue());
		System.out.println(var1==var2);
	}
	public static void doSomething(Integer integer) throws Exception {
		System.out.println("Addess: " + Addresser.addressOf(integer));//3594611568
		integer=new Integer(2);
		System.out.println("Addess: " + Addresser.addressOf(integer));//3594634008
	}

在這裏插入圖片描述
參考:
https://blog.csdn.net/mxd446814583/article/details/79599752 (獲取對象地址的方法)
https://blog.csdn.net/tianwei0822/article/details/78921823 (c++與java區別的理解(一)–引用)
https://www.cnblogs.com/coderising/p/5697986.html java中的值傳遞和引用傳遞問題
https://blog.csdn.net/cout_operator/article/details/82799372
https://blog.csdn.net/hackersuye/article/details/78875119

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