java方法传String类型的参数时,为啥方法对值的修改没有在方法之外生效?
巴扎黑
巴扎黑 2017-04-18 09:13:32
0
8
1042

1、问题描述
java方法传String类型的参数时,为啥方法对值的修改没有在方法之外生效?假设是其他类型的对象,那么方法对对象的数据的修改是会在方法之外生效的啊,可是String类型也是对象类型啊,为啥就没有在方法之外生效呢?

2、代码示例

//示例1:对象类型
public class PassReferByValue  
{  
    String a = "123";  
    public static void test(PassReferByValue test)  
    {  
        test.a = "abc";  
    }  
  
    public static void main(String[] args)  
    {  
        PassReferByValue test = new PassReferByValue();  
        System.out.println("对象的数据的初始值是:" + test.a); //123 
          
        test(test);  
        System.out.println("通过调用test方法修改对象的数据的值之后:" + test.a); //abc  
          
    }  
} 

总结:因为是对象类型,所以方法每次修改对象的数据的值之后,都在方法之外生效了,也就是说,在方法之外和在方法之内访问到的对象的数据的值是一致的。

//示例2:String类型
public class Test  
{  
    public static void test(String str)  
    {  
        str = "word";  
    }  
  
    public static void main(String[] args)  
    {  
        String string = "hello";  
        System.out.println("对象的数据的初始值是:" + string);  //hello  
        test(string);  
        System.out.println("通过调用test方法修改对象的值之后还是以前的值:" + string);  //hello  
    }  
}

总结:如果方法的参数是String类型,虽然方法修改了它的值,但是并没有在方法之外生效,也就是说,在方法之外和在方法之内的访问到的值不一样!!!这是为什么???

3、网上的解释
String等immutable类型因为没有提供自身修改的函数,每次操作都是新生成一个对象,所以要特殊对待。
http://blog.csdn.net/fbysss/article/details/3082949

这样的解释太肤浅了,完全是基于文字的描述,有没有基于代码层面的解释,或者其他的更深层次的解释呢?

巴扎黑
巴扎黑

全部回覆(8)
阿神

按照你的理解思路來講

範例一確實是對对象的数据的修改,但是示例二却是对参数本身的修改

不管是值傳遞還是引用傳遞,其實本質上都是值传递,方法呼叫的時候 計算出參數的值,拷貝一份給對應的參數

只是對於對象類型,傳遞的是對象的引用(或者理解為指針,地址)值,雖然參數拿到的是拷貝的引用,但是和原引用指向的是同一個對象,因此根據這個引用可以操作到原對象的資料

你的範例二中,呼叫方法時,計算參數string的值(可以理解为hello这个字符串对象在内存中的地址),拷贝给参数str,参数str虽然拿到了原来hello字符串对象的引用(即str的值等于string的值,也即hello的地址),但是立马给这个参数重新赋值,这个时候str参数已经跟hello字符串没有任何关系了,str不再指向hello,改指向world了,但是这个过程中hello自身以及hello在内存中的地址没有任何变化,方法外部的string变量也一直指向hello這個字串對象,沒有任何變化

Peter_Zhu

三句話總結一下:
1.物件就是傳引用对象就是传引用
2.原始类型就是传值
3.String,Integer, Double2.原始型別就是傳值
3.String,Integer, Double等immutable類型因為沒有提供自身修改的函數,每次操作都是新產生一個對象,所以要特殊對待。可以認為是傳值。
Integer 和 String 一樣。保存value的類別變數是Final屬性,無法被修改,只能被重新賦值/產生新的物件。 當Integer 做為方法參數傳遞進方法內時,對其的賦值都會導致 原Integer 的引用被 指向了方法內的堆疊位址,失去了對原類別變數位址的指向。對賦值後的Integer物件做得任何操作,都不會影響原來物件。

連結描述🎜
PHPzhong

test1.a = "567",这里你改的是 test1 所引用的物件的屬性

str = "word" 你这里改的是引用变量 str 所指向的對象。

