Java内存管理-Stackoverflow问答-Java是传值还是传引用?(十一)
做一個積極的人
編碼、改bug、提升自己
我有一個樂園,面向編程,春暖花開!
推薦閱讀
第一季
0、Java的線程安全、單例模式、JVM內存結構等知識梳理
1、Java內存管理-程序運行過程(一)
2、Java內存管理-初始JVM和JVM啟動流程(二)
3、Java內存管理-JVM內存模型以及JDK7和JDK8內存模型對比總結(三)
4、Java內存管理-掌握虛擬機類加載機制(四)
5、Java內存管理-掌握虛擬機類加載器(五)
6、Java內存管理-類加載器的核心源碼和設計模式(六)
7、Java內存管理-掌握自定義類加載器的實現(七)
第一季總結:由淺入深JAVA內存管理 Core Story
第二季
8、Java內存管理-愚人節new一個對象送給你(八)
【福利】JVM系列學習資源無套路贈送
9、Java內存管理-”一文掌握虛擬機創建對象的秘密”(九)
10、Java內存管理-你真的理解Java中的數據類型嗎(十)
11、Java內存管理-Stackoverflow問答-Java是傳值還是傳引用?(十一)
12、Java內存管理-探索Java中字符串String(十二)
實戰
一文學會Java死鎖和CPU 100% 問題的排查技巧
分享一位老師的人工智能教程。零基礎!通俗易懂!風趣幽默!
大家可以看看是否對自己有幫助,點擊這里查看【人工智能教程】。接下來進入正文。
勿在流沙筑高臺,出來混遲早要還的。
本文導圖:
分享一位老師的人工智能教程。零基礎!通俗易懂!風趣幽默!
大家可以看看是否對自己有幫助,點擊這里查看【人工智能教程】。接下來進入正文。
文章目錄
- 第一季
- 第二季
- 實戰
- 一、由一個提問引發的思考
- 二、為什么有傳值還是傳引用的說法
- 三、圖解傳值和傳引用過程
- 四、本文總結
- 參考文章
一、由一個提問引發的思考
在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變18ppg.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 = 50 mian 中 arrs[3] = 9 mian 中 prettyGirl.getAge() = 28 ----------------------------------------- change 中 pnum = 100 change 中 parrs[3] = 8 change 中 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編程技術樂園:一個分享編程知識的公眾號。跟著老司機一起學習干貨技術知識,每天進步一點點,讓小的積累,帶來大的改變!
掃描關注,后臺回復【資源】,獲取珍藏干貨! 99.9%的伙伴都很喜歡
? 每天都在變得更好的阿飛云
總結
以上是生活随笔為你收集整理的Java内存管理-Stackoverflow问答-Java是传值还是传引用?(十一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: .net html5页面缓存技术,.ne
- 下一篇: java美元兑换,(Java实现) 美元