Java方法的參數(shù)傳遞機制詳解
前言
對于Java初學者來說,剛學習Java的時候可能經常會聽到調用方法時參數(shù)的值傳遞與引用傳遞。
但是,實際上Java中方法的參數(shù)傳遞機制只有值傳遞。 首先,我們要了解一個概念——棧幀。
棧幀位于java虛擬機棧中,用于支持虛擬機進行方法的調用和方法的執(zhí)行??梢院唵蔚睦斫鉃闂捶椒?。
每個方法有自己獨立的棧幀。棧幀中有局部變量表、操作數(shù)棧、動態(tài)鏈接、返回地址等。
下面我們通過幾個例子具體分析java方法的參數(shù)傳遞機制。
方法的參數(shù)為基本數(shù)據類型
首先我們來看下面代碼:
public class ParamTransmit {
public static void main(String[] args) {
int i = 1;
change(i);
System.out.println("i = " + i);
}
public static void change(int j) {
j += 1;
}
}程序的運行結果為:

當在main方法中調用change方法時,參數(shù)傳遞相當于將main棧幀中的參數(shù)i=1拷貝了一份給change棧幀,此時change棧幀中,變量j的值為1。然后在change方法中執(zhí)行j += 1;運算,j的值變?yōu)?,change方法結束。main方法未結束,此時main棧幀中變量i的值仍為1。當傳遞的參數(shù)是基本數(shù)據類型時,傳遞的是數(shù)據值,即將參數(shù)拷貝一份到被調用方法的棧幀中。該方法中對變量的操作不影響原來棧幀中的變量的值,原棧幀中的變量不被修改。

方法的參數(shù)為引用類型
引用類型變量如String類型,包裝類,數(shù)組和其他自定義類等。下面依次介紹。
String類型
先上代碼
public class ParamTransmit {
public static void main(String[] args) {
String str = "hello";
change(str);
System.out.println("str = " + str);
}
public static void change(String str) {
str += "world";
}
}程序運行結果為:

首先,在jdk1.7之后,運行時常量池從方法區(qū)中移了出來,在堆中開辟了一塊區(qū)域存放運行時常量。
并且要知道String的值是不可變的,每次對String的操作都會產生新的String對象。
因此,在上面代碼中,main方法中調用change方法時,首先是將變量str的值(即運行時常量池中hello的地址)拷貝一份到change棧幀中,此時change棧幀中的變量str也是指向“hello”。
然后執(zhí)行str += "world";操作,由于String值不可變,此時change棧幀中的str指向新的字符串“helloworld”,str的地址值已改變。
而此時main棧幀中str的值并沒有改變,仍指向“hello”,所以main方法中輸出的str仍是“hello”。

包裝類
先上代碼:
public class ParamTransmit {
public static void main(String[] args) {
Integer num = 200;
change( num);
System.out.println("num = " + num);
}
private static void change(Integer num) {
num += 1;
}
}程序運行結果為:

上面代碼中,首先Integer num = 200;會自動裝箱,先調用Integer.valueOf(200),在堆中創(chuàng)建一個值為200的Integer實例,并將該實例的地址賦值給num。
main方法在調用change方式時傳遞的是num的地址值,此時change棧幀中的num也指向值為200的Integer實例。
當執(zhí)行num += 1;時,也自動裝箱,先調用Integer.valueOf(201),在堆中創(chuàng)建一個職位201的Integer實例,并將該實例的地址賦值給change棧幀中的變量num,change方法結束。
但此時,main棧幀的num仍指向值為200的Integer實例,因此打印結果為200。
內存結構圖如下:

注:上述內存結構圖僅適用于不在-128至127之間的整數(shù)。Integer類的自動裝箱機制是首先提供一個Integer cache[],用于存放-128至127的緩存。因此,若對實參及對形參操作的結果均在-128至127之間,則實參及形參應指向數(shù)組中的元素,而不是堆中新的Integer實例。
數(shù)組
直接上代碼
import java.util.Arrays;
public class ParamTransmit {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
change(arr);
System.out.println("arr = " + Arrays.toString(arr));
}
private static void change(int[] arr) {
arr[0] += 1;
}程序運行結果為:

數(shù)組變量也是引用類型。main方法在調用change方法時,傳遞的是數(shù)組arr的地址值。
change方法內,將數(shù)組中第一個元素的值自增1,此時數(shù)組中第一個元素的值變?yōu)?。
change方法結束,main方法繼續(xù)執(zhí)行,此時main棧幀變量arr仍指向該數(shù)組,只不過該數(shù)組中的元素已被修改。所以輸出的是被修改后的數(shù)組。
內存結構示意圖如下:

可以引申為多維數(shù)組及數(shù)組元素為引用類型的情況。
其他自定義類
直接上代碼
class MyData {
int a = 10;
}
public class ParamTransmit {
public static void main(String[] args) {
MyData md = new MyData();
change(md);
System.out.println("md.a = " + md.a);
}
private static void change(MyData md) {
md.a += 1;
}
}程序運行結果為:

main方法調用change方法時,同樣是將main棧幀中的變量md的值傳遞到change棧幀中,此時傳遞的是地址值,指向堆中同一個MyData實例。
然后執(zhí)行md.a += 1,此時該實例的成員變量a為11。
change方法執(zhí)行完畢,main方法未結束,main棧幀中的變量md仍指向該實例,此時輸出的a為11。內存結構如下圖。

若將上面代碼修改如下,會是什么結果呢?
class MyData {
int a = 10;
}
public class ParamTransmit {
public static void main(String[] args) {
MyData md = new MyData();
change(md);
System.out.println("md.a = " + md.a);
}
private static void change(MyData md) {
md = new MyData();
md.a += 1;
}
}程序運行結果為:

main方法調用change方法時,同樣傳遞的是地址值。
然而執(zhí)行md = new MyData();時,在堆中創(chuàng)建了一個新的實例。
此時change棧幀中的變量md的值變成了新的MyData實例的地址。
然后繼續(xù)執(zhí)行md.a += 1;,修改的是新的實例的成員變量。
當change方法調用結束后,main方法繼續(xù)執(zhí)行,此時main棧幀中md指向的仍是原來的實例,原實例的成員變量未被修改,仍是10。因此輸出結果是10。
內存結構如下圖。

總結
通過以上分析,我們可以知道java的參數(shù)傳遞機制是值傳遞。
若參數(shù)是基本數(shù)據類型,傳遞的是數(shù)據值;若參數(shù)是引用數(shù)據類型,傳遞的是地址值。
若參數(shù)是基本數(shù)據類型,對形參的操作不影響實參,因其是不同棧幀的不同變量。
若參數(shù)是引用數(shù)據類型,并且是String、包裝類等。
因其對象的不可變性,對形參的操作會導致其指向新的String或包裝類等對象,但不影響實參,實參仍指向原來的對象且該對象并未被修改。
若參數(shù)是引用數(shù)據類型,并且是數(shù)組、StringBuffer及其他自定義類等時,對形參的操作會影響到實參,因其指向的是同一個實例。
到此這篇關于Java方法的參數(shù)傳遞機制詳解的文章就介紹到這了,更多相關Java方法的參數(shù)傳遞內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
關于Spring的AnnotationAwareAspectJAutoProxyCreator類解析
這篇文章主要介紹了關于Spring的AnnotationAwareAspectJAutoProxyCreator類解析,Spring是一個開源免費的框架 , 容器,是一個輕量級的框架 ,需要的朋友可以參考下2023-05-05
快速解決SpringMVC @RequestBody 用map接收請求參數(shù)的問題
今天小編就為大家分享快速解決SpringMVC @RequestBody 用map接收請求參數(shù)的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08
SpringCloud?Feign實現(xiàn)微服務之間相互請求問題
Feign是Netflix開發(fā)的聲明式、模板化的HTTP客戶端,?Feign可以幫助我們更快捷、優(yōu)雅地實現(xiàn)微服務之間的調用,這篇文章主要介紹了SpringCloud?Feign實現(xiàn)微服務之間相互請求,需要的朋友可以參考下2022-06-06
關于HttpServletRequest獲取POST請求Body參數(shù)的3種方式
這篇文章主要介紹了關于HttpServletRequest獲取POST請求Body參數(shù)的3種方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-11-11

