這一次链蕊,徹底解決Java的值傳遞和引用傳遞

QJ8821827165.jpg

本文旨在用最通俗的語(yǔ)言講述最枯燥的基本知識(shí)

學(xué)過Java基礎(chǔ)的人都知道:值傳遞和引用傳遞是初次接觸Java時(shí)的一個(gè)難點(diǎn)弥喉,有時(shí)候記得了語(yǔ)法卻記不得怎么實(shí)際運(yùn)用,有時(shí)候會(huì)的了運(yùn)用卻解釋不出原理橄碾,而且坊間討論的話題又是充滿爭(zhēng)議:有的論壇帖子說Java只有值傳遞,有的博客說兩者皆有颠锉;這讓人有點(diǎn)摸不著頭腦法牲,下面我們就這個(gè)話題做一些探討,對(duì)書籍琼掠、對(duì)論壇博客的說法拒垃,做一次考證,以得出信得過的答案瓷蛙。

其實(shí)悼瓮,對(duì)于值傳遞和引用傳遞的語(yǔ)法和運(yùn)用,百度一下艰猬,就能出來可觀的解釋和例子數(shù)目横堡,或許你看一下例子好像就懂,但是當(dāng)你參加面試冠桃,做一道這個(gè)知識(shí)點(diǎn)的筆試題時(shí)感覺自己會(huì)命贴,胸有成熟的寫了答案,卻發(fā)現(xiàn)是錯(cuò)的,或者是你根本不會(huì)做胸蛛。

是什么原因培己?

那是因?yàn)槟銓?duì)知識(shí)點(diǎn)沒有了解透徹,只知道其皮毛胚泌。要熟讀一個(gè)語(yǔ)法很簡(jiǎn)單省咨,要理解一行代碼也不難,但是能把學(xué)過的知識(shí)融會(huì)貫通玷室,串聯(lián)起來理解零蓉,那就是非常難了,在此穷缤,關(guān)于值傳遞和引用傳遞敌蜂,小編會(huì)從以前學(xué)過的基礎(chǔ)知識(shí)開始,從內(nèi)存模型開始津肛,一步步的引出值傳遞和引用傳遞的本質(zhì)原理章喉,故篇幅較長(zhǎng),知識(shí)點(diǎn)較多身坐,望讀者多有包涵秸脱。

1. 形參與實(shí)參

我們先來重溫一組語(yǔ)法:

  1. 形參:方法被調(diào)用時(shí)需要傳遞進(jìn)來的參數(shù),如:func(int a)中的a部蛇,它只有在func被調(diào)用期間a才有意義摊唇,也就是會(huì)被分配內(nèi)存空間,在方法func執(zhí)行完成后涯鲁,a就會(huì)被銷毀釋放空間巷查,也就是不存在了
  2. 實(shí)參:方法被調(diào)用時(shí)是傳入的實(shí)際值,它在方法被調(diào)用前就已經(jīng)被初始化并且在方法被調(diào)用時(shí)傳入抹腿。

舉個(gè)栗子:

public static void func(int a){
 a=20;
 System.out.println(a);
}
public static void main(String[] args) {
 int a=10;//實(shí)參
 func(a);
}

例子中
int a=10;中的a在被調(diào)用之前就已經(jīng)創(chuàng)建并初始化岛请,在調(diào)用func方法時(shí),他被當(dāng)做參數(shù)傳入警绩,所以這個(gè)a是實(shí)參崇败。
而func(int a)中的a只有在func被調(diào)用時(shí)它的生命周期才開始,而在func調(diào)用結(jié)束之后房蝉,它也隨之被JVM釋放掉僚匆,,所以這個(gè)a是形參搭幻。

2. Java的數(shù)據(jù)類型

所謂數(shù)據(jù)類型,是編程語(yǔ)言中對(duì)內(nèi)存的一種抽象表達(dá)方式逞盆,我們知道程序是由代碼文件和靜態(tài)資源組成檀蹋,在程序被運(yùn)行前,這些代碼存在在硬盤里,程序開始運(yùn)行俯逾,這些代碼會(huì)被轉(zhuǎn)成計(jì)算機(jī)能識(shí)別的內(nèi)容放到內(nèi)存中被執(zhí)行贸桶。
因此

數(shù)據(jù)類型實(shí)質(zhì)上是用來定義編程語(yǔ)言中相同類型的數(shù)據(jù)的存儲(chǔ)形式,也就是決定了如何將代表這些值的位存儲(chǔ)到計(jì)算機(jī)的內(nèi)存中桌肴。

