Java中真的只有值傳遞麼?

原文連接

(本文非引戰或diss,只是說出本身的理解,歡迎擺正心態觀看或探討)php

關於Java是值傳遞仍是引用傳遞,網上有不同的說法。java

一、基本類型或基本類型的包裝類以及String是值傳遞,引用類型是引用傳遞。
二、Java中只有值傳遞。程序員

關於這個問題應該是存在爭議的。根據測試出來的結果和咱們本身的經驗,以及口口相傳或是上學時老師講的,咱們認爲是第一種。但第二種說法的呼聲也很高,漸漸地咱們也認爲第2中才是對的。那麼下面咱們就來分析一下這個問題。web

回顧值傳遞和引用傳遞

在談這個問題以前咱們先了解值傳遞和引用傳遞的概念及現象。我還記得,值傳遞和引用傳遞這些概念是大學裏學Java的時候老師教給個人,它們的概念是什麼呢?老師是經過例子來說解的,大概是這樣的。sql

值傳遞

例子1:ruby

 1public static void main(String[] args){
2    TestJavaParamPass() tjpp = new TestJavaParamPass();
3    int num = 10;
4    tjpp.change(num);
5    System.out.println("num in main():"+num);
6}
7public void change(int param){
8    param = 20;
9    System.out.println("param in change():"+param);
10}
複製代碼

控制檯輸出:網絡

1param in change():20
2num in main():10
複製代碼

mian()方法中的int變量num傳遞給change()方法,change()方法接收到後將值改變爲20。經過看控制檯輸出,main()方法中的num變量的值沒有改變。app

結論:基本類型是值傳遞。函數

引用傳遞

例子2:測試

 1public static void main(String[] args){
2    TestJavaParamPass() tjpp = new TestJavaParamPass();
3    User user = new User();
4    user.setName("Jerry");
5    tjpp.change(user);
6    System.out.println("user in mian():"+user);
7}
8public void change(User param){
9    param.setName("Tom");
10    System.out.println("param in change():"+param);
11}
複製代碼

控制檯輸出:

1param in change():User(name=Tom}
2user in mian():User(name=Tom}
複製代碼

main()方法中的user變量傳遞給change()方法,change()方法改變了其name屬性值。經過看控制檯輸出,main()方法中的user變量的name屬性值發生改變。

結論:引用類型是引用傳遞。

特殊的值傳遞

例子3:

 1public static void main(String[] args){
2    TestJavaParamPass() tjpp = new TestJavaParamPass();
3    String name = "Jerry";
4    tjpp.change(name);
5    System.out.println("name in main():"+name);
6}
7public void change(String param){
8    param = "Tom";
9    System.out.println("param in change():"+param);
10}
複製代碼

控制檯輸出:

1param in change():Tom
2name in mian():Jerry
複製代碼

String也是引用類型的數據類型,爲何值沒改變?由於在change()方法裏param = "Tom";至關於param = new String("Tom");就至關於param被從新賦值指向了另一個對象。因此,其實String類型傳的是引用,只不過被從新賦值指向了別的對象了,沒有修改原對象。即,String本質上仍是引用傳遞,表像上是值傳遞。

結論:基本類型是值傳遞,引用類型是引用傳遞,String是特殊的值傳遞。

這個結論也是網絡上流傳的比較多的,可能大部分程序員的認知都是這樣的。至於值傳遞和引用傳遞的概念,接下來即可根據上面的例子和結論推斷出來,以及解釋爲何大多數程序員都將String理解爲是特殊的值傳遞。

概念提取

與其叫概念提取好不如叫結論總結呢。

值傳遞:基本類型的變量在被傳遞給方法時,傳遞的是該變量的值(即複製本身的值傳遞給方法)。

引用傳遞:引用類型的變量在被傳遞給方法時, 傳遞的是該變量的引用(即本身所指向的內存地址)。

爲何說String是特殊的值傳遞:是由於String和基本類型從表象來講表現出來的結果是同樣,大概是爲了便於記憶這個結果才這樣說的吧。可是要知道String也是引用傳遞只不過它的引用被從新賦值,指向了別的對象了,因此不會影響原值。因此String不能簡單的說是值傳遞。

解析Java只有值傳遞的說法

只有值傳遞的說法

網上還流傳一種說法叫Java只有值傳遞。網上有文章論證了Java只有值傳遞的說法,其中舉的例子和上面的相似。

分析的很透徹,解釋了上面三個例子的本質。指出了上面第二個例子的錯誤之處,舉的例子不恰當。並指出下面這樣的例子才恰當,又舉了鑰匙和房子的例子,佐證了上面第2個例子確實不恰當。由於上面的例子的側重點都是最後實際變量的值有沒有改變。

 1public static void main(String[] args){
2    TestJavaParamPass() tjpp = new TestJavaParamPass();
3    User user = new User();
4    user.setName("Jerry");
5    tjpp.change(user);
6    System.out.println("user in mian():"+user);
7}
8public void change(User param){
9    param = new User()
10    param.setName("Tom");
11    System.out.println("param in change():"+param);
12}
複製代碼

輸出:

1param in change():User(name=Tom}
2user in mian():User(name=Jerry}
複製代碼

最後文章的結論是Java只有值傳遞。引用類型大概是這樣解釋的( 基本類型就不用說了 ),實際變量(實際參數)賦值一份本身的引用地址的值傳給方法,方法的形式參數拿到的是實參的引用地址的值。側重點在值,因此結論說的是引用類型也是值傳遞。

