目錄作者:小牛呼嚕嚕 | https://xiaoniuhululu.com
計算機內功、JAVA底層、面試相關資料等更多精彩文章在公眾號「小牛呼嚕嚕 」
- 經典的問題
- 形參&實參
- Java是值傳遞還是參考傳遞?
- 傳參的型別:基本資料型別
- 傳參的型別:參考資料型別
- 尾語
經典的問題
Java 傳參是值傳遞還是參考傳遞?這個問題很基礎,但是許多人都有點懵
形參&實參
首先我們得了解關于引數的幾個概念
形式引數:定義函式時使用的引數,用來接收函式傳入引數,比如我們寫個函式,函式中的引數為形式引數
public void test(String str) { //str為形式引數
System.out.println(str);
}
實際引數:我們呼叫函式時,函式名后面括號中的引數稱為實際引數,必須有確定的值,如下面例子所示
public static void main(String[] args) {
A a = new A();
a.test("小 明"); //"小 明"則為實際引數
}
可以發現,當呼叫一個有參函式的時候,會把實際引數傳遞給形式引數,
這種傳遞的程序的引數一般有2種情況值傳遞和參考傳遞,
- 值傳遞:呼叫函式時將實際引數復制一份傳遞到函式中,函式內部對引數內部進行修改不會影響到實際引數,即
創建副本,不會影響原生物件 - 參考傳遞 :方法接收的是實際引數所參考的地址,不會創建副本,對形參的修改將影響到實參,即
不創建副本,會影響原生物件
我們還得知道:在Java中有2種資料型別,其中主要有基本資料型別和參考資料型別,除了8種基本資料型別以外都是參考資料型別,分別是byte,short,int,long,char,boolean,float,double
Java是值傳遞還是參考傳遞?
對于這個問題,我們先來看幾個例子慢慢道來:
傳參的型別:基本資料型別
public class TestBasic {
public static void main(String[] args) {
int num1 = 10;
int num2 = 20;
change(num1, num2);
System.out.println("==============");
System.out.println("num1 = " + num1);
System.out.println("num2 = " + num2);
}
public static void change(int param1, int param2) {
System.out.println("param1 = " + param1);
System.out.println("param2 = " + param2);
param1 = 333;
param2 = 444;
System.out.println("after change....");
System.out.println("param1 = " + param1);
System.out.println("param2 = " + param2);
}
}
結果:
param1 = 10
param2 = 20
after change....
param1 = 333param2 = 444
==============
num1 = 10
num2 = 20
我們可以發現,change()方法內對變數重新賦值,并未改變變數num1和num2的值,改變的只是change()方法內的num1和num2的副本,
我們需要知道,基本資料型別在記憶體中只有一塊存盤空間,分配在堆疊stack中,
Java傳參的型別如果是基本資料型別,是值傳遞,

傳參的型別:參考資料型別
public class TestQuote {
public static void main(String[] args) {
String str = "小明";
StringBuilder str2 = new StringBuilder("今天天氣好");
change(str,str2);
System.out.println("==============");
System.out.println("str = " + str);
System.out.println("str2 = " + str2);
}
public static void change(String param1,StringBuilder param2) {
System.out.println("param1 = " + param1);
System.out.println("param2 = " + param2);
param1= "小張";
param2.append(",我們去釣魚");
System.out.println("after change....");
System.out.println("param1 = " + param1);
System.out.println("param2 = " + param2);
}
}
結果:
param1 = 小明
param2 = 今天天氣好
after change....
param1 = 小張param2 = 今天天氣好,我們去釣魚
str = 小明
str2 = 今天天氣好,我們去釣魚
我們發現str變數沒有改變,但是str2變數卻改變了,大家是不是迷惑了:Java傳參的型別如果是參考資料型別,是值傳遞還是參考傳遞?

其實大家被一堆術語給忽悠了,筆者畫了2張圖,幫助大家理解:
before change():

after change():

在Java中,除了基本資料型別以外,其他的都是參考型別,參考型別在記憶體中有兩塊存盤空間(一塊在堆疊stack中,一塊在堆heap中),
如果引數是參考型別,傳遞的就是實參所參考的物件在堆疊中地址值的拷貝,這里創建的副本是 地址的拷貝,那就有人說了,可是它值變了呀,這明明就是"參考傳遞"嘛?
我們可以換個角度理解,如果我們把堆疊地址當成值,會創建堆疊地址副本(復制堆疊幀),堆疊地址最終并沒有改變,改變的是堆記憶體中的值,這就好比堆疊地址是鑰匙,我們copy了一把,它能打開保險箱,我們關心的是鑰匙有沒有花紋這種變化,至于打開保險箱后的錢多錢少,我們并不需要關心,
雖然呼叫完函式后,str2變數值(堆中的資料)改變了,但是引數是參考型別,傳遞的實參是 堆疊中地址值,這是我們關心的,拷貝的是堆疊中地址值,最終堆疊中地址值并沒有改變,所以是符合值傳遞的定義創建副本,不會影響原生物件,
可能又有人問了,那str變數值為啥沒有改變呢?其實這完全是由于String類的特殊,我們知道它是不可變的final,這個時候在函式中 param1= "小張";其實會隱式創建一個新的String物件,同時堆記憶體中會開辟一個新的記憶體空間,param1指向了這個新開辟的記憶體空間,原地址str指向的堆記憶體空間中資料沒有任何改變,
尾語
Java中只有值傳遞,始終是傳值的,我們要牢記,這個是官方明確說的,我們還應該清楚,其中的緣由,
引數是基本資料型別,復制的是具體值;如果引數是參考型別,把地址當成值,復制的是地址;還有String類是一個非常特殊的類,她是不可變的,
參考資料:
《深入理解Java虛擬機:JVM高級特性與最佳實踐》
https://www.cnblogs.com/ITnoteforlsy/p/12266409.html
本篇文章到這里就結束啦,很感謝你能看到最后,如果覺得文章對你有幫助,別忘記關注我!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/500497.html
標籤:Java