所以皇筛,數(shù)據(jù)在內(nèi)存中的存儲(chǔ),是根據(jù)數(shù)據(jù)類型來劃定存儲(chǔ)形式和存儲(chǔ)位置的坠七。
那么
Java的數(shù)據(jù)類型有哪些水醋?

  1. 基本類型:編程語(yǔ)言中內(nèi)置的最小粒度的數(shù)據(jù)類型。它包括四大類八種類型:

4種整數(shù)類型:byte彪置、short拄踪、int、long
2種浮點(diǎn)數(shù)類型:float拳魁、double
1種字符類型:char
1種布爾類型:boolean

  1. 引用類型:引用也叫句柄惶桐,引用類型,是編程語(yǔ)言中定義的在句柄中存放著實(shí)際內(nèi)容所在地址的地址值的一種數(shù)據(jù)形式潘懊。它主要包括:


接口
數(shù)組

有了數(shù)據(jù)類型姚糊,JVM對(duì)程序數(shù)據(jù)的管理就規(guī)范化了,不同的數(shù)據(jù)類型授舟,它的存儲(chǔ)形式和位置是不一樣的叛拷,要想知道JVM是怎么存儲(chǔ)各種類型的數(shù)據(jù),就得先了解JVM的內(nèi)存劃分以及每部分的職能岂却。

3.JVM內(nèi)存的劃分及職能

Java語(yǔ)言本身是不能操作內(nèi)存的忿薇,它的一切都是交給JVM來管理和控制的,因此Java內(nèi)存區(qū)域的劃分也就是JVM的區(qū)域劃分躏哩,在說JVM的內(nèi)存劃分之前署浩,我們先來看一下Java程序的執(zhí)行過程,如下圖:


jvm運(yùn)行.png

有圖可以看出:Java代碼被編譯器編譯成字節(jié)碼之后扫尺,JVM開辟一片內(nèi)存空間(也叫運(yùn)行時(shí)數(shù)據(jù)區(qū))筋栋,通過類加載器加到到運(yùn)行時(shí)數(shù)據(jù)區(qū)來存儲(chǔ)程序執(zhí)行期間需要用到的數(shù)據(jù)和相關(guān)信息,在這個(gè)數(shù)據(jù)區(qū)中正驻,它由以下幾部分組成:

1. 虛擬機(jī)棧
2. 堆
3. 程序計(jì)數(shù)器
4. 方法區(qū)
5. 本地方法棧

我們接著來了解一下每部分的原理以及具體用來存儲(chǔ)程序執(zhí)行過程中的哪些數(shù)據(jù)弊攘。


1. 虛擬機(jī)棧

虛擬機(jī)棧是Java方法執(zhí)行的內(nèi)存模型,棧中存放著棧幀姑曙,每個(gè)棧幀分別對(duì)應(yīng)一個(gè)被調(diào)用的方法襟交,方法的調(diào)用過程對(duì)應(yīng)棧幀在虛擬機(jī)中入棧到出棧的過程。

棧是線程私有的伤靠,也就是線程之間的棧是隔離的捣域;當(dāng)程序中某個(gè)線程開始執(zhí)行一個(gè)方法時(shí)就會(huì)相應(yīng)的創(chuàng)建一個(gè)棧幀并且入棧(位于棧頂),在方法結(jié)束后,棧幀出棧焕梅。

下圖表示了一個(gè)Java棧的模型以及棧幀的組成:
[圖片上傳失敗...(image-286d99-1540275071183)]
棧幀:是用于支持虛擬機(jī)進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)迹鹅,它是虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)中的虛擬機(jī)棧的棧元素。

每個(gè)棧幀中包括:

  1. 局部變量表:用來存儲(chǔ)方法中的局部變量(非靜態(tài)變量贞言、函數(shù)形參)斜棚。當(dāng)變量為基本數(shù)據(jù)類型時(shí),直接存儲(chǔ)值该窗,當(dāng)變量為引用類型時(shí)弟蚀,存儲(chǔ)的是指向具體對(duì)象的引用。
  2. 操作數(shù)棧:Java虛擬機(jī)的解釋執(zhí)行引擎被稱為"基于棧的執(zhí)行引擎"挪捕,其中所指的棧就是指操作數(shù)棧粗梭。
  3. 指向運(yùn)行時(shí)常量池的引用:存儲(chǔ)程序執(zhí)行時(shí)可能用到常量的引用。
  4. 方法返回地址:存儲(chǔ)方法執(zhí)行完成后的返回地址级零。