解析

我以爲論證者分析基本類型和引用類型的實參形參的變化的原理是沒有問題的,可是得出的結論是否是有點不恰當。怎麼說呢?請繼續看。

論證者的意思是,java只有值傳遞。也就是說引用類型也是值傳遞,側重點是複製一份引用的地址的值給形參,在於這裏的值是引用的地址的值(不是引用所指向的內存裏存的值),因此說是值傳遞。是否是有點牽強?我以爲有點偷換概念,沒錯,你們都知道引用類型傳遞的是引用的值,但你不能由於傳遞的是值就說是值傳遞,不傳值還能傳什麼?引用是內存地址,不是也得用值表示麼?

而傳統的說法:基本類型是值傳遞(內存裏存東西所表明的值),引用類型是引用傳遞。我以爲這個側重點是:基本類型把值複製一份傳遞過去,引用類型把引用複製一份傳遞過去。側重點是傳遞的東西,基本類型傳遞的東西叫變量的值(變量自己所表明的值),引用類型傳遞的東西叫引用(引用自己的值,即內存地址),而非引用所指向的內存空間內的值。因此這樣理解引用類型傳遞的是引用也沒問題啊。

因此,Java中基本類型傳遞的是變量所表明的自身的值(內存裏存東西所表明的值),是值傳遞;引用類型傳遞的是對象的引用,是引用傳遞;再深一步,引用也是一個確切的值來表示的,或者你把引用看做是對象的值,那也能夠說引用類型傳遞的是對象的值,是值傳遞。

文章還說了

不管是值傳遞仍是引用傳遞,其實都是一種求值策略(Evaluation strategy)。在求值策略中,還有一種叫作按共享傳遞(call by sharing)。其實Java中的參數傳遞嚴格意義上說應該是按共享傳遞。

按共享傳遞,是指在調用函數時,傳遞給函數的是實參的地址的拷貝(若是實參在棧中,則直接拷貝該值)。在函數內部對參數進行操做時,須要先拷貝的地址尋找到具體的值,再進行操做。若是該值在棧中,那麼由於是直接拷貝的值,因此函數內部對參數進行操做不會對外部變量產生影響。若是原來拷貝的是原值在堆中的地址,那麼須要先根據該地址找到堆中對應的位置,再進行操做。由於傳遞的是地址的拷貝因此函數內對值的操做對外部變量是可見的。

簡單點說,Java中的傳遞,是值傳遞,而這個值,其實是對象的引用。

這裏的意思是,不管是基本類型仍是引用類型傳給函數的是實參的地址拷貝,也就是內存地址,能夠說是引用,只不過基本類型在棧中,函數內對參數操做時直接拷貝的值,引用類型的值在堆中,須要先找到它的位置,即地址、引用。最後說java是值傳遞,而這個值是對象的引用。

看到這明白了麼?

地址就是引用,那是否是能夠說java是引用傳遞了?傳遞的是引用的值,計算機中不全是值嗎,不是值還能是什麼,說是引用傳遞是側重點不一樣傳,傳過去的就是地址就是引用,引用不用值表示用啥

這裏說的值不是一個概念,說基本類型傳的是值,這個是值變量自己的值,說對象傳的也是值,這個值說的是引用是地址,而說對象說是引用傳遞,側重點在於說是傳的地址,指向對象所表明的內部的屬性的地址,非對象所表示的內部的屬性的值,爲的是和基本類型直接傳值區分開。

維基百科:引用 (程序設計)

計算機科學中,引用(英語:reference)是指一個可讓程序間接訪問於電腦存儲器或其餘存儲設備中一特定數據的,該數據能夠爲變量記錄
引用和數據自己不一樣。通常而言,引用會是數據存儲於存儲器或存儲設備中的物理地址。所以,引用亦常被稱爲該數據的指針地址

看看引用的定義,引用是指一個XXX數據的值。好吧,引用自己就是一個值。但不是值還能是什麼呢?計算機中不都是值麼?

說值傳遞仍是引用傳遞都沒有錯,關鍵是你怎麼定義和解釋值傳遞、引用傳遞的概念以及值所表示的東西。

計算機中一切皆值,若是從這點出發,那全都是傳的值啊,只不過細化到java中,基本類型傳遞的是自身的值,引用類型傳遞的是引用的值,而非對象內屬性的值。

因此若是武斷的說只有值傳遞也是沒問題的,由於在計算機中只能用值來表示啊,但以爲有點投機取巧,就和說世界上只有***,那還區分**和**幹嗎,道理差很少。(暫時想不到好的例子哈哈)

仍是剛纔說的那句,說是引用傳遞,側重點在於說是傳的是引用是地址,而非對象所表示的內部的屬性值,爲的是和基本類型直接傳值區分開,便於記憶.

最後

最後,你們理解現象的原理便可,不必追的那麼深,或玩文字遊戲,鑽牛角尖。

若是有人問你,你能夠這麼說,基本類型和他們的包裝類是值傳遞,引用類型傳遞的是對象的引用即地址值,String傳遞的也是地址值,只不過在函數內地址值被修改了,因此不會影響到實參,因表現上和基本類型同樣,因此可能爲了便於記住這個現象才說String是值傳遞的吧。歸根到底傳的都是值只不過值的含義不一樣,因此纔有Java只有值傳遞的說法吧。

(本文非引戰或diss,只是說出本身的理解,歡迎探討)

相關文章
相關標籤/搜索