本文轉載自 https://blog.csdn.net/SEU_Calvin/article/details/70089977
1. 你覺得下面程序會輸出什么
- public static void change(String s) {
- s = “123”;
- }
- public static void main(String args[]) {
- String s = “abc”;
- change(s);
- System.out.println(s);
- }
面試官寫下了這段代碼,問我s會輸出什么。
我想起了各種排序算法直接把一個int數組array,通過參數傳遞給一個方法,在方法里完成數據的移動后,直接在main()函數中輸出array[]中的值就是修改過的。所以我回答說,那應該輸出123吧。
然后面試官說,不對,是輸出abc,你覺得原因是什么。
我想了想回答說,String是final的,所以值是不變的。
接着面試官寫下了如下代碼:
- public static void main(String[] args) {
- MyClass myClass = new MyClass();
- change(myClass);
- System.out.println(myClass.val);
- }
- private static void change(MyClass myClass) {
- myClass = new MyClass();
- myClass.val = 2;
- }
- public static class MyClass{
- int val = 1;
- }
問我MyClass類不是final的,但為什么這里還是會輸出1。我有點被問懵了,其實后來面試結果又仔細看一下,是自己忽略掉了第八行代碼,其實把第八行代碼注釋掉,肯定就輸出2了。
2. Java參數傳值
2.1 值傳遞
先從一個例子說起:
- public static void change(int i, int j) {
- inttemp = i;
- i =j;
- j =temp;
- }
- public static void main(String[] args) {
- inta = 3;
- intb = 4;
- change(a,b);
- System.out.println(a);
- System.out.println(b);
- }
輸出為:
- 3
- 4
值傳遞是指方法調用時,實際參數把它的值傳遞給對應方法的形參,如change()方法中i和j,也在內存空間中分配了存儲單元,這樣形參在change()方法中的改變不會影響實際參數的值。
值傳遞的數據類型包括,八種基本數據類型和String。
2.2 引用傳遞
值傳遞的數據類型包括,八種基本數據類型和String,八種數據類型還比較好理解,比如2.1中的int,但是String並不是基本數據類型,這要怎么理解呢。比如下面這個例子。
- public class Example {
- String str = new String("abc");
- char[] ch = { 'a', 'b', 'c' };
- public static void main(String args[]) {
- Example ex = new Example();
- ex.change(ex.str, ex.ch);
- System.out.println(ex.str);
- System.out.println(ex.ch);
- }
- public void change(String str, char ch[]) {
- //ch = new char[]{'a','b','c'};
- str = "change";
- ch[0] = 'c';
- }
- }
這個例子的輸出為:
- abc
- cbc
要知道String和char數組都是引用類型,不是基本類型。
當我們把str作為參數傳入方法后,會新建另一個變量,已經不是原來的變量了,但是他們指向的數據區域都一樣,所以如果你在方法中改變了str指向的數據區域,即執行str = "change",那也只是改變新建的另一個變量所指向的數據區域,即指向一個新對象"change",str仍然指向原來的數據區域。所以會輸出abc。
但是對於char數組的例子呢,即對象類型,也就是Object的子類(除了String),是把ch的引用傳遞進來,即引用傳遞。這樣里面和外面的ch都指向了相同的數據區域,執行ch[0] = 'c',就會把這個數據區域里的第一個字符改成c,並沒有改變內部ch的數據地址,所以這個修改也會反映到外部的ch。所以會輸出cbc。如果你改變了內部ch所指向的數據區域,即把上面代碼中注釋的那一行打開,即執行ch = new char[]{'a','b','c'},這樣ch已經指向一個新的數據區域。輸出的結果肯定也就是abc了。
這樣在1中面試官給的兩段代碼也就很容易解釋了。
3 兩種數據傳遞總結
值傳遞和引用傳遞的本質區別在於是否在傳遞的時候進行對象的內存拷貝。
基本類型是由於在JVM中存儲區域不同於普通對象所以傳遞前會拷貝,傳遞的是拷貝后的值,但是對象在傳遞的時候不拷貝,直接傳“引用值”,指向同一片對象堆內存區域,當然要注意String這種特殊情況。