2. 堆:

堆是用來存儲(chǔ)對(duì)象本身和數(shù)組的断医,在JVM中只有一個(gè)堆,因此奏纪,堆是被所有線程共享的鉴嗤。


3. 方法區(qū):

方法區(qū)是一塊所有線程共享的內(nèi)存邏輯區(qū)域,在JVM中只有一個(gè)方法區(qū)序调,用來存儲(chǔ)一些線程可共享的內(nèi)容醉锅,它是線程安全的,多個(gè)線程同時(shí)訪問方法區(qū)中同一個(gè)內(nèi)容時(shí)发绢,只能有一個(gè)線程裝載該數(shù)據(jù)硬耍,其它線程只能等待。

方法區(qū)可存儲(chǔ)的內(nèi)容有:類的全路徑名边酒、類的直接超類的權(quán)全限定名经柴、類的訪問修飾符、類的類型(類或接口)墩朦、類的直接接口全限定名的有序列表坯认、常量池(字段,方法信息氓涣,靜態(tài)變量牛哺,類型引用(class))等。


4. 本地方法棧:

本地方法棧的功能和虛擬機(jī)棧是基本一致的劳吠,并且也是線程私有的引润,它們的區(qū)別在于虛擬機(jī)棧是為執(zhí)行Java方法服務(wù)的,而本地方法棧是為執(zhí)行本地方法服務(wù)的赴背。

有人會(huì)疑惑:什么是本地方法椰拒?為什么Java還要調(diào)用本地方法晶渠?


5. 程序計(jì)數(shù)器:

線程私有的凰荚。
記錄著當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器燃观,在程序運(yùn)行過程中,字節(jié)碼解釋器工作時(shí)就是通過改變這個(gè)計(jì)數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令便瑟,分支缆毁、循環(huán)、異常處理到涂、線程恢復(fù)等基礎(chǔ)功能都需要依賴計(jì)數(shù)器完成脊框。


4. 數(shù)據(jù)如何在內(nèi)存中存儲(chǔ)?

從上面程序運(yùn)行圖我們可以看到践啄,JVM在程序運(yùn)行時(shí)的內(nèi)存分配有三個(gè)地方:

  • 靜態(tài)方法區(qū)
  • 常量區(qū)

相應(yīng)地浇雹,每個(gè)存儲(chǔ)區(qū)域都有自己的內(nèi)存分配策略:

  • 堆式:
  • 棧式
  • 靜態(tài)

我們已經(jīng)知道:Java中的數(shù)據(jù)類型有基本數(shù)據(jù)類型和引用數(shù)據(jù)類型,那么這些數(shù)據(jù)的存儲(chǔ)都使用哪一種策略呢屿讽?
這里要分以下的情況進(jìn)行探究:

1. 基本數(shù)據(jù)類型的存儲(chǔ):

  • A. 基本數(shù)據(jù)類型的局部變量
  • B. 基本數(shù)據(jù)類型的成員變量
  • C. 基本數(shù)據(jù)類型的靜態(tài)變量

2. 引用數(shù)據(jù)類型的存儲(chǔ)


1. 基本數(shù)據(jù)類型的存儲(chǔ)


我們分別來研究一下:

A.基本數(shù)據(jù)類型的局部變量
  1. 定義基本數(shù)據(jù)類型的局部變量以及數(shù)據(jù)都是直接存儲(chǔ)在內(nèi)存中的棧上昭灵,也就是前面說到的“虛擬機(jī)棧”伐谈,數(shù)據(jù)本身的值就是存儲(chǔ)在椑猛辏空間里面。
    虛擬機(jī)棧.png

如上圖诵棵,在方法內(nèi)定義的變量直接存儲(chǔ)在棧中抠蚣,如

int age=50;
int weight=50;
int grade=6;

當(dāng)我們寫“int age=50;”履澳,其實(shí)是分為兩步的:

int age;//定義變量
age=50;//賦值

