摘要:引用數據類型指針存放在局部變量表中,調用方法的時候,副本引用壓棧,賦值僅改變副本的引用。方法執行完畢,不再局部變量不再被使用到,等待被回收。
小方法大門道
小瓜瓜作為一個Java初學者,今天跟我說她想通過一個Java方法,將外部變量通過參數傳遞到方法中去,進行邏輯處理,方法執行完畢之后,再對修改過的變量進行判斷處理,代碼如下所示。
public class MethodParamsPassValue { public static void doErrorHandle() { boolean a = false; int b = 5; passBaseValue(a, b); if (a == true || b == 10) { System.out.println("Execute Something"); } else { System.out.println("param result wrong"); } } public static void passBaseValue(boolean flg, int num) { flg = true; num = 10; } public static void main(String[] args) { doErrorHandle(); } }
上述代碼是有問題的,布爾變量a和整型變量b在方法操作之后,它們的值并沒有發生變化,小瓜瓜事與愿違。
究其原因在Java方法中參數列表有兩種類型的參數,基本類型和引用類型。
基本類型:值存放在局部變量表中,無論如何修改只會修改當前棧幀的值,方法執行結束對方法外不會做任何改變;此時需要改變外層的變量,必須返回主動賦值。
引用數據類型:指針存放在局部變量表中,調用方法的時候,副本引用壓棧,賦值僅改變副本的引用。但是如果通過操作副本引用的值,修改了引用地址的對象,此時方法以外的引用此地址對象當然被修改。(兩個引用,同一個地址,任何修改行為2個引用同時生效)。
這兩種類型都是將外面的參數變量拷貝一份到局部變量中,基本類型為值拷貝,引用類型就是將引用地址拷貝一份。
方法參數為基本類型的值傳遞public class MethodParamsPassValue { public static void passBaseValue(boolean flg, int num) { flg = true; num = 10; } public static void main(String[] args) { boolean a = false; int b = 5; System.out.println("a : " + a + " b : " + b); passBaseValue(a, b); System.out.println("a : " + a + " b : " + b); } }
返回結果:
a : false b : 5 a : false b : 5
方法參數flg被初始化為外部變量a的拷貝,值為false。參數num被初始化為外部變量b的拷貝,值為5。
執行方法邏輯,方法中的局部變量flg被改變為true,局部變量flg被改變為10。
3.方法執行完畢,不再局部變量不再被使用到,等待被GC回收。
結論:當方法參數為基本類型時,是將外部變量值拷貝到局部變量中而進行邏輯處理的,故方法是不能修改原基本變量的。
方法參數為包裝類型的引用傳遞public class MethodParamsPassValue { public static void passReferenceValue(Boolean flg, Integer num) { flg = true; num = 10; } public static void main(String[] args) { Boolean a = false; Integer b = 5; System.out.println("a : " + a + " b : " + b); passReferenceValue(a, b); System.out.println("a : " + a + " b : " + b); } }
結果為:
a : false b : 5 a : false b : 5
當傳入參數為包裝類型時,為對象的引用地址拷貝。那么既然是引用拷貝為什么還是沒有更改原來的包裝類型的變量值呢?
這是因為Java中的自動裝箱機制,當在方法中執行 flg = true 時,實際在編譯后執行的是 flg = Boolean.valueOf(true),即又會產生一個新的Boolean對象。同理Integer num也是如此。
方法參數為類的對象引用時public class ParamObject { private boolean flg; private int num; public ParamObject(boolean flg, int num) { this.flg = flg; this.num = num; } public boolean isFlg() { return flg; } public void setFlg(boolean flg) { this.flg = flg; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } @Override public String toString() { return "ParamObject{" + "flg=" + flg + ", num=" + num + "}"; } }
public class MethodParamsPassValue { public static void passObjectValue(ParamObject paramObject) { paramObject.setFlg(true); paramObject.setNum(10); } public static void main(String[] args) { ParamObject a = new ParamObject(false, 5); System.out.println(a); passObjectValue(a); System.out.println(a); } }
結果為:
ParamObject{flg=false, num=5} ParamObject{flg=true, num=10}
結論:對于引用類型的方法參數,會將外部變量的引用地址,復制一份到方法的局部變量中,兩個地址指向同一個對象。所以如果通過操作副本引用的值,修改了引用地址的對象,此時方法以外的引用此地址對象也會被修改。(兩個引用,同一個地址,任何修改行為2個引用同時生效)。
腦筋急轉彎之"交換兩個對象"public class MethodParamsPassValue { public static void swapObjectReference(ParamObject object1, ParamObject object2) { ParamObject temp = object1; object1 = object2; object2 = temp; } public static void main(String[] args) { ParamObject a = new ParamObject(true, 1); ParamObject b = new ParamObject(false, 2); System.out.println("a : " + a + " b : " + b); swapObjectReference(a, b); System.out.println("a : " + a + " b : " + b); } }
結果為
a : ParamObject{flg=true, num=1} b : ParamObject{flg=false, num=2} a : ParamObject{flg=true, num=1} b : ParamObject{flg=false, num=2}
有了上面的知識之后,我們會發現這個方法中的引用地址交換,只不過是一個把戲而已,只是對方法中的兩個局部變量的對象引用值進行了交換,不會對原變量引用產生任何影響的。
一個方法返回兩個返回值Java方法中只能Return一個返回值,那么如何在一個方法中返回兩個或者多個返回值呢?(⊙v⊙)嗯,我們可以通過使用泛型來定義一個二元組來達到我們的目的。
public class TwoTuple { public final A first; public final B second; public TwoTuple(A a, B b) { first = a; second = b; } public String toString() { return "(" + first + ", " + second + ")"; } }
public class MethodParamsPassValue { public static TwoTuplereturnTwoResult(Boolean flg, Integer num) { flg = true; num = 10; return new TwoTuple<>(flg, num); } public static void main(String[] args) { TwoTuple result = returnTwoResult(false,5); System.out.println("first : " + result.first + ", second : " + result.second); } }
結果為:
first : true, second : 10完整代碼
package com.lingyejun.authenticator; /** * 基本類型,賦值運算=,會直接改變變量的值,原來的值被覆蓋掉。 * 引用類型,賦值運算=,會改變引用中所保存的地址,舊地址被覆蓋掉,但原來的對象不會改變。 * * @Author: lingyejun * @Date: 2019/6/16 * @Describe: * @Modified By: */ public class MethodParamsPassValue { public static void doErrorHandle() { boolean a = false; int b = 5; passBaseValue(a, b); if (a == true || b == 10) { System.out.println("Execute Something"); } else { System.out.println("param result wrong"); } } public static void passBaseValue(boolean flg, int num) { flg = true; num = 10; } public static void passReferenceValue(Boolean flg, Integer num) { flg = true; num = 10; } public static void passObjectValue(ParamObject paramObject) { paramObject.setFlg(true); paramObject.setNum(10); } public static void swapObjectReference(ParamObject object1, ParamObject object2) { ParamObject temp = object1; object1 = object2; object2 = temp; } public static TwoTuplereturnTwoResult(Boolean flg, Integer num) { flg = true; num = 10; return new TwoTuple<>(flg, num); } public static void main(String[] args) { doErrorHandle(); System.out.println("============================"); boolean initFlg = false; int initNum = 5; System.out.println("init flg : " + initFlg + " init num : " + initNum); passBaseValue(initFlg, initNum); System.out.println("init flg : " + initFlg + " init num : " + initNum); System.out.println("============================"); Boolean referenceFlg = false; Integer referenceNum = 5; System.out.println("reference flg : " + referenceFlg + " reference num : " + referenceNum); passReferenceValue(referenceFlg, referenceNum); System.out.println("reference flg : " + referenceFlg + " reference num : " + referenceNum); System.out.println("============================"); ParamObject paramObject = new ParamObject(false, 5); System.out.println(paramObject); passObjectValue(paramObject); System.out.println(paramObject); System.out.println("============================"); ParamObject object1 = new ParamObject(true, 1); ParamObject object2 = new ParamObject(false, 2); System.out.println("object1 : " + object1 + " object2 : " + object2); swapObjectReference(object1, object2); System.out.println("object1 : " + object1 + " object2 : " + object2); System.out.println("============================"); TwoTuple result = returnTwoResult(false,5); System.out.println("first : " + result.first + ", second : " + result.second); } }
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77780.html
摘要:內置函數們能夠被提拔出來,這就意味著它們皆有獨到之處,有用武之地。因此,掌握內置函數的用法,就成了我們應該點亮的技能。報錯包含了內置命名空間中的名稱,在控制臺中輸入,就能發現很多內置函數異常和其它屬性的名稱。 Python 提供了很多內置的工具函數(Built-in Functions),在最新的 Python 3 官方文檔中,它列出了 69 個。 大部分函數是我們經常使用的,例如 p...
摘要:有種流行的觀點說的另外一個特殊之處在于,在方法調用傳參數時,是按值傳遞的,其他普通對象是引用傳遞。然而這種說法是大大錯誤的,至少是完全誤解了值傳遞和引用傳遞的概念。方法調用傳參只有一種傳遞就是值傳遞。 上篇文章說到Java的String是比較特殊的對象,它是不可變的。 有種流行的觀點說String的另外一個特殊之處在于,在方法調用傳參數時,String是按值傳遞的,其他普通對象是引用傳...
摘要:接下了,我們調用方法,來嘗試改變的值以此驗證中的傳值方式。我們將作為實參傳給方法,形參來接受這個實參,在這里就體現出了兩種傳參方式的不同。中只有值傳遞這一種方式,只不過對于引用類型來說,傳遞的參數是對象的引用罷了。 前言 這幾天在整理java基礎知識方面的內容,對于值傳遞還不是特別理解,于是查閱了一些資料和網上相關博客,自己進行了歸納總結,最后將其整理成了一篇博客。 值傳遞 值傳遞是指...
摘要:每個棧幀中包括局部變量表用來存儲方法中的局部變量非靜態變量函數形參。操作數棧虛擬機的解釋執行引擎被稱為基于棧的執行引擎,其中所指的棧就是指操作數棧。指向運行時常量池的引用存儲程序執行時可能用到常量的引用。 本篇文章轉自微信公眾號:Java后端技術 學過Java基礎的人都知道:值傳遞和引用傳遞是初次接觸Java時的一個難點,有時候記得了語法卻記不得怎么實際運用,有時候會的了運用卻解釋不出...
摘要:操作數棧虛擬機的解釋執行引擎被稱為基于棧的執行引擎,其中所指的棧就是指操作數棧。基本數據類型的靜態變量前面提到方法區用來存儲一些共享數據,因此基本數據類型的靜態變量名以及值存儲于方法區的運行時常 本文旨在用最通俗的語言講述最枯燥的基本知識 學過Java基礎的人都知道:值傳遞和引用傳遞是初次接觸Java時的一個難點,有時候記得了語法卻記不得怎么實際運用,有時候會的了運用卻解釋不出原理,而...
閱讀 3884·2021-11-17 09:33
閱讀 1196·2021-10-09 09:44
閱讀 399·2019-08-30 13:59
閱讀 3477·2019-08-30 11:26
閱讀 2177·2019-08-29 16:56
閱讀 2848·2019-08-29 14:22
閱讀 3150·2019-08-29 12:11
閱讀 1266·2019-08-29 10:58