JVM(五)值傳遞還是引用傳遞?

1.對(duì)象的創(chuàng)建

1.遇到new指令時(shí),首先檢查這個(gè)指令的參數(shù)是否能在常量池中定位到一個(gè)類的符號(hào)引用,并且檢查這個(gè)符號(hào)引用代表的類是否已經(jīng)被加載、解析和初始化過(guò)。如果沒有,執(zhí)行相應(yīng)的類加載。
2.類加載檢查通過(guò)之后,為新對(duì)象分配內(nèi)存(內(nèi)存大小在類加載完成后便可確認(rèn))。在堆的空閑內(nèi)存中劃分一塊區(qū)域(指針碰撞或空閑列表的分配方式)

指針碰撞:Java堆是規(guī)整的,所有用過(guò)的內(nèi)存放在一邊,空閑的放在另一邊,中間放著一個(gè)指針作為分界點(diǎn)的指示器。分配內(nèi)存只是把指針向空閑空間挪動(dòng)與對(duì)象大小相等的距離。這種分配稱為“指針碰撞”。

空閑列表:Java堆是不規(guī)整的,用過(guò)的內(nèi)存和空閑的內(nèi)存相互交錯(cuò)。那就沒辦法進(jìn)行指針碰撞。虛擬機(jī)通過(guò)維護(hù)一個(gè)列表,記錄那些內(nèi)存塊是可用的,在分配時(shí)找出一塊足夠大的空間分配給對(duì)象實(shí)例,并更新表上記錄。這種分配方式稱為“空閑列表”。

使用哪種分配方式由Java堆是否規(guī)整決定,Java堆規(guī)整不規(guī)整由垃圾收集器是否算法或者是否帶有壓縮整理功能決定。

分配對(duì)象保證線程安全的做法:虛擬機(jī)使用CAS失敗重試的機(jī)制保證更新操作的原子性。

3.每個(gè)線程在堆中都會(huì)有私有的分配緩沖區(qū)(TLAB),這樣可以很大程度避免在并發(fā)情況下頻繁創(chuàng)建 對(duì)象造成的線程不安全。
4.內(nèi)存空間分配完成后,會(huì)初始化0(不包括對(duì)象頭)
5.接下來(lái)就是填充對(duì)象頭,把對(duì)象是哪個(gè)類的實(shí)例、如何才能找到類的元數(shù)據(jù)信息,對(duì)象的哈希碼,對(duì)象的GC分代年齡等信息存入對(duì)象頭中。
6.執(zhí)行<init>方法,把對(duì)象按程序員的意愿進(jìn)行初始化。執(zhí)行完init方法后才算一份真正可用的對(duì)象創(chuàng)建完成。

2.對(duì)象內(nèi)存布局

java對(duì)象:對(duì)象頭+實(shí)例數(shù)據(jù)+對(duì)齊填充
對(duì)象頭:MarkWord (8字節(jié))+ 類指針(8字節(jié) 開啟壓縮4字節(jié)) + 數(shù)組對(duì)象長(zhǎng)度數(shù)據(jù)(4字節(jié))
實(shí)例數(shù)據(jù):基礎(chǔ)類型占實(shí)際大小,引用類型8字節(jié)(開啟壓縮4字節(jié))
對(duì)齊填充:填充到8的倍數(shù)。


3.對(duì)象的訪問(wèn)定位

一般來(lái)說(shuō),一個(gè)Java的引用訪問(wèn)涉及到3個(gè)內(nèi)存區(qū)域,JVM棧、堆、方法區(qū)

以最簡(jiǎn)單的本地變量引用為例
Object obj = new Object();
Object obj表示一個(gè)本地引用,存儲(chǔ)在JVM棧的本地變量表中,表示一個(gè)reference類型數(shù)據(jù)。
new Object() 作為實(shí)例對(duì)象數(shù)據(jù)存儲(chǔ)在堆中。
堆中還記錄了能夠查詢到此Object對(duì)象的類型數(shù)據(jù)(接口、方法、field、對(duì)象類型等)的地址,實(shí)際的數(shù)據(jù)則存儲(chǔ)在方法區(qū)中。

在Java虛擬機(jī)規(guī)范中,只規(guī)定了指向?qū)ο蟮囊?,?duì)于reference類型引用訪問(wèn)具體對(duì)象的方式并未做規(guī)定。不過(guò)目前主流的有兩種:通過(guò)句柄飯顧問(wèn),使用直接指針訪問(wèn)。

    1. 通過(guò)句柄訪問(wèn)

