淺談Java中真的只有值傳遞么
回顧值傳遞和引用傳遞
關(guān)于Java是值傳遞還是引用傳遞,網(wǎng)上有不一樣的說(shuō)法。
1、基本類(lèi)型或基本類(lèi)型的包裝類(lèi)以及String是值傳遞,引用類(lèi)型是引用傳遞。
2、Java中只有值傳遞。
關(guān)于這個(gè)問(wèn)題應(yīng)該是存在爭(zhēng)議的。根據(jù)測(cè)試出來(lái)的結(jié)果和我們自己的經(jīng)驗(yàn),以及口口相傳或是上學(xué)時(shí)老師講的,我們認(rèn)為是第一種。但第二種說(shuō)法的呼聲也很高,漸漸地我們也認(rèn)為第2中才是對(duì)的。那么下面我們就來(lái)分析一下這個(gè)問(wèn)題。
在談這個(gè)問(wèn)題之前我們先了解值傳遞和引用傳遞的概念及現(xiàn)象。我還記得,值傳遞和引用傳遞這些概念是大學(xué)里學(xué)Java的時(shí)候老師教給我的,它們的概念是什么呢?老師是通過(guò)例子來(lái)講解的,大概是這樣的。
值傳遞
例子1:
public static void main(String[] args){
TestJavaParamPass() tjpp = new TestJavaParamPass();
int num = 10;
tjpp.change(num);
System.out.println("num in main():"+num);
}
public void change(int param){
param = 20;
System.out.println("param in change():"+param);
}
控制臺(tái)輸出:
param in change():20
num in main():10
mian()方法中的int變量num傳遞給change()方法,change()方法接收到后將值改變?yōu)?0。通過(guò)看控制臺(tái)輸出,main()方法中的num變量的值沒(méi)有改變。
結(jié)論:基本類(lèi)型是值傳遞。
引用傳遞
例子2:
public static void main(String[] args){
TestJavaParamPass() tjpp = new TestJavaParamPass();
User user = new User();
user.setName("Jerry");
tjpp.change(user);
System.out.println("user in mian():"+user);
}
public void change(User param){
param.setName("Tom");
System.out.println("param in change():"+param);
}
控制臺(tái)輸出:
param in change():User(name=Tom}
user in mian():User(name=Tom}
main()方法中的user變量傳遞給change()方法,change()方法改變了其name屬性值。通過(guò)看控制臺(tái)輸出,main()方法中的user變量的name屬性值發(fā)生改變。
結(jié)論:引用類(lèi)型是引用傳遞。
特殊的值傳遞
例子3:
public static void main(String[] args){
TestJavaParamPass() tjpp = new TestJavaParamPass();
String name = "Jerry";
tjpp.change(name);
System.out.println("name in main():"+name);
}
public void change(String param){
param = "Tom";
System.out.println("param in change():"+param);
}
控制臺(tái)輸出:
param in change():Tom
name in mian():Jerry
String也是引用類(lèi)型的數(shù)據(jù)類(lèi)型,為什么值沒(méi)改變?因?yàn)樵赾hange()方法里param = "Tom";相當(dāng)于param = new String("Tom");就相當(dāng)于param被重新賦值指向了另外一個(gè)對(duì)象。所以,其實(shí)String類(lèi)型傳的是引用,只不過(guò)被重新賦值指向了別的對(duì)象了,沒(méi)有修改原對(duì)象。即,String本質(zhì)上還是引用傳遞,表像上是值傳遞。
結(jié)論:基本類(lèi)型是值傳遞,引用類(lèi)型是引用傳遞,String是特殊的值傳遞。
這個(gè)結(jié)論也是網(wǎng)絡(luò)上流傳的比較多的,可能大部分程序員的認(rèn)知都是這樣的。至于值傳遞和引用傳遞的概念,接下來(lái)便可根據(jù)上面的例子和結(jié)論推斷出來(lái),以及解釋為什么大多數(shù)程序員都將String理解為是特殊的值傳遞。
概念提取
與其叫概念提取好不如叫結(jié)論總結(jié)呢。
值傳遞:基本類(lèi)型的變量在被傳遞給方法時(shí),傳遞的是該變量的值(即復(fù)制自己的值傳遞給方法)。
引用傳遞:引用類(lèi)型的變量在被傳遞給方法時(shí), 傳遞的是該變量的引用(即自己所指向的內(nèi)存地址)。
為什么說(shuō)String是特殊的值傳遞:是因?yàn)镾tring和基本類(lèi)型從表象來(lái)說(shuō)表現(xiàn)出來(lái)的結(jié)果是一樣,大概是為了便于記憶這個(gè)結(jié)果才這樣說(shuō)的吧。但是要知道String也是引用傳遞只不過(guò)它的引用被重新賦值,指向了別的對(duì)象了,所以不會(huì)影響原值。所以String不能簡(jiǎn)單的說(shuō)是值傳遞。
解析Java只有值傳遞的說(shuō)法
只有值傳遞的說(shuō)法
網(wǎng)上還流傳一種說(shuō)法叫Java只有值傳遞。網(wǎng)上有文章論證了Java只有值傳遞的說(shuō)法,其中舉的例子和上面的類(lèi)似。
分析的很透徹,解釋了上面三個(gè)例子的本質(zhì)。指出了上面第二個(gè)例子的錯(cuò)誤之處,舉的例子不恰當(dāng)。并指出下面這樣的例子才恰當(dāng),又舉了鑰匙和房子的例子,佐證了上面第2個(gè)例子確實(shí)不恰當(dāng)。因?yàn)樯厦娴睦拥膫?cè)重點(diǎn)都是最后實(shí)際變量的值有沒(méi)有改變。
public static void main(String[] args){
TestJavaParamPass() tjpp = new TestJavaParamPass();
User user = new User();
user.setName("Jerry");
tjpp.change(user);
System.out.println("user in mian():"+user);
}
public void change(User param){
param = new User()
param.setName("Tom");
System.out.println("param in change():"+param);
}
輸出:
param in change():User(name=Tom}
user in mian():User(name=Jerry}
最后文章的結(jié)論是Java只有值傳遞。引用類(lèi)型大概是這樣解釋的( 基本類(lèi)型就不用說(shuō)了 ),實(shí)際變量(實(shí)際參數(shù))賦值一份自己的引用地址的值傳給方法,方法的形式參數(shù)拿到的是實(shí)參的引用地址的值。側(cè)重點(diǎn)在值,所以結(jié)論說(shuō)的是引用類(lèi)型也是值傳遞。
解析
我覺(jué)得論證者分析基本類(lèi)型和引用類(lèi)型的實(shí)參形參的變化的原理是沒(méi)有問(wèn)題的,但是得出的結(jié)論是不是有點(diǎn)不恰當(dāng)。怎么說(shuō)呢?請(qǐng)繼續(xù)看。
論證者的意思是,java只有值傳遞。也就是說(shuō)引用類(lèi)型也是值傳遞,側(cè)重點(diǎn)是復(fù)制一份引用的地址的值給形參,在于這里的值是引用的地址的值(不是引用所指向的內(nèi)存里存的值),所以說(shuō)是值傳遞。是不是有點(diǎn)牽強(qiáng)?我覺(jué)得有點(diǎn)偷換概念,沒(méi)錯(cuò),大家都知道引用類(lèi)型傳遞的是引用的值,但你不能因?yàn)閭鬟f的是值就說(shuō)是值傳遞,不傳值還能傳什么?引用是內(nèi)存地址,不是也得用值表示么?
而傳統(tǒng)的說(shuō)法:基本類(lèi)型是值傳遞(內(nèi)存里存東西所代表的值),引用類(lèi)型是引用傳遞。我覺(jué)得這個(gè)側(cè)重點(diǎn)是:基本類(lèi)型把值復(fù)制一份傳遞過(guò)去,引用類(lèi)型把引用復(fù)制一份傳遞過(guò)去。側(cè)重點(diǎn)是傳遞的東西,基本類(lèi)型傳遞的東西叫變量的值(變量本身所代表的值),引用類(lèi)型傳遞的東西叫引用(引用本身的值,即內(nèi)存地址),而非引用所指向的內(nèi)存空間內(nèi)的值。所以這樣理解引用類(lèi)型傳遞的是引用也沒(méi)問(wèn)題啊。
所以,Java中基本類(lèi)型傳遞的是變量所代表的自身的值(內(nèi)存里存東西所代表的值),是值傳遞;引用類(lèi)型傳遞的是對(duì)象的引用,是引用傳遞;再深一步,引用也是一個(gè)確切的值來(lái)表示的,或者你把引用看作是對(duì)象的值,那也可以說(shuō)引用類(lèi)型傳遞的是對(duì)象的值,是值傳遞。
文章還說(shuō)了
無(wú)論是值傳遞還是引用傳遞,其實(shí)都是一種求值策略(Evaluation strategy)。在求值策略中,還有一種叫做按共享傳遞(call by sharing)。其實(shí)Java中的參數(shù)傳遞嚴(yán)格意義上說(shuō)應(yīng)該是按共享傳遞。
按共享傳遞,是指在調(diào)用函數(shù)時(shí),傳遞給函數(shù)的是實(shí)參的地址的拷貝(如果實(shí)參在棧中,則直接拷貝該值)。在函數(shù)內(nèi)部對(duì)參數(shù)進(jìn)行操作時(shí),需要先拷貝的地址尋找到具體的值,再進(jìn)行操作。如果該值在棧中,那么因?yàn)槭侵苯涌截惖闹?,所以函?shù)內(nèi)部對(duì)參數(shù)進(jìn)行操作不會(huì)對(duì)外部變量產(chǎn)生影響。如果原來(lái)拷貝的是原值在堆中的地址,那么需要先根據(jù)該地址找到堆中對(duì)應(yīng)的位置,再進(jìn)行操作。因?yàn)閭鬟f的是地址的拷貝所以函數(shù)內(nèi)對(duì)值的操作對(duì)外部變量是可見(jiàn)的。
簡(jiǎn)單點(diǎn)說(shuō),Java中的傳遞,是值傳遞,而這個(gè)值,實(shí)際上是對(duì)象的引用。
這里的意思是,不論是基本類(lèi)型還是引用類(lèi)型傳給函數(shù)的是實(shí)參的地址拷貝,也就是內(nèi)存地址,可以說(shuō)是引用,只不過(guò)基本類(lèi)型在棧中,函數(shù)內(nèi)對(duì)參數(shù)操作時(shí)直接拷貝的值,引用類(lèi)型的值在堆中,需要先找到它的位置,即地址、引用。最后說(shuō)java是值傳遞,而這個(gè)值是對(duì)象的引用。
看到這明白了么?
地址就是引用,那是不是可以說(shuō)java是引用傳遞了?傳遞的是引用的值,計(jì)算機(jī)中不全是值嗎,不是值還能是什么,說(shuō)是引用傳遞是側(cè)重點(diǎn)不同傳,傳過(guò)去的就是地址就是引用,引用不用值表示用啥
這里說(shuō)的值不是一個(gè)概念,說(shuō)基本類(lèi)型傳的是值,這個(gè)是值變量本身的值,說(shuō)對(duì)象傳的也是值,這個(gè)值說(shuō)的是引用是地址,而說(shuō)對(duì)象說(shuō)是引用傳遞,側(cè)重點(diǎn)在于說(shuō)是傳的地址,指向?qū)ο笏淼膬?nèi)部的屬性的地址,非對(duì)象所表示的內(nèi)部的屬性的值,為的是和基本類(lèi)型直接傳值區(qū)分開(kāi)。
維基百科:引用 (程序設(shè)計(jì))
在計(jì)算機(jī)科學(xué)中,引用(英語(yǔ):reference)是指一個(gè)可以讓程序間接訪問(wèn)于電腦存儲(chǔ)器或其他存儲(chǔ)設(shè)備中一特定數(shù)據(jù)的值,該數(shù)據(jù)可以為變量或記錄。
引用和數(shù)據(jù)本身不同。一般而言,引用會(huì)是數(shù)據(jù)存儲(chǔ)于存儲(chǔ)器或存儲(chǔ)設(shè)備中的物理地址。因此,引用亦常被稱(chēng)為該數(shù)據(jù)的指針或地址。
看看引用的定義,引用是指一個(gè)XXX數(shù)據(jù)的值。好吧,引用本身就是一個(gè)值。但不是值還能是什么呢?計(jì)算機(jī)中不都是值么?
說(shuō)值傳遞還是引用傳遞都沒(méi)有錯(cuò),關(guān)鍵是你怎么定義和解釋值傳遞、引用傳遞的概念以及值所表示的東西。
計(jì)算機(jī)中一切皆值,如果從這點(diǎn)出發(fā),那全都是傳的值啊,只不過(guò)細(xì)化到j(luò)ava中,基本類(lèi)型傳遞的是自身的值,引用類(lèi)型傳遞的是引用的值,而非對(duì)象內(nèi)屬性的值。
所以如果武斷的說(shuō)只有值傳遞也是沒(méi)問(wèn)題的,因?yàn)樵谟?jì)算機(jī)中只能用值來(lái)表示啊,但覺(jué)得有點(diǎn)投機(jī)取巧,就和說(shuō)世界上只有***,那還區(qū)分**和**干嘛,道理差不多。(暫時(shí)想不到好的例子哈哈)
還是剛才說(shuō)的那句,說(shuō)是引用傳遞,側(cè)重點(diǎn)在于說(shuō)是傳的是引用是地址,而非對(duì)象所表示的內(nèi)部的屬性值,為的是和基本類(lèi)型直接傳值區(qū)分開(kāi),便于記憶.
最后
最后,大家理解現(xiàn)象的原理即可,沒(méi)必要追的那么深,或玩文字游戲,鉆牛角尖。
如果有人問(wèn)你,你可以這么說(shuō),基本類(lèi)型和他們的包裝類(lèi)是值傳遞,引用類(lèi)型傳遞的是對(duì)象的引用即地址值,String傳遞的也是地址值,只不過(guò)在函數(shù)內(nèi)地址值被修改了,所以不會(huì)影響到實(shí)參,因表現(xiàn)上和基本類(lèi)型一樣,所以可能為了便于記住這個(gè)現(xiàn)象才說(shuō)String是值傳遞。歸根到底傳的都是值只不過(guò)值的含義不同。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Mybatis中Collection集合標(biāo)簽的使用詳解
這篇文章主要介紹了Mybatis中Collection集合標(biāo)簽的使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
java實(shí)現(xiàn)獲取安卓設(shè)備里已安裝的軟件包
本文給大家介紹的是如何獲取設(shè)備中已經(jīng)安裝的應(yīng)用軟件包的代碼,其核心方法原理很簡(jiǎn)單,我們通過(guò)Android中提供的PackageManager類(lèi),來(lái)獲取手機(jī)中安裝的應(yīng)用程序信息2015-10-10
SpringBoot整合Echarts實(shí)現(xiàn)用戶(hù)人數(shù)和性別展示功能(詳細(xì)步驟)
這篇文章主要介紹了SpringBoot整合Echarts實(shí)現(xiàn)用戶(hù)人數(shù)和性別展示,通過(guò)數(shù)據(jù)庫(kù)設(shè)計(jì)、實(shí)現(xiàn)數(shù)據(jù)訪問(wèn)層、業(yè)務(wù)邏輯層和控制層的代碼編寫(xiě),以及前端頁(yè)面的開(kāi)發(fā),本文詳細(xì)地介紹了SpringBoot整合Echarts的實(shí)現(xiàn)步驟和代碼,需要的朋友可以參考下2023-05-05
Java實(shí)現(xiàn)簡(jiǎn)單的酒店管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)酒店管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07
Java網(wǎng)絡(luò)編程之IO模型阻塞與非阻塞簡(jiǎn)要分析
這篇文章主要介紹Java網(wǎng)絡(luò)編程中的IO模型阻塞與非阻塞簡(jiǎn)要分析,文中附有示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09
Bean實(shí)例化之前修改BeanDefinition示例詳解
這篇文章主要為大家介紹了Bean實(shí)例化之前修改BeanDefinition示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
java在linux本地執(zhí)行shell命令的實(shí)現(xiàn)方法
本文主要介紹了java在linux本地執(zhí)行shell命令的實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
詳解Spring Data Jpa 模糊查詢(xún)的正確用法
本篇文章主要介紹了詳解Spring Data Jpa 模糊查詢(xún)的正確用法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05

