值傳遞和引用傳遞

此篇博客只是copy下列博客或文章整理出來的,謝絕轉載,侵權刪除:

https://www.cnblogs.com/dolphin0520/p/3592498.htmlhtml

https://zwmf.iteye.com/blog/1738574java

https://www.cnblogs.com/aspirant/p/10320652.html程序員

https://www.zhihu.com/question/31203609編程


概念

  • 按值調用:一個方法接收的是調用着提供的值(值傳遞)
  • 按引用調用:一個方法接收的是調用者提供的變量地址(若是是C語言的話來講就是指針啦,固然java並無指針的概念)。

若是想搞明白值傳遞和引用傳遞須要搞明白對象對象引用

1、對象對象引用

對象

首先咱們應該明白對象對象引用不是一回事。數組

《Java編程思想》原話說:「按照通俗的說法,每一個對象都是某個類(class)的一個實例(instance),這裏,‘類’就是‘類型’的同義詞。」jvm

從這句話能夠得知:對象類的實例。好比,能夠把人類看作類的實例,那麼具體到某我的的張三他就是對象.編程語言


對象對象引用

再讀《Java編程思想》的一段話:「每種編程語言都有本身的數據處理方式。有些時候,程序員必須注意將要處理的數據是什麼類型。你是直接操縱元素,仍是用某種基於特殊語法的間接表示(例如C/C++裏的指針)來操做對象。全部這些在 Java 裏都獲得了簡化,一切都被視爲對象。所以,咱們可採用一種統一的語法。==儘管將一切都「看做」對象,但操縱的標識符實際是指向一個對象的「引用」(reference)。」==函數

"操縱的標識符實際是指向一個對象的「引用」(reference)"這句話明確的指出引用對象不是一回事。ui

爲了方便說明,定義一個簡單的類:this

class Person {
    
    String name;
    int age;
    
}

有了這個類,就能夠用它來建立對象:

Person person1 = new Person();

一般把這條語句的動做稱之爲建立一個對象,其實,它包含了四個動做。

  • 1)右邊的「new Person」,是以Person類爲模板,在堆空間裏建立一個Person類對象(也簡稱爲Person對象)。
  • 2)末尾的()意味着,在對象建立後,當即調用Person類的構造函數,對剛生成的對象進行初始化。構造函數是確定有的。若是你沒寫,Java會給你補上一個默認的構造函數。
  • 3)左邊的「Person person1」建立了一個Person類的引用變量。所謂Person類引用,就是之後能夠用來指向Person這一類對象對象引用
  • 4)「=」操做符使對象引用指向剛建立的那個Person類的對象

爲了更加清楚瞭解原理,把Person person1 = new Person() 這條語句拆成兩部分:

Person person1;//第一句

person1 = new Person(); //第二句

爲了形象地說明對象、引用及它們之間的關係,能夠作一個或許不很穩當的比喻。對象比如是一隻很大的氣球,大到咱們抓不住它。引用變量是一根繩, 能夠用來系汽球。

若是隻執行了第一條語句,還沒執行第二條,此時建立的引用變量person1還沒指向任何一個對象,它的值是null.(引用變量能夠指向某個對象,或者爲null。)

此時的person1比如一根尚未繫上任何一個汽球的繩1。

執行了第二句後,一隻汽球1作出來了(new一個Person類對象),並被系在person1這根繩上。咱們抓住這根繩,就等於抓住了那隻汽球1

在原來的基礎上增長多幾句代碼:

Person person1;//第一句

person1 = new Person(); //第二句

Person person2; //第三句

person2 = person1; //第四句

person2 = new Person(); //第五句

person1 = person2; //第六句

執行到第三句代碼就又作了一根繩2,還沒繫上汽球。

執行第四句代碼,這裏發生了複製行爲,名爲person1的對象引用複製給了person2(要說明的是,對象自己並無被複制),person2也是對象引用,至關於繩2也系在汽球1上。

執行第五句代碼,則引用變量person2改指向第二個對象,繩2系向新的汽球。

  • 從以上敘述再推演下去,咱們能夠得到如下結論:

    • 1.一個對象引用能夠指向0個或1個對象(一根繩子能夠不繫汽球,也能夠系一個汽球);
    • 2.一個對象能夠有N個引用指向它(能夠有N條繩子繫住一個汽球)。

按上面的推斷,person1也指向了第二個對象。這個沒問題。問題是第一個對象呢?沒有一條繩子繫住它,它飛了。多數書裏說,它被Java的垃圾回收機制回收了。這不確切。正確地說,它已成爲垃圾回收機制的處理對象。至於何時真正被回收,那要看垃圾回收機制的心情了。