簡單的說,兩個,小明和小紅,你給小明把手換成了機械手,小明的手就真的換了,但是你把小紅改名叫小明……小紅這個人本身還是沒變的。

再深層一點,小明和小紅去參加一個活動,活動不需要知道你的真名,所以給他們分配了代號A和B。這時候,活動中A受傷了,把手換成了機械的,結果活動完了,小明的手確實已經換成機械的了。但B只是把名牌了C進行了交換,出來之後,小紅還是小紅,並沒有變成C對應的那個人。

這裡,活動就是方法,進去分配的代號就是形參,對形參直接賦值不會影響實參,但對形參引用的對象的屬性進行賦值,實際上已經影響到了對象的屬性。


補充一段附註解的程式碼,看能不能幫助你理解。如果要了解方法呼叫的真實過程還涉及棧之類的知識,就不搞那麼複雜了。這段程式碼沒有輸出,你可以在理解之後自己嘗試加一些輸出來驗證自己的理解是否正確。

public class PassReferByValue  
{  
    String a = "123";  
    public static void test(PassReferByValue test)  
    {  
        test.a = "abc";  
    }  
    
    public static void test2(PassReferByValue test) {
        test = new PassReferByValue();
        test.a = "cde";
    }
    
    public static void test3(String s) {
        s = "world";
    }
  
    public static void main(String[] args)  
    {
        PassReferByValue obj = new PassReferByValue();

        // -----------------------------------------------
        // test(obj);       
        // 如果把这个调用过程展开,代码就像这样(为了便于识别,我改了下变量名

        // 说明一下:下面用 { } 代码段主要是为了限定里面的变量作用域
        // 进入 test(obj);
        {
            PassReferByValue test = obj;
            test.a = "abc";
        }
        // 没有返回类型,所以这里方法直接结束,出来
        
        // ------------------------------------------------
        // 现在来展开 test2(obj);
        {
            PassReferByValue test = obj;
            test = new PassReferByValue();  // test 是一个新对象,不是 obj 了
            test.a = "cde";     // 改变的是新对象,对 obj 没有影响
        }
        // 所以方法调用结束之后 obj 还是没变,还是 obj.a.equals("abc");
        
        // -------------------------------------------------
        // 现在来看 string 的情况
        String hello = "hello";
        // 这里开始展开 test3(hello);
        {
            String s = hello;
            s = "world";    // 这里修改了 s 引用的对象,但没有修改 hello 引用的对象
            // 所以 hello.equals("hello"), s.equals("world");
        }
    }
} 
洪涛

值傳遞,引用傳遞

PHPzhong

Java中除了String,其他的Object都是按引用傳遞。

小葫芦
//示例2:String类型
public class Test  
{  
    public static void test(String str)  
    {  
        //str这个变量作用域在test这个函数里,原本指向"hello",现在指向"word"
        str = "word";  
    }  
  
    public static void main(String[] args)  
    {  
        String string = "hello";  
        System.out.println("对象的数据的初始值是:" + string);  //hello  
        test(string);  
        System.out.println("通过调用test方法修改对象的值之后还是以前的值:" + string);  //hello  
    }  
}

我的一篇專欄文章的開頭有講到相關知識點,看你有點暈乎乎的,向你推薦一下。

阿神

String是immutable類型,也就是說其值是不可變的。例如String a = "abc",在内存中a这个引用指向"abc"这块内存,当使用a = "bcd"時,並沒有將"abc"所在記憶體區塊的值改為"bcd",而是另外產生一個字串"bcd",然後將a指向"bcd",所以方法外的引用仍然指向"abc"這塊記憶體。

而可變的類型,會直接修改"abc"所在記憶體的值,這樣做方法外的引用仍然指向原來的記憶體位址,於是可以在方法外存取到修改後的值。

阿神

String物件中包含的字串是不能被修改的,當String類型的變數被重新賦值後,其引用也會發生變化,是一個新的物件了。

熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板