首先JVM創(chuàng)建一個(gè)名為age的變量嘶窄,存于局部變量表中,然后去棧中查找是否存在有字面量值為50的內(nèi)容距贷,如果有就直接把a(bǔ)ge指向這個(gè)地址柄冲,如果沒有,JVM會(huì)在棧中開辟一塊空間來存儲(chǔ)“50”這個(gè)內(nèi)容储耐,并且把a(bǔ)ge指向這個(gè)地址羊初。因此我們可以知道:
我們聲明并初始化基本數(shù)據(jù)類型的局部變量時(shí),變量名以及字面量值都是存儲(chǔ)在棧中什湘,而且是真實(shí)的內(nèi)容长赞。

我們?cè)賮砜础癷nt weight=50;”闽撤,按照剛才的思路:字面量為50的內(nèi)容在棧中已經(jīng)存在得哆,因此weight是直接指向這個(gè)地址的。由此可見:棧中的數(shù)據(jù)在當(dāng)前線程下是共享的哟旗。

那么如果再執(zhí)行下面的代碼呢贩据?

weight=40栋操;

當(dāng)代碼中重新給weight變量進(jìn)行賦值時(shí),JVM會(huì)去棧中尋找字面量為40的內(nèi)容饱亮,發(fā)現(xiàn)沒有矾芙,就會(huì)開辟一塊內(nèi)存空間存儲(chǔ)40這個(gè)內(nèi)容,并且把weight指向這個(gè)地址近上。由此可知:

基本數(shù)據(jù)類型的數(shù)據(jù)本身是不會(huì)改變的剔宪,當(dāng)局部變量重新賦值時(shí),并不是在內(nèi)存中改變字面量?jī)?nèi)容壹无,而是重新在棧中尋找已存在的相同的數(shù)據(jù)葱绒,若棧中不存在,則重新開辟內(nèi)存存新數(shù)據(jù)斗锭,并且把要重新賦值的局部變量的引用指向新數(shù)據(jù)所在地址地淀。


B. 基本數(shù)據(jù)類型的成員變量

成員變量:顧名思義,就是在類體中定義的變量岖是。
看下圖:


JVM堆內(nèi)存.png

我們看per的地址指向的是堆內(nèi)存中的一塊區(qū)域帮毁,我們來還原一下代碼:

public class Person{
  private int age;
  private String name;
  private int grade;
//篇幅較長(zhǎng),省略setter getter方法
  static void run(){
     System.out.println("run...."); 
   };
}

//調(diào)用
Person per=new Person();

同樣是局部變量的age璧微、name作箍、grade卻被存儲(chǔ)到了堆中為per對(duì)象開辟的一塊空間中。因此可知:基本數(shù)據(jù)類型的成員變量名和值都存儲(chǔ)于堆中前硫,其生命周期和對(duì)象的是一致的胞得。


C. 基本數(shù)據(jù)類型的靜態(tài)變量

前面提到方法區(qū)用來存儲(chǔ)一些共享數(shù)據(jù),因此基本數(shù)據(jù)類型的靜態(tài)變量名以及值存儲(chǔ)于方法區(qū)的運(yùn)行時(shí)常量池中屹电,靜態(tài)變量隨類加載而加載阶剑,隨類消失而消失


2. 引用數(shù)據(jù)類型的存儲(chǔ):

上面提到:堆是用來存儲(chǔ)對(duì)象本身和數(shù)組,而引用(句柄)存放的是實(shí)際內(nèi)容的地址值危号,因此通過上面的程序運(yùn)行圖牧愁,也可以看出,當(dāng)我們定義一個(gè)對(duì)象時(shí)

Person per=new Person();

實(shí)際上外莲,它也是有兩個(gè)過程:

Person per;//定義變量
per=new Person();//賦值

在執(zhí)行Person per;時(shí)猪半,JVM先在虛擬機(jī)棧中的變量表中開辟一塊內(nèi)存存放per變量,在執(zhí)行per=new Person()時(shí)偷线,JVM會(huì)創(chuàng)建一個(gè)Person類的實(shí)例對(duì)象并在堆中開辟一塊內(nèi)存存儲(chǔ)這個(gè)實(shí)例磨确,同時(shí)把實(shí)例的地址值賦值給per變量。因此可見:
對(duì)于引用數(shù)據(jù)類型的對(duì)象/數(shù)組声邦,變量名存在棧中乏奥,變量值存儲(chǔ)的是對(duì)象的地址,并不是對(duì)象的實(shí)際內(nèi)容亥曹。

6. 值傳遞和引用傳遞