通過(guò)句柄訪問(wèn)的實(shí)現(xiàn)方式中,JVM堆中會(huì)劃分單獨(dú)一塊內(nèi)存區(qū)域作為句柄池,句柄池中存儲(chǔ)了對(duì)象實(shí)例數(shù)據(jù)(在堆中)和對(duì)象類型數(shù)據(jù)(在方法區(qū)中)的指針,這種實(shí)現(xiàn)方式由于用句柄表示地址,因此十分穩(wěn)定。Java堆中會(huì)分配一塊內(nèi)存作為句柄池。reference存儲(chǔ)的是句柄地址。詳情見圖。


通過(guò)句柄訪問(wèn)對(duì)象
  • 2 使用直接指針訪問(wèn)
    通過(guò)直接指針訪問(wèn)的實(shí)現(xiàn)方式中,reference中存儲(chǔ)的就是對(duì)象在堆中的實(shí)際地址,在堆中存儲(chǔ)的對(duì)象信息包含了在方法區(qū)中相應(yīng)類型數(shù)據(jù)。這種方法最大的優(yōu)勢(shì)就是速度快,HotSpot虛擬機(jī)中使用的就是這種方式。


    通過(guò)直接指針訪問(wèn)對(duì)象

比較
使用句柄的最大好處是reference中存儲(chǔ)的是穩(wěn)定的句柄地址,在對(duì)象移動(dòng)(GC)是只改變實(shí)例數(shù)據(jù)指針地址,reference不需要修改。
直接指針好處就是速度快,節(jié)省了一次指針定位的開銷。
如果是對(duì)象頻繁GC那么句柄訪問(wèn)好,如果是對(duì)象頻繁訪問(wèn)那么是直接指針訪問(wèn)好。


4.方法執(zhí)行過(guò)程

public class Test {
    public static void main (String args[]) {
        Student stu = new Student();
        stu.setName("John");
        System.out.println(stu);
    }
}

1.通過(guò)java.exe 運(yùn)行Test.class,Test.class文件會(huì)被ApplassLoader加載器(雙親委派,啟動(dòng)類加載器和擴(kuò)展類加載器都不會(huì)加載它)加載到JVM中,元空間存儲(chǔ)著類信息(類名,方法信息,字段信息)
2.然后JVM找到Test的主函數(shù)入口(main),為main函數(shù)建立棧幀,開始執(zhí)行main函數(shù)。
3.main函數(shù)的第一條命令是Student stu = new Student();就是讓JVM創(chuàng)建一個(gè)Student對(duì)象,但是這時(shí)候方法區(qū)中沒有Student類信息,所以JVM會(huì)馬上加載Student類,把Student類的類型信息放到方法區(qū)中。
4.加載完Stundent類之后,Java在堆區(qū)中為一個(gè)新的Student分配內(nèi)存,然后調(diào)用構(gòu)造函數(shù)初始化Student實(shí)例,這個(gè)Student實(shí)例持有者指向方法區(qū)Student類的類型信息的引用。
5.當(dāng)使用stu.setName("John")的時(shí)候,JVM根據(jù)引用找到Student對(duì)象持有的引用定位到方法區(qū)中Student類的類型信息的方法表,獲得setName函數(shù)的字節(jié)碼地址。
為setName函數(shù)創(chuàng)建棧幀,開始運(yùn)行setName函數(shù)。

5.HotSpot的GC算法實(shí)現(xiàn)

1.HotSpot怎么快速找到GC Root?

HotSpot使用一組稱為OopMap的數(shù)據(jù)結(jié)構(gòu)。引用會(huì)記錄在這個(gè)數(shù)據(jù)結(jié)構(gòu)上。
在類加載完成后,HotSpot就把對(duì)象內(nèi)什么偏移量上是什么類型的數(shù)據(jù)計(jì)算出來(lái)
在JIT編譯過(guò)程中,也會(huì)在棧和寄存器中哪些位置是引用。
這樣子,在GC掃描的時(shí)候,就可以直接知道哪些是可達(dá)對(duì)象了。

2.安全點(diǎn)

