1. 程式人生 > 實用技巧 >題解 「BZOJ2137」submultiple

題解 「BZOJ2137」submultiple

值傳遞


在方法被呼叫時,實參通過形參把它的內容副本傳入方法內部,此時形參接收到的內容是實參值的一個拷貝,因此在方法內對形參的任何操作,都僅僅是對這個副本的操作,不影響原始值的內容。

先來看個例子:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 publicstaticvoidvalueCross(intage,floatweight) { System.out.println("傳入的age值:"+age); System.out.println("傳入的weight值:"+weight); age=23; weight=
60; System.out.println("修改後的age值:"+age); System.out.println("修改後的weight值:"+weight); } publicstaticvoidmain(String[] args) { intage=10; intweight=50; valueCross(age,weight); System.out.println("方法執行後的age:"+age); System.out.println("方法執行後的weight:"+weight); }

  

執行結果:

1 2 3 4 5 6 傳入的age值:
10 傳入的weight值:50.0 修改後的age值:23 修改後的weight值:60.0 方法執行後的age:10 方法執行後的weight:50

  

我們可以看到valueCross方法執行後,實參age和weight的值並沒有發生變化,這是什麼原因?

首先程式執行時,先從main方法開始執行,此時JVM為main()方法往虛擬機器棧中壓入一個棧幀,即為當前棧幀,用來存放main()中的區域性變量表(包括引數)、操作棧、方法出口等資訊,如a和w都是mian()方法中的區域性變數,因此可以斷定,age和weight是躺著mian方法所在的棧幀中

接著呼叫valueCross方法,此時JVM為valueCross()方法往虛擬機器中壓入一個棧幀,即為當前棧幀,用於存放valueCross方法的區域性變數等資訊;因此age和weight是躺著valueCrossTest方法所在的棧幀中,而他們的值是從a和w的值copy了一份副本而得,如圖:

因此這兩個age和weight對應的內容不是同一個,在valueCross方法中修改的只是自己棧中的內容,並沒有修改main方法棧中的內容

引用傳遞

”引用”也就是指向真實內容的地址值,在方法呼叫時,實參的地址通過方法呼叫被傳遞給相應的形參,在方法體內,形參和實參指向同一塊記憶體地址,對形參的操作會影響的真實內容。

先有一個Person類程式碼如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 publicclassPerson { privateString name; privateintage; publicString getName() { returnname; } publicvoidsetName(String name) { this.name = name; } publicintgetAge() { returnage; } publicvoidsetAge(intage) { this.age = age; } }

  

測試類程式碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 publicclassTestArr { publicstaticvoidPersonCross(Person person) { System.out.println("傳入的person的name:"+person.getName()); person.setName("敖丙"); System.out.println("方法內重新賦值後的name:"+person.getName()); } publicstaticvoidmain(String[] args) { Person p=newPerson(); p.setName("哪吒"); p.setAge(4); PersonCross(p); System.out.println("方法執行後的name:"+p.getName()); } }

  

測試結果1:

1 2 3 傳入的person的name:哪吒 方法內重新賦值後的name:敖丙 方法執行後的name:敖丙

  

我們可以看到PersonCross方法執行後,person的name值被改變了

下面再看一段程式碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 publicclassTestArr { publicstaticvoidPersonCross(Person person) { System.out.println("傳入的person的name:"+person.getName()); person=newPerson();//新新增的程式碼 person.setName("敖丙"); System.out.println("方法內重新賦值後的name:"+person.getName()); } publicstaticvoidmain(String[] args) { Person p=newPerson(); p.setName("哪吒"); p.setAge(4); PersonCross(p); System.out.println("方法執行後的name:"+p.getName()); } }

  

測試結果2:

1 2 3 傳入的person的name:哪吒 方法內重新賦值後的name:敖丙 方法執行後的name:哪吒

  

有沒有發現什麼不同,這次PersonCross方法執行後person的name值並沒有改變,這又是為什麼?

我們知道,java中的物件和陣列是存放在堆記憶體中的,而堆記憶體是執行緒共享的,所以main方法執行時,會在堆記憶體中開闢一塊記憶體,用來儲存p物件的所有內容,然後再棧記憶體中建立一個引用p儲存堆區中p物件的真實地址,如下圖:

當執行到PersonCross方法時,因為方法內有這麼一行程式碼:person=newPerson(),此時JVM在堆記憶體中又開闢了一塊記憶體空間,假設地址為xo2222,那麼現在的person則指向了xo2222這塊記憶體,現在修改person的name值修改的是xo2222這塊記憶體空間的值,不會改變xo3333的值,所以測試結果2中的name沒有發生變化

引用傳遞本質上就是值傳遞,將引用變數的值傳遞給形參,因為引用變數的值存放的是地址值,所以當地址值傳遞給形參後,形參和實參指向同一塊記憶體區域。