前面已經(jīng)介紹過形參和實(shí)參邓了,也介紹了數(shù)據(jù)類型以及數(shù)據(jù)在內(nèi)存中的存儲(chǔ)形式恨诱,接下來,就是文章的主題:值傳遞和引用的傳遞骗炉。

值傳遞:
在方法被調(diào)用時(shí)照宝,實(shí)參通過形參把它的內(nèi)容副本傳入方法內(nèi)部,此時(shí)形參接收到的內(nèi)容是實(shí)參值的一個(gè)拷貝痕鳍,因此在方法內(nèi)對(duì)形參的任何操作硫豆,都僅僅是對(duì)這個(gè)副本的操作龙巨,不影響原始值的內(nèi)容笼呆。

來看個(gè)例子:

public static void valueCrossTest(int age,float weight){
    System.out.println("傳入的age:"+age);
    System.out.println("傳入的weight:"+weight);
    age=33;
    weight=89.5f;
    System.out.println("方法內(nèi)重新賦值后的age:"+age);
    System.out.println("方法內(nèi)重新賦值后的weight:"+weight);
    }

//測(cè)試
public static void main(String[] args) {
        int a=25;
        float w=77.5f;
        valueCrossTest(a,w);
        System.out.println("方法執(zhí)行后的age:"+a);
        System.out.println("方法執(zhí)行后的weight:"+w);
}

輸出結(jié)果:

傳入的age:25
傳入的weight:77.5

方法內(nèi)重新賦值后的age:33
方法內(nèi)重新賦值后的weight:89.5

方法執(zhí)行后的age:25
方法執(zhí)行后的weight:77.5

從上面的打印結(jié)果可以看到:
a和w作為實(shí)參傳入valueCrossTest之后,無論在方法內(nèi)做了什么操作旨别,最終a和w都沒變化诗赌。

這是什么造型呢?=粘凇铭若!

下面我們根據(jù)上面學(xué)到的知識(shí)點(diǎn),進(jìn)行詳細(xì)的分析:

首先程序運(yùn)行時(shí)递览,調(diào)用mian()方法叼屠,此時(shí)JVM為main()方法往虛擬機(jī)棧中壓入一個(gè)棧幀,即為當(dāng)前棧幀绞铃,用來存放main()中的局部變量表(包括參數(shù))镜雨、操作棧、方法出口等信息儿捧,如a和w都是mian()方法中的局部變量荚坞,因此可以斷定,a和w是躺著mian方法所在的棧幀中
如圖:


JVM-main所在棧.png

而當(dāng)執(zhí)行到valueCrossTest()方法時(shí)菲盾,JVM也為其往虛擬機(jī)棧中壓入一個(gè)棧颓影,即為當(dāng)前棧幀,用來存放valueCrossTest()中的局部變量等信息懒鉴,因此age和weight是躺著valueCrossTest方法所在的棧幀中诡挂,而他們的值是從a和w的值copy了一份副本而得,如圖:


JVM_方法棧幀.png

因而可以a和age临谱、w和weight對(duì)應(yīng)的內(nèi)容是不一致的璃俗,所以當(dāng)在方法內(nèi)重新賦值時(shí),實(shí)際流程如圖:


JVM_重新賦值.png

也就是說吴裤,age和weight的改動(dòng)旧找,只是改變了當(dāng)前棧幀(valueCrossTest方法所在棧幀)里的內(nèi)容,當(dāng)方法執(zhí)行結(jié)束之后麦牺,這些局部變量都會(huì)被銷毀钮蛛,mian方法所在棧幀重新回到棧頂鞭缭,成為當(dāng)前棧幀,再次輸出a和w時(shí)魏颓,依然是初始化時(shí)的內(nèi)容岭辣。
因此:
值傳遞傳遞的是真實(shí)內(nèi)容的一個(gè)副本,對(duì)副本的操作不影響原內(nèi)容甸饱,也就是形參怎么變化沦童,不會(huì)影響實(shí)參對(duì)應(yīng)的內(nèi)容。

引用傳遞:
”引用”也就是指向真實(shí)內(nèi)容的地址值叹话,在方法調(diào)用時(shí)偷遗,實(shí)參的地址通過方法調(diào)用被傳遞給相應(yīng)的形參,在方法體內(nèi)驼壶,形參和實(shí)參指向通愉快內(nèi)存地址氏豌,對(duì)形參的操作會(huì)影響的真實(shí)內(nèi)容。

舉個(gè)栗子:
先定義一個(gè)對(duì)象:

public class Person {
        private String name;
        private int 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;
        }
}

我們寫個(gè)函數(shù)測(cè)試一下:

public static void PersonCrossTest(Person person){
        System.out.println("傳入的person的name:"+person.getName());
        person.setName("我是張小龍");
        System.out.println("方法內(nèi)重新賦值后的name:"+person.getName());
    }
//測(cè)試
public static void main(String[] args) {
        Person p=new Person();
        p.setName("我是馬化騰");
        p.setAge(45);
        PersonCrossTest(p);
        System.out.println("方法執(zhí)行后的name:"+p.getName());
}

輸出結(jié)果:

傳入的person的name:我是馬化騰
方法內(nèi)重新賦值后的name:我是張小龍
方法執(zhí)行后的name:我是張小龍

可以看出热凹,person經(jīng)過personCrossTest()方法的執(zhí)行之后泵喘,內(nèi)容發(fā)生了改變,這印證了上面所說的“引用傳遞”般妙,對(duì)形參的操作纪铺,改變了實(shí)際對(duì)象的內(nèi)容。

那么碟渺,到這里就結(jié)題了嗎鲜锚?
不是的,沒那么簡(jiǎn)單止状,
能看得到想要的效果
是因?yàn)閯偤眠x對(duì)了例子而已E朊蕖!怯疤!

下面我們對(duì)上面的例子稍作修改浆洗,加上一行代碼,

public static void PersonCrossTest(Person person){
        System.out.println("傳入的person的name:"+person.getName());
        person=new Person();//加多此行代碼
        person.setName("我是張小龍");
        System.out.println("方法內(nèi)重新賦值后的name:"+person.getName());
    }

輸出結(jié)果:

傳入的person的name:我是馬化騰
方法內(nèi)重新賦值后的name:我是張小龍
方法執(zhí)行后的name:我是馬化騰

為什么這次的輸出和上次的不一樣了呢集峦?
看出什么問題了嗎伏社?

按照上面講到JVM內(nèi)存模型可以知道,對(duì)象和數(shù)組是存儲(chǔ)在Java堆區(qū)的塔淤,而且堆區(qū)是共享的摘昌,因此程序執(zhí)行到main()方法中的下列代碼時(shí)

Person p=new Person();
        p.setName("我是馬化騰");
        p.setAge(45);
        PersonCrossTest(p);

JVM會(huì)在堆內(nèi)開辟一塊內(nèi)存,用來存儲(chǔ)p對(duì)象的所有內(nèi)容高蜂,同時(shí)在main()方法所在線程的棧區(qū)中創(chuàng)建一個(gè)引用p存儲(chǔ)堆區(qū)中p對(duì)象的真實(shí)地址聪黎,如圖:
[圖片上傳失敗...(image-421e9f-1540275071183)]
當(dāng)執(zhí)行到PersonCrossTest()方法時(shí),因?yàn)榉椒▋?nèi)有這么一行代碼:

person=new Person();

JVM需要在堆內(nèi)另外開辟一塊內(nèi)存來存儲(chǔ)new Person()备恤,假如地址為“xo3333”稿饰,那此時(shí)形參person指向了這個(gè)地址锦秒,假如真的是引用傳遞,那么由上面講到:引用傳遞中形參實(shí)參指向同一個(gè)對(duì)象喉镰,形參的操作會(huì)改變實(shí)參對(duì)象的改變旅择。

可以推出:實(shí)參也應(yīng)該指向了新創(chuàng)建的person對(duì)象的地址,所以在執(zhí)行PersonCrossTest()結(jié)束之后侣姆,最終輸出的應(yīng)該是后面創(chuàng)建的對(duì)象內(nèi)容生真。

然而實(shí)際上,最終的輸出結(jié)果卻跟我們推測(cè)的不一樣捺宗,最終輸出的仍然是一開始創(chuàng)建的對(duì)象的內(nèi)容柱蟀。

由此可見:引用傳遞,在Java中并不存在偿凭。

但是有人會(huì)疑問:為什么第一個(gè)例子中产弹,在方法內(nèi)修改了形參的內(nèi)容,會(huì)導(dǎo)致原始對(duì)象的內(nèi)容發(fā)生改變呢弯囊?

這是因?yàn)椋?strong>無論是基本類型和是引用類型,在實(shí)參傳入形參時(shí)胶果,都是值傳遞匾嘱,也就是說傳遞的都是一個(gè)副本,而不是內(nèi)容本身早抠。