hotspot只在特定的位置生成OopMap,這些位置稱為安全點(diǎn)。
程序執(zhí)行過(guò)程中并非所有的地方都可以停下來(lái)開始GC,只有在到達(dá)安全點(diǎn)才可以暫停。
安全點(diǎn)的選定基本上以“是否具有讓程序長(zhǎng)時(shí)間執(zhí)行”的特征選定的。比如說(shuō)方法調(diào)用,循環(huán)跳轉(zhuǎn),異常跳轉(zhuǎn)等。具有這些功能的指令才會(huì)產(chǎn)生Safepoint。

3.中斷方式

搶占式中斷:在GC發(fā)生時(shí),首先把所有線程中斷,如果發(fā)現(xiàn)有線程不在安全點(diǎn)上,就恢復(fù)線程,讓它跑到安全點(diǎn)上。
主動(dòng)式中斷:GC需要中斷線程時(shí),不直接對(duì)線程操作,僅僅設(shè)置一個(gè)標(biāo)志,各個(gè)線程執(zhí)行時(shí)主動(dòng)去輪詢這個(gè)標(biāo)志,當(dāng)發(fā)現(xiàn)中斷標(biāo)記為true時(shí)就自己掛起,輪詢標(biāo)記的地方和安全點(diǎn)是重合的。

4.安全區(qū)域

一段代碼片段中,對(duì)象的引用關(guān)系不會(huì)發(fā)生變化,在這個(gè)區(qū)域中任何地方開始GC都是安全的。
在線程進(jìn)入安全區(qū)域時(shí),他首先標(biāo)記自己已經(jīng)進(jìn)入啊安全語(yǔ)氣,在這段時(shí)間里,當(dāng)JVM發(fā)起GC時(shí),就不用管進(jìn)入安全區(qū)域的線程了。
在線程將要離開安全區(qū)域時(shí),他檢查系統(tǒng)是否完成了GC過(guò)程,如果完成了,就繼續(xù)前行。否則,他就必須等待收到可以離開安全區(qū)域的信號(hào)。

5.GC時(shí)為什么要停頓所有Java線程

因?yàn)镚C需要先進(jìn)行可達(dá)性分析
可達(dá)性分析是判斷GC Root對(duì)象到其他對(duì)象是否可達(dá)
加入分析過(guò)程中對(duì)象引用關(guān)系在不斷變化,分析結(jié)果的準(zhǔn)確性就無(wú)法得到保證。


6.值傳遞引用傳遞

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

例如:

public static void doJob(int a ) {
    a = 20;
    System.out.println(a);
}

public static void main(String[] args){
    int a = 10; //實(shí)參
    doJob(a);
}

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

2.數(shù)據(jù)存儲(chǔ)策略

這里要分情況進(jìn)行探究

  • 1.基本數(shù)據(jù)類型的存儲(chǔ)
    - A.基本數(shù)據(jù)類型的局部變量
    - B.基本數(shù)據(jù)類型的全局變量
    - C.基本數(shù)據(jù)類型的靜態(tài)變量

2.引用數(shù)據(jù)類型的存儲(chǔ)
- A.引用數(shù)據(jù)類型的局部變量
- B.引用數(shù)據(jù)類型的全局變量
- C.引用數(shù)據(jù)類型的靜態(tài)變量

1.基本數(shù)據(jù)類型的局部變量

定義基本數(shù)據(jù)類型的局部變量以及數(shù)據(jù)都是直接存儲(chǔ)在內(nèi)存中的棧上(局部變量表中),也就是運(yùn)行時(shí)數(shù)據(jù)區(qū)的虛擬機(jī)棧上,數(shù)據(jù)本身值就存儲(chǔ)在局部變量表中。


虛擬機(jī)棧-局部變量表

在方法內(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ì)在棧中開辟一塊空間來(lái)存儲(chǔ)"50"這個(gè)內(nèi)容.并且把a(bǔ)ge指向這個(gè)地址。
聲明并初始化基本數(shù)據(jù)類型的局部變量時(shí),變量名以及字面量值都是存儲(chǔ)在棧中,而且是真實(shí)內(nèi)容

我們?cè)賮?lái)看int weight = 50; 按照剛才的思路,字面量為50的內(nèi)容已經(jīng)在棧中存在了,因此weight直接指向這個(gè)地址。由此可見
棧中的數(shù)據(jù)在當(dāng)前線程下是共享的

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