由此看來,下面的語句應該不合法吧?至少是沒用的吧?

new Person();

它是合法的,並且可用的。譬如,若是咱們僅僅爲了打印而生成一個對象,就不須要用引用變量來繫住它。最多見的就是打印字符串:

System.out.println(「Hello World」);

字符串對象「Hello World」在打印後即被丟棄。有人把這種對象稱之爲臨時對象。

對象與引用的關係將持續到對象回收。


值傳遞

  • Java只有一種參數傳遞方式:那就是按值傳遞,即Java中傳遞任何東西都是傳值。若是傳入方法的是基本類型的東西,你就獲得此基本類型的一份拷貝。若是是傳遞引用,就獲得引用的拷貝。

基本類型

public class TestBasicType {

    //基本類型的參數傳遞
    public static void testBasicType(int m) {
        System.out.println("m=" + m);//m=50
        m = 100;
        System.out.println("m=" + m);//m=100

    }

    public static void main(String[] args) {
        int i = 50;
        testBasicType(i);
        System.out.println("i=+"+i);//i=50
        
        //只是將值複製給參數m,m和i是兩回事
    }

}

引用類型

public class Person {



    public int age;//Person的屬性,全局變量,初始age爲0


    public void addAge(){
        this.age = this.age+1;
    }


  
    public static void changeAge(Person p){
        Person p1 = p;    //這裏將對象引用p複製給p1,p和p1指向同一對象,所以不管p仍是p1操縱對象,對象的內容都會改變
        p1.addAge();      //p1操縱 對象發生改變,p1指向的對象的age=1
        System.out.println("p1.age="+p1.age); //所以p1.age=1
    }

    public static void main(String[] args) {

        Person p = new Person();//建立一個Person對象,Person p 建立一個Person類的對象引用
        changeAge(p);//將引用p複製給了這個方法的形參
        System.out.println("p.age="+p.age);//p和p1指向同一對象,所以p.age=1
    }

}

能夠將上面的p1比喻爲汽車的剎車,p比做汽車的油門,汽車的速度同時受到油門和剎車的控制,不管踩下油門仍是剎車,
車速必有變化。固然控制車速的還會有離合,能夠假設離合爲新的並指向車速的引用p2,那麼車速就由p,p1,p2控制,也就是說能夠多個引用指向一個對象。


下面咱們經過一個反例來證實值傳遞

public class CallByValue {

    private static User user=null;
    private static User stu=null;

    /**
     * 交換兩個對象
     * @param x
     * @param y
     */
    public static void swap(User x,User y){
        User temp =x;
        x=y;
        y=temp;
        //只是複製了引用給了參數x,y
        //x,y變化影響不了外面的引用user,stu
    }


    public static void main(String[] args) {
        user = new User("user",26);
        stu = new User("stu",18);
        System.out.println("調用前user的值:"+user.toString());
        System.out.println("調用前stu的值:"+stu.toString());
        swap(user,stu);
        System.out.println("調用後user的值:"+user.toString());
        System.out.println("調用後stu的值:"+stu.toString());
    }
}
調用前user的值:User [name=user, age=26]

調用前stu的值:User [name=stu, age=18]

調用後user的值:User [name=user, age=26]

調用後stu的值:User [name=stu, age=18]

即便java函數在傳遞引用數據類型時,也只是拷貝了引用的值罷了,之因此能修改引用數據是由於它們同時指向了一個對象,但這仍然是按值調用而不是引用調用。


  • 總結:
    • 一個方法不能修改一個基本數據類型的參數(數值型和布爾型)。
    • 一個方法能夠修改一個引用所指向的對象狀態,但這仍然是按值調用而非引用調用。
    • Java只存在值傳遞。上面兩種傳遞都進行了值拷貝的過程。

"="號

Java中對 = 的理解很重要啊!!

  • =賦值操做 :在java中,= 是一個動做,一個能夠改變內存狀態的操做,一個能夠改變變量的符號,而+ - * /卻不會。

  • 這裏的賦值操做實際上是包含了兩個意思:
    • 1.放棄了原有的值或引用.
    • 2.獲得了=右側變量的值或引用。
  • 對於基本數據類型變量,=操做是完整地複製了變量的值。換句話說,「=以後,你我已無關聯」.

  • 對於非基本數據類型變量,=操做是複製了變量的引用。

  • 參數自己是變量,全部咱們對變量的操做、變量能有的行爲,參數都有,參數傳遞本質就是一種 =操做。

相關文章
相關標籤/搜索