JVM_副本.png

有圖可以看出霎烙,方法內(nèi)的形參person和實(shí)參p并無實(shí)質(zhì)關(guān)聯(lián),它只是由p處copy了一份指向?qū)ο蟮牡刂啡锪藭r(shí):

p和person都是指向同一個(gè)對(duì)象悬垃。

因此在第一個(gè)例子中,對(duì)形參p的操作甘苍,會(huì)影響到實(shí)參對(duì)應(yīng)的對(duì)象內(nèi)容尝蠕。而在第二個(gè)例子中,當(dāng)執(zhí)行到new Person()之后载庭,JVM在堆內(nèi)開辟一塊空間存儲(chǔ)新對(duì)象看彼,并且把person改成指向新對(duì)象的地址,此時(shí):

p依舊是指向舊的對(duì)象囚聚,person指向新對(duì)象的地址靖榕。

所以此時(shí)對(duì)person的操作,實(shí)際上是對(duì)新對(duì)象的操作顽铸,于實(shí)參p中對(duì)應(yīng)的對(duì)象毫無關(guān)系茁计。

結(jié)語(yǔ)

因此可見:在Java中所有的參數(shù)傳遞,不管基本類型還是引用類型谓松,都是值傳遞星压,或者說是副本傳遞瓶蝴。
只是在傳遞過程中:

如果是對(duì)基本數(shù)據(jù)類型的數(shù)據(jù)進(jìn)行操作,由于原始內(nèi)容和副本都是存儲(chǔ)實(shí)際值租幕,并且是在不同的棧區(qū)舷手,因此形參的操作,不影響原始內(nèi)容劲绪。

如果是對(duì)引用類型的數(shù)據(jù)進(jìn)行操作男窟,分兩種情況,一種是形參和實(shí)參保持指向同一個(gè)對(duì)象地址贾富,則形參的操作歉眷,會(huì)影響實(shí)參指向的對(duì)象的內(nèi)容。一種是形參被改動(dòng)指向新的對(duì)象地址(如重新賦值引用)颤枪,則形參的操作汗捡,不會(huì)影響實(shí)參指向的對(duì)象的內(nèi)容。

以上為小編關(guān)于“值傳遞和引用傳遞”問題的思考和論證畏纲,對(duì)于這個(gè)問題扇住,歷來都是多有爭(zhēng)論,在此希望和讀者一起探討和學(xué)習(xí)盗胀,有不同意見或者建議請(qǐng)假小編微信:sisi-ceo艘蹋。理性評(píng)論,不喜勿噴票灰。


覺得本文對(duì)你有幫助女阀?請(qǐng)分享給更多人
關(guān)注「編程無界」,提升裝逼技能

image

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屑迂,一起剝皮案震驚了整個(gè)濱河市浸策,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌惹盼,老刑警劉巖庸汗,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異逻锐,居然都是意外死亡夫晌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門昧诱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晓淀,“玉大人,你說我怎么就攤上這事盏档⌒钻” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)懦窘。 經(jīng)常有香客問我前翎,道長(zhǎng),這世上最難降的妖魔是什么畅涂? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任港华,我火速辦了婚禮,結(jié)果婚禮上午衰,老公的妹妹穿的比我還像新娘立宜。我一直安慰自己,他們只是感情好臊岸,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布橙数。 她就那樣靜靜地躺著,像睡著了一般帅戒。 火紅的嫁衣襯著肌膚如雪灯帮。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天逻住,我揣著相機(jī)與錄音钟哥,去河邊找鬼。 笑死鄙信,一個(gè)胖子當(dāng)著我的面吹牛瞪醋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播装诡,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼践盼!你這毒婦竟也來了鸦采?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤咕幻,失蹤者是張志新(化名)和其女友劉穎渔伯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肄程,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡锣吼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蓝厌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片玄叠。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拓提,靈堂內(nèi)的尸體忽然破棺而出读恃,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布寺惫,位于F島的核電站疹吃,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏西雀。R本人自食惡果不足惜萨驶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望艇肴。 院中可真熱鬧腔呜,春花似錦、人聲如沸豆挽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)帮哈。三九已至膛檀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間娘侍,已是汗流浹背咖刃。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留憾筏,地道東北人嚎杨。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像氧腰,于是被迫代替她去往敵國(guó)和親枫浙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355