weight = 40;

當(dāng)代碼中重新給weight變量進(jìn)行復(fù)制時(shí),JVM會(huì)去棧中尋找字面量為40的內(nèi)容,發(fā)現(xiàn)沒有,就會(huì)開辟一塊內(nèi)存空間存儲(chǔ)40這個(gè)內(nèi)容,并且把weight只想這個(gè)地址,由此可知:
基本數(shù)據(jù)類型的局部變量,本身是不會(huì)改變的,當(dāng)重新賦值時(shí),并不是在內(nèi)存中改變字面量的內(nèi)容,而是重新尋找已存在的相同數(shù)據(jù),若不存在,則重新開辟內(nèi)存存新數(shù)據(jù)。并將引用指向新數(shù)據(jù)所在的地址


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

成員變量:就是在類體重定義的非靜態(tài)變量


非靜態(tài)成員變量存儲(chǔ)結(jié)構(gòu)

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

public class Person{
  private int age;
  private String name;
  private int grade;

  static void run(){
     System.out.println("run...."); 
   };
}

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

成員變量age、name、grade卻被存儲(chǔ)到了堆中為per對(duì)象開辟的一塊內(nèi)存空間。因此可知
基本數(shù)據(jù)類型的成員變量名和值都存儲(chǔ)于堆中,其生命周期和對(duì)象是一致的。


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

基本數(shù)據(jù)類型的靜態(tài)變量名以及值存儲(chǔ)在方法區(qū)的運(yùn)行時(shí)常量池中,靜態(tài)變量隨類加載而加載,隨類消失而消失。

4.引用數(shù)據(jù)類型

堆是用來(lái)存儲(chǔ)對(duì)象本身和數(shù)組,而引用存放的是實(shí)際內(nèi)容的地址值,因此當(dāng)我們定義一個(gè)對(duì)象時(shí)

Person per = new Person();

實(shí)際上他也是有兩個(gè)過(guò)程

Person per;
per = new Person();

在執(zhí)行Person per時(shí),JVM先在棧中變量表開辟一塊內(nèi)存存放per變量,在執(zhí)行per= new Person()時(shí),JVM會(huì)創(chuàng)建一個(gè)Person類的實(shí)例對(duì)象并在堆中存儲(chǔ)這個(gè)實(shí)例,同時(shí)把實(shí)例地址賦值給per變量。

引用數(shù)據(jù)類型,變量名都在棧中出現(xiàn),棧中的變量值存儲(chǔ)的是對(duì)象地址,并不是實(shí)際內(nèi)容。

5.值傳遞和引用傳遞
  • 值傳遞:在方法被調(diào)用時(shí),實(shí)參通過(guò)形參把它的內(nèi)容副本傳入方法內(nèi)部,此時(shí)形參接收到的內(nèi)容是實(shí)參值的一個(gè)拷貝,因此在方法內(nèi)對(duì)形參的任何操作,都僅僅是對(duì)這個(gè)副本的操作,不影響原始值的內(nèi)容。
  • 引用傳遞:引用也就是指向真實(shí)內(nèi)容的地址,在方法調(diào)用時(shí),實(shí)參的地址通過(guò)方法調(diào)用被傳遞給相應(yīng)的形參,在方法體內(nèi),形參和實(shí)參指向同一塊內(nèi)存地址,對(duì)形參的操作會(huì)影響實(shí)參的真實(shí)內(nèi)容。

