java中值傳遞和引用傳遞的差別是什麼
區別:1、值傳遞會建立副本,而引用傳遞不建立副本;2、值傳遞中函數中無法改變原始對象,而引用傳遞中函數中可以改變原始物件。值傳遞是指在呼叫函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數;而引用傳遞是指在呼叫函數時將實際參數的位址直接傳遞到函數中,那麼在函數中對參數所進行的修改,就會影響到實際參數。
本教學操作環境:windows7系統、java8版、DELL G3電腦。
實參與形參
我們都知道,在Java中定義方法的時候是可以定義參數的。例如Java中的main方法, public static void main(String[ ] args),這裡面的args就是參數。參數在程式語言中分為形式參數和實際參數。
形式參數:是在定義函數名稱和函數體的時候使用的參數,目的是用來接收呼叫函數時傳入的參數。
實際參數:在呼叫有參函數時,主調函數和被調函數之間有資料傳遞關係。在主調函數中呼叫函數時,函數名稱後面括號中的參數稱為「實際參數」
#簡單舉個例子:
public static void main( String[ ] args) { ParamTest pt = new ParamTest(); pt.sout( "Hollis");//实际参数为Hollis } public void sout( String name) {/!形式参数为name system.out.println(name); }
實際參數是呼叫有參方法的時候真正傳遞的內容,而形式參數是用來接收實參內容的參數。
值傳遞與引用傳遞
上面提到了,當我們呼叫一個有參函數的時候,會把實際參數傳遞給形式參數。但是,在程式語言中,這個傳遞過程中傳遞的兩種情況,即值傳遞和引用傳遞。我們來看下程式語言中是如何定義和區分值傳遞和引用傳遞的。
值傳遞是指在呼叫函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數。
引用傳遞是指在呼叫函數時將實際參數的位址直接傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。
有了上面的概念,然後大家就可以寫程式碼實作了,來看看Java中到底是值傳遞還是引用傳遞,於是,最簡單的一段程式碼出來了:
public static void main( String[] args) { ParamTest pt = new ParamTest(); int i = 10; pt.pass(i); System.out.println( "print in main , i is " +i); } public void pass(int j){ j = 20; system.out.println( "print in pass , j is " + j); }
上面的程式碼中,我們在pass方法中修改了參數j的值,然後分別在pass方法和main方法中列印參數的值。輸出結果如下:
print in pass , j is 20 print in main , i is 10
可見,pass方法內部對i的值的修改並沒有改變實際參數i的值。那麼,依照上面的定義,有人得到結論: Java的方法傳遞就是值傳遞。
但是,很快就有人提出質疑了(哈哈,所以,不要輕易下結論咯。)。然後,他們會搬出以下程式碼:
public static void main(String[ ] args) { ParamTest pt = new ParamTest(); User hollis = new User(); hollis.setName( "Hollis"); hollis.setGender("Male"); pt.pass(hollis); system.out.println( "print in main , user is " + hollis);}public void pass(User user) { user.setName( "hollischuang"); System.out.println( "print in pass , user is " + user);}
同樣是一個pass方法,同樣是在pass方法內修改參數的值。輸出結果如下:
print in pass , user is User{name='hollischuang', gender='Male '} print in main , user is User{name='hollischuang' , gender='Male '}
經過pass方法執行後,實參的值竟然被改變了,那按照上面的引用傳遞的定義,實際參數的值被改變了,這不就是引用傳遞了麼。於是,根據上面的兩段程式碼,有人得出一個新的結論:Java的方法中,在傳遞普通類型的時候是值傳遞,在傳遞物件類型的時候是引用傳遞。
但是,這種表述仍然是錯誤的。不信你看下面這個參數型別為對象的參數傳遞:
public static void main( string[] args) { ParamTest pt = new ParamTest(); string name = "Hollis"; pt.pass(name ) ; System.out.println( "print in main , name is " + name); } public void pass(string name) { name = "hollischuang"; system.out.println( "print in pass , name is " + name); }
上面的程式碼輸出結果為
print in pass , name is hollischuangprint in main , name is Hollis
這又作何解釋呢?同樣傳遞了一個對象,但是原始參數的值並沒有被修改,難道傳遞物件又變成值傳遞了?
Java中的值傳遞
上面,我們舉了三個例子,表現的結果卻不一樣,這也是導致許多初學者,甚至許多高階程式設計師對於Java的傳遞類型有困惑的原因。其實,我想告訴大家的是,上面的概念沒有錯,只是程式碼的例子有問題。來,我再來給大家畫—下概念中的重點,然後再舉幾個真正恰當的例子。
值傳遞是指在呼叫函數時將實際參數複製一份傳遞到函數中,這樣在函數中如果對參數進行修改,將不會影響到實際參數。
引用傳遞是指在呼叫函數時將實際參數的位址直接傳遞到函數中,那麼在函數中對參數所進行的修改,將影響到實際參數。
那麼,我來跟大家總結一下,值傳遞和引用傳遞之前的差異的重點是什麼。
值傳遞 | #參考傳遞 | |
---|---|---|
根本區別 | 會建立副本 | 不建立副本 |
我们上面看过的几个pass的例子中,都只关注了实际参数内容是否有改变。如传递的是User对象,我们试着改变他的name属性的值,然后检查是否有改变。其实,在实验方法上就错了,当然得到的结论也就有问题了。
为什么说实验方法错了呢?这里我们来举一个形象的例子。再来深入理解一下值传递和引用传递,然后你就知道为啥错了。
你有一把钥匙,当你的朋友想要去你家的时候,如果你直接把你的钥匙给他了,这就是引用传递。这种情况下,如果他对这把钥匙做了什么事情,比如他在钥匙上刻下了自己名字,那么这把钥匙还给你的时候,你自己的钥匙上也会多出他刻的名字。
你有一把钥匙,当你的朋友想要去你家的时候,你复刻了一把新钥匙给他,自己的还在自己手里,这就是值传递。这种情况下,他对这把钥匙做什么都不会影响你手里的这把钥匙。
但是,不管上面那种情况,你的朋友拿着你给他的钥匙,进到你的家里,把你家的电视砸了。那你说你会不会受到影响?而我们在pass方法中,改变user对象的name属性的值的时候,不就是在“砸电视”么。
还拿上面的一个例子来举例,我们真正的改变参数,看看会发生什么?
public static void main(String[ ] args){ ParamTest pt = new ParamTest(); User hollis = new User(); hollis.setName( "Hollis"); hollis.setGender("Male" ); pt.pass(hollis); system.out.println("print in main , user is " + hollis); public void pass(User user) { user = new User(); user.setName( "hollischuang"); user.setGender( "Male"); system.out.println( "print in pass , user is " + user);
上面的代码中,我们在pass方法中,改变了user对象,输出结果如下:
print in pass , user is User{name='hollischuang ' , gender='Male '} print in main , user is User{name='Hollis', gender= 'Male '}
我们来画一张图,看一下整个过程中发生了什么,然后我再告诉你,为啥Java中只有值传递。
稍微解释下这张图,当我们在main中创建一个User对象的时候,在堆中开辟一块内存,其中保存了name和gender等数据。然后hollis持有该内存的地址ex123456(图1)。当尝试调用pass方法,并且hollis作为实际参数传递给形式参数user的时候,会把这个地址ex123456交给user,这时,user也指向了这个地址(图2)。然后在pass方法内对参数进行修改的时候,即user = newUser();,会重新开辟一块 eX456789的内存,赋值给user。后面对user的任何修改都不会改变内存eX123456的内容(图3)。
上面这种传递是什么传递?肯定不是引用传递,如果是引用传递的话,在user=new User()的时候,实际参数的引用也应该改为指向eX456789,但是实际上并没有。
通过概念我们也能知道,这里是把实际参数的引用的地址复制了一份,传递给了形式参数。所以,上面的参数其实是值传递,把实参对象引用的地址当做值传递给了形式参数。
我们再来回顾下之前的那个“砸电视”的例子,看那个例子中的传递过程发生了什么。
同样的,在参数传递的过程中,实际参数的地址eX1213456被拷贝给了形参,只是,在这个方法中,并没有对形参本身进行修改,而是修改的形参持有的地址中存储的内容。
所以,值传递和引用传递的区别并不是传递的内容。而是实参到底有没有被复制一份给形参。在判断实参内容有没有受影响的时候,要看传的的是什么,如果你传递的是个地址,那么就看这个地址的变化会不会有影响,而不是看地址指向的对象的变化。就像钥匙和房子的关系。
那么,既然这样,为啥上面同样是传递对象,传递的String对象和User对象的表现结果不一样呢?我们在pass方法中使用name = “hollischuang”;试着去更改name的值,阴差阳错的直接改变了name的引用的地址。因为这段代码,会new一个String,在把引用交给name,即等价于name =new String(“hollischuang”);。而原来的那个”Hollis”字符串还是由实参持有着的,所以,并没有修改到实际参数的值。
所以说,Java中其实还是值传递的,只不过对于对象参数,值的内容是对象的引用。
总结
无论是值传递还是引用传递,其实都是一种求值策略(Evaluation strategy)。在求值策略中,还有一种叫做按共享传递。其实Java中的参数传递严格意义上说应该是按共享传递。
依共享傳遞,是指在呼叫函數時,傳遞給函數的是實參的位址的拷貝(如果實參在堆疊中,則直接拷貝該值)。在函數內部對參數進行操作時,需要先拷貝的位址尋找到具體的值,再進行操作。如果該值在堆疊中,那麼因為是直接拷貝的值,所以函數內部對參數進行操作不會對外部變數產生影響。如果原來拷貝的是原值在堆中的位址,那麼需要先根據該位址找到堆中對應的位置,再進行操作。因為傳遞的是位址的拷貝所以函數內對值的運算對外部變數是可見的。
簡單點說,Java中的傳遞,就是值傳遞,而這個值,其實是物件的參考。
而以共享傳遞其實只是按值傳遞的一個特例罷了。所以我們可以說Java的傳遞是依照共享傳遞,或者說Java中的傳遞是值傳遞。
所以函數內部對參數進行操作不會對外部變數產生影響。如果原來拷貝的是原值在堆中的位址,那麼需要先根據該位址找到堆中對應的位置,再進行操作。因為傳遞的是位址的拷貝所以函數內對值的運算對外部變數是可見的。
簡單點說,Java中的傳遞,就是值傳遞,而這個值,其實是物件的參考。
而以共享傳遞其實只是按值傳遞的一個特例罷了。所以我們可以說Java的傳遞是依照共享傳遞,或者說Java中的傳遞是值傳遞。
更多程式相關知識,請造訪:程式設計教學! !
以上是java中值傳遞和引用傳遞的差別是什麼的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

Java是熱門程式語言,適合初學者和經驗豐富的開發者學習。本教學從基礎概念出發,逐步深入解說進階主題。安裝Java開發工具包後,可透過建立簡單的「Hello,World!」程式來實踐程式設計。理解程式碼後,使用命令提示字元編譯並執行程序,控制台上將輸出「Hello,World!」。學習Java開啟了程式設計之旅,隨著掌握程度加深,可創建更複雜的應用程式。

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。
