传值类型_Java内存管理:Stackoverflow问答-Java是传值还是传引用(十一)
勿在流沙筑高臺,出來混遲早要還的。
做一個積極的人
編碼、改bug、提升自己
我有一個樂園,面向編程,春暖花開!
本文導圖:
一、由一個提問引發的思考
在Stack Overflow 看到這樣一個問題:
Is Java “pass-by-reference” or “pass-by-value”?
翻譯成中文:
Java是傳值還是傳引用?
請先不要看下面的內容,思考10秒后,在繼續閱讀!!!
為什么建議先思考,在閱讀內容呢?
我們每天可能會利用碎片化的時間閱讀很多內容,有很多信息和知識其實在大腦過一下,然后就忘記了!如何才能高效的利用碎片化時間掌握或者記住更多的內容和知識,我自己碎片化閱讀的理解和技巧:閱讀一篇自己感興趣技術文章,在時間允許的時間下,一定是一次性閱讀完,在閱讀中帶著自己的問題,閱讀后有自己的簡單總結。 千萬不要閱讀 一段內容,看到微信有人發消息給你,就切換聊天框回復消息,然后回復完再切換回來閱讀技術文章。這種 大腦 的切換是需要耗費資源的,也影響閱讀的效果和效率(大腦在多個任務切換類似cpu多線程調度,線程的頻繁切換。就如多線程不一定能提供效率,頻繁的線程/任務切換耗費cpu大量資源)!
扯遠 了,切換回到本文正題,Java是傳值還是傳引用?
我相信你閱讀完本篇后一定能夠回答上面的問題,并且在工作在寫類似傳參的代碼也會有更深入的理解。開啟探索之旅,Let's go!
二、為什么有傳值還是傳引用的說法
在Java程序中會包含方法,方法會分為方法聲明和方法實現,在方法聲明中又有參數列表,參數根據調用后的效果不同,也就是是否改變參數的原始值,可以劃分為兩種: 按值傳遞參數和按引用傳遞參數。
- 按值傳遞參數 == 傳值
- 按引用傳遞參數 == 傳引用
也就是之前介紹過的Java的基本類型和引用類型,如果方法參數中傳遞的基本類型就認為是 按值傳遞(傳值),方法參數中傳遞的是引用類型,就稱之為按引用傳遞(傳引用)。
三、圖解傳值和傳引用過程
一段簡單的代碼:
public class PrettyGirl { /** * 芳齡幾何 */ int age; public int getAge() { return age; } public void setAge(int age) { this.age = age; } public static void main(String[] args) { // 引用類型 PrettyGirl prettyGirl = new PrettyGirl(); prettyGirl.setAge(28); // 基本類型 int num = 50; // 數組arrs也引用類型 int[] arrs = new int[]{2,0,1,9}; System.out.println("mian 中 num = " + num); System.out.println("mian 中 arrs[3] = " + arrs[3]); System.out.println("mian 中 prettyGirl.getAge() = " + prettyGirl.getAge()); System.out.println("-----------------------------------------"); // 調用 change方法 prettyGirl.change(num, arrs, prettyGirl); System.out.println("調用change 后 mian 中 num = " + num); System.out.println("調用change 后 mian 中 arrs[3] = " + arrs[3]); System.out.println("調用change 后 mian 中 prettyGirl.getAge() = " + prettyGirl.getAge()); } public void change(int pnum, int[] parrs, PrettyGirl ppg) { //在change中 改變值類型pnum的值 pnum = pnum + 50; //在change中 改變引用類型 parrs,ppg 的值 parrs[3] = 8; // 從28變18 ppg.setAge(18); System.out.println("change 中 pnum = " + pnum); System.out.println("change 中 parrs[3] = " + parrs[3]); System.out.println("change 中 ppg.getAge() = " + ppg.getAge()); System.out.println("-----------------------------------------"); }}思考一下,打印的結果是什么?
打印結果如下:
mian 中 num = 50mian 中 arrs[3] = 9mian 中 prettyGirl.getAge() = 28-----------------------------------------change 中 pnum = 100change 中 parrs[3] = 8change 中 ppg.getAge() = 18-----------------------------------------調用change 后 mian 中 num = 50調用change 后 mian 中 arrs[3] = 8調用change 后 mian 中 prettyGirl.getAge() = 18下面開啟分析之旅,結合之前學過的Java內存模型來畫上面代碼執行的內存變化的圖
注:下圖只是為了演示講解說明,真實內存地址不一定是這樣!
int 類型變量num在棧中分配一塊內存,而 parrs 與 ppg 分配兩塊內存,棧中一塊,堆中一塊。當調用change方法時,創建三個變量 pnum,parrs,ppg這里相當于把棧中的數據全備份一份給這三個數值,則有
在change方法中對傳遞的參數進行修改,此時pnum的值修改為 100,堆中ppg指向的對象年齡由28改為18,數組中parrs[3]修改為8。也就是 ppg與 parrs 改變了堆中的具體數值,而 pnum 改變的只是棧中的數值。
最后,當change方法調用結束,change棧幀被彈出,則對應的pnum,ppg,parrs 三個變量也消亡,此時只有main棧幀情況如下圖:
通過上圖的演示,上面代碼的打印結果就很清晰明了了。
tips:回顧 java 棧
Java棧中存放的是一個個的棧幀,每個棧幀對應一個被調用的方法,在棧幀中包括局部變量表(Local Variables)、操作數棧(Operand Stack)、指向當前方法所屬的類的運行時常量池(運行時常量池的概念在方法區部分會談到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些額外的附加信息。當線程執行一個方法時,就會隨之創建一個對應的棧幀,并將建立的棧幀壓棧。當方法執行完畢之后,便會將棧幀出棧。因此可知,線程當前執行的方法所對應的棧幀必定位于Java棧的頂部。
根據上面例子中這樣的內存變換,想必你應該知道按值傳遞與按引用傳遞的深層原因了吧!
從上圖中看所有的參數傳遞 本質都是按址值傳遞, 也就是內存地址的值 。 基本類型因為棧內存地址中保存就是其本身值,所有在參數傳遞的時候,拷貝本身的值進行傳遞,而引用類型在棧內存地址中保存的是引用的值,通過棧內存保存引用的值指向堆中獲取對象真是的值,在參數傳遞的時候,拷貝的是引用的值。
所有在方法傳遞參數后,如果基本類型的值在傳遞的方法中有修改,不會影響傳遞前方法中的值。而引用類型就不同了,因為它修改的是引用地址指向堆中數據,這部分數據在參數傳遞的時候不會拷貝一份,就如上面圖解標識出的一樣。
四、本文總結
在Java中,對象通過引用傳遞,基本類型按值傳遞。
這句話的描述有一半是不準確的。就如上面我們圖中看到的那樣基本類型是按照值傳遞的; 引用類型是拷貝引用的址值傳遞的,也即對象不通過引用傳遞。正確的描述語句是對象引用也是按值傳遞。
其實在Java語言規范(JLS)中描述:Java 嚴格按值傳遞,可以理解與C完全相同,也就是Java中參數傳遞的本質是按址值傳遞。
如果你在閱讀完本篇后,對上面問題有自己的深入的理解,有歡迎文末留言一起探討!
參考文章
Is Java “pass-by-reference” or “pass-by-value”?
Java is Pass-by-Value, Dammit!
謝謝你的閱讀,如果您覺得這篇博文對你有幫助,請點贊或者喜歡,讓更多的人看到!祝你每天開心愉快!
不管做什么,只要堅持下去就會看到不一樣!在路上,不卑不亢!
愿你我在人生的路上能都變成最好的自己,能夠成為一個獨擋一面的人
? 每天都在變得更好的阿飛云
總結
以上是生活随笔為你收集整理的传值类型_Java内存管理:Stackoverflow问答-Java是传值还是传引用(十一)的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Bit-Z亮相巴厘岛XBlockchai
- 下一篇: Bit-Z CEO长顺入围2018中国经