來(lá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之后,無(wú)論在方法內(nèi)做了什么操作,最終a和w都沒變化。

我們對(duì)上面例子進(jìn)行詳細(xì)分析:
首先程序運(yùn)行時(shí),調(diào)用main()方法,此時(shí)JVM為main()方法往棧中壓入一個(gè)棧幀,用來(lái)存儲(chǔ)main()方法中的局部變量表,操作數(shù)棧,方法出口,動(dòng)態(tài)鏈接等,a和w都存在main()方法局部變量表中。

main方法局部變量表

而當(dāng)執(zhí)行到ValueCrossTest()方法時(shí),JVM會(huì)再壓入一個(gè)棧幀,用來(lái)存放ValueCrossTest()中的局部變量等信息,因此age和weight是在valueCrossTest方法的局部變量表中。而它們的值是從a和w的值copy了一份副本而已。


valueCrossTest方法局部變量表

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


valueCrossTest變量重新賦值

也就是說(shuō),age和weight的改動(dòng),只是改變了當(dāng)前棧幀里的內(nèi)容,當(dāng)方法執(zhí)行結(jié)束后,這些局部變量都會(huì)被銷毀,main方法棧幀重新回到棧頂,稱為當(dāng)前棧幀,再次輸出a和w時(shí),依然是初始化時(shí)的內(nèi)容。
值傳遞傳遞的是真實(shí)內(nèi)容的副本,對(duì)副本操作不影響原內(nèi)容,也就是形參怎么變化,不會(huì)影響實(shí)參的內(nèi)容

舉個(gè)例子:

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è)示例測(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結(jié)果personCrossTest()方法的執(zhí)行之后,內(nèi)容發(fā)生了改變,看起來(lái)是引用傳遞的。

下面我們對(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:我是馬化騰

為什么會(huì)不一樣了呢?
程序執(zhí)行到main()方法中下列代碼時(shí)

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

是在main方法棧幀局部變量表中,創(chuàng)建一個(gè)引用p,p的值是地址,是存儲(chǔ)在堆區(qū)中p對(duì)象的真實(shí)地址。


引用結(jié)構(gòu)

當(dāng)執(zhí)行到PersonCrossTest方法時(shí),因?yàn)榉椒▋?nèi)有這么一行代碼:

person=new Person();

JVM需要在堆內(nèi)另外開辟一塊內(nèi)存來(lái)存儲(chǔ)new Person(),加入地址為“xo3333”,那么此時(shí)形參指向了這個(gè)地址,加入真的是引用傳遞,那么由上面講到引用傳遞中形參實(shí)參指向同一對(duì)象,形參的操作會(huì)改變實(shí)參對(duì)象,可以退出。實(shí)參也應(yīng)該指向新創(chuàng)建的person對(duì)象的地址。然而實(shí)際上并不是這樣。由此可見引用傳遞在java中并不存在。

那么為什么第一個(gè)例子中,在方法內(nèi)修改了形參的內(nèi)容,會(huì)導(dǎo)致原始對(duì)象的內(nèi)容發(fā)生改變?
是因?yàn)?strong>無(wú)論是基本類型還是引用類型,在實(shí)參傳入形參時(shí),都是值傳遞,也就是說(shuō)傳遞的都是一個(gè)副本,而不是內(nèi)容本身

修改形參

由圖可以看出,方法實(shí)參和形參并無(wú)實(shí)際關(guān)聯(lián),只是從p處拷貝了一份指向?qū)ο蟮牡刂贰4藭r(shí) p和person都指向堆中同一個(gè)對(duì)象。
在第二個(gè)例子中,當(dāng)執(zhí)行到new Person()之后,JVM在堆內(nèi)開辟一塊空間存儲(chǔ)新對(duì)象,并且把person改成指向新對(duì)象的地址,此時(shí) p依舊指向舊的地址,person則指向了新的地址。

有個(gè)例子很形象的說(shuō)明值傳遞和引用傳遞。
你有一把鑰匙,當(dāng)你的朋友想要去你家的時(shí)候,如果你直接把你的鑰匙給他了,這就是引用傳遞。這種情況下,如果他對(duì)這把鑰匙做了什么事情,比如他在鑰匙上刻下了自己名字,那么這把鑰匙還給你的時(shí)候,你自己的鑰匙上也會(huì)多出他刻的名字。

你有一把鑰匙,當(dāng)你的朋友想要去你家的時(shí)候,你復(fù)制了一把新鑰匙給他,自己的還在自己手里,這就是值傳遞。這種情況下,他對(duì)這把鑰匙做什么都不會(huì)影響你手里的這把鑰匙。

但是,不管上面那種情況,你的朋友拿著你給他的鑰匙,進(jìn)到你的家里,把你家的電視砸了。那你說(shuō)你會(huì)不會(huì)受到影響?而我們?cè)诜椒ㄖ?,改變?duì)象的屬性的值的時(shí)候,不就是在“砸電視”么。


總結(jié)

簡(jiǎn)單來(lái)說(shuō):
如果參數(shù)是基本類型,傳遞的是基本類型的字面量值的拷貝。
如果參數(shù)是引用類型,傳遞的是該參量所引用的對(duì)象在堆中地址值的拷貝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容