Java 方法調用中的參數是值傳遞仍是引用傳遞呢?相信每一個作開發的同窗都碰到過傳這個問題,不光是作 Java 的同窗,用 C#、Python 開發的同窗一樣確定遇到過這個問題,並且頗有可能不止一次。java
那麼,Java 中究竟是值傳遞仍是引用傳遞呢,答案是值傳遞,Java 中沒有引用傳遞這個概念。程序員
Java 中有能夠歸納爲兩大類數據類型,一類是基本類型,另外一類是引用類型。web
基本類型shell
byte、short、int、long、float、double、char、boolean 是 Java 中的八種基本類型。基本類型的內存分配在棧上完成,也就是 JVM 的虛擬機棧。也就是說,當你使用以下語句時:json
int i = 89;
複製代碼
會在虛擬機棧上分配 4 個字節的空間出來存放。數組
引用類型app
引用類型有類、接口、數組以及 null 。咱們平時熟悉的各類自定義的實體類啊就在這個範疇裏。ide
當咱們定義一個對象而且使用 new 關鍵字來實例化對象時。學習
User user = new User();
複製代碼
會經歷以下三個步驟:this
一、聲明一個引用變量 user,在虛擬機棧上分配空間;
二、使用 new 關鍵字建立對象實例,在堆上分配空間存放對象內的屬性信息;
三、將堆上的對象連接到 user 變量上,因此棧上存儲的實際上就是存的對象在堆上的地址信息;
數組對象也是同樣的,棧上只是存了一個地址,指向堆上實際分配的數組空間,實際的值是存在堆上的。
爲了清楚的展現空間分配,我畫了一張類型空間分配的示例圖。
當咱們將 8 種基本類型做爲方法參數傳遞時,沒有爭議,傳的是什麼(也就是實參),方法中接收的就是什麼(也就是形參)。傳遞過去的是 1 ,那接到的就是1,傳過去的是 true,接收到的也就是 true。
看下面這個例子,將變量 oldIntValue 傳給 changeIntValue 方法,在方法內對參數值進行修改,最後輸出的結果仍是 1。
public static void main( String[] args ) throws Exception{
int oldIntValue = 1;
System.out.println( oldIntValue );
passByValueOrRef.changeIntValue( oldIntValue );
System.out.println( oldIntValue );
}
public static void changeIntValue( int oldValue ){
int newValue = 100;
oldValue = newValue;
}
複製代碼
改變參數值並不會改變原變量的值,沒錯吧,Java 是按值傳遞。
有的同窗說那不對呀,你看我下面這段代碼,就不是這樣。
public static void main( String[] args ) throws Exception{
int[] oldArray = new int[] { 1, 2 };
System.out.println( oldArray[0] );
changeArrayValue( oldArray );
System.out.println( oldArray[0] );
}
public static void changeArrayValue( int[] newArray ){
newArray[0] = 100;
}
複製代碼
這段代碼的輸出是
1
100
複製代碼
說明調用 changeArrayValue 方法時,修改傳過來的數組參數中的第一項後,原變量的內容改變了,那這怎麼是值傳遞呢。
別急,看看下面這張圖,展現了數組在 JVM 中的內存分配示例圖。
實際上能夠理解爲 changeArrayValue 方法接收的參數是原變量 oldArray 的副本拷貝,只不過數組引用中存的只是指向堆中數組空間的首地址而已,因此,當調用 changeArrayValue 方法後,就造成了 oldArray 和 newArray 兩個變量在棧中的引用地址都指向了同一個數組地址。因此修改參數的每一個元素就至關於修改了原變量的元素。
通常咱們在開發過程當中有不少將類實例做爲參數的狀況,咱們抽象出來的各類對象常常在方法間傳遞。好比咱們定義了一個用戶實體類。
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
複製代碼
比方說咱們有一個原始的實體 User 類對象,將這個實體對象傳給一個方法,這個方法可能會有一些邏輯處理,好比咱們拿到這個用戶的 name 屬性,發現 name 爲空,咱們就給 name 屬性賦予一個隨機名稱,例如 「用戶398988」。這應該是很常見的一類場景了。
咱們一般這樣使用,將 user 實例當作參數傳過來,處理完成後,再將它返回。
public static void main( String[] args ) throws Exception{
User oldUser = new User( "原始姓名", 8 );
System.out.println( oldUser.toString() );
oldUser = changeUserValue( oldUser );
System.out.println( oldUser.toString() );
}
public static User changeUserValue( User newUser ){
newUser.setName( "新名字" );
newUser.setAge( 18 );
return newUser;
}
複製代碼
但有的同窗說,我發現修改完成後就算不返回,原變量 oldUser 的屬性也改變了,好比下面這樣:
public static void main( String[] args ) throws Exception{
User oldUser = new User( "原始姓名", 8 );
System.out.println( oldUser.toString() );
changeUserValue( oldUser );
System.out.println( oldUser.toString() );
}
public static void changeUserValue( User newUser ){
newUser.setName( "新名字" );
newUser.setAge( 18 );
}
複製代碼
返回的結果都是下面這樣
User{name='原始姓名', age=8}
User{name='新名字', age=18}
複製代碼
那這不就是引用傳遞嗎,改了參數的屬性,就改了原變量的屬性。仍然來看一張圖
實際上仍然不是引用傳遞,引用傳遞咱們學習 C++ 的時候常常會用到,就是指針。而這裏傳遞的實際上是一個副本,副本中只存了指向堆空間對象實體的地址而已。咱們咱們修改參數 newUser 的屬性間接的就是修改了原變量的屬性。
有同窗說,那畫一張圖說這樣就是這樣嗎,你說是副本就是副本嗎,我偏說就是傳的引用,就是原變量,也說得通啊。
確實是說的通,若是真是引用傳遞,也確實是這樣的效果沒錯。那咱們就來個反例。
public static void main( String[] args ) throws Exception{
User oldUser = new User( "原始姓名", 8 );
System.out.println( oldUser.toString() );
wantChangeUser( oldUser );
System.out.println( oldUser.toString() );
}
public static void wantChangeUser( User newUser ){
newUser = new User( "新姓名", 18 );
}
複製代碼
假設就是引用傳遞,那麼 newUser 和 main 方法中的 oldUser 就是同一個引用對象,那我在 wantChangeUser 方法中從新 new 了一個 User 實體,並賦值給了 newUser,按照引用傳遞這個說法,我賦值給了參數也就是賦值給了原始變量,那麼當完成賦值操做後,原變量 oldUser 就應該是 name = "新名字"、age=18 纔對。
而後,咱們運行看看輸出結果:
User{name='原始姓名', age=8}
User{name='原始姓名', age=8}
複製代碼
結果依然是修改前的值,咱們修改了 newUser ,並無影響到原變量,顯然不是引用傳遞。
Java 中的參數傳遞是值傳遞,而且 Java 中沒有引用傳遞這個概念。咱們一般說的引用傳遞,通常都是從 C 語言和 C like 而來,由於它們有指針的概念。
而咱們也知道,C、C++ 中須要程序員本身管理內存,而指針的使用常常會致使內存泄漏一類的問題,Java 千辛萬苦的就是爲了讓程序員解放出來,而使用垃圾收集策略管理內存,這其中很重要的一點就是規避了指針的使用,因此在 Java 的世界中沒有所謂的指針傳遞。
人在江湖,各位捧個贊場,輕輕點一下贊吧 👍