java引用與引數傳遞
阿新 • • 發佈:2018-12-30
網上都在講引數傳遞是一種拷貝,拷貝的變化不會影響原值,當然這是對的。還有一種說法,叫JAVA裡只有一種引數傳遞方式,值傳遞,這也是對的。但是我總覺得這些說法容易讓新人犯迷糊,因為有一個很常見的現象,很多時候拷貝也能改變原始物件的屬性。所以有些人又把引數傳遞跟基本型別傳遞分開去解釋這個問題,拆開顯然是不對的。
先來看這段非常簡單的程式碼:
再來看這樣的引數傳遞:
3.通過引用去改變堆中物件屬性的時候,只要通過一個引用改變指向物件的屬性,指向同一個物件的所有引用,看起來都會變。所以有時候看起來,方法內和方法外的引用一起改變了。 所以,在JAVA編寫過程中,儘量不要使用public屬性,而是要使用get,set方法。因為如果在你的函式中,通過引用改變了一些類的屬性,就會使得程式變得混亂而不可讀。為了後期維護的方便,請不要直接在呼叫的方法中通過引用改變引用指向物件的屬性。 於此同時,克隆也是一樣的道理,淺克隆中,克隆的物件如果存在引用,將會使得調用出現異常,因為兩個克隆物件中如果擁有同一個引用屬性,當你呼叫這個引用的時候,就會出現克隆的屬性一起改變的情況,克隆的兩個物件確實是兩個物件,但是他們的引用屬性卻指向了同一個物件。如果他們指向的物件無法被修改,那麼淺克隆完全是符合要求的。但是如果要通過這個引用去修改指向的那個物件,這其實是一定程度上違反了JAVA的基本原則(對擴充套件開放,對修改關閉)。
先來看這段非常簡單的程式碼:
package main;
public class Main {
public static void main(String[] args) {
TestObject a = new TestObject();
TestObject b = new TestObject();
System.out.println(a); //列印結果為:[email protected]
System.out.println(b); //列印結果為:[email protected]
System.out.println(a.equals(b)); //列印結果為:false
System.out.println(b.testString);//列印結果為:no.0
a = b;
System.out.println(a); //列印結果為 : [email protected]
System.out.println(b); //列印結果為 :[email protected]
System.out.println(a.equals(b)); //列印結果為:true
a.testString = "no.1";
System.out.println(b.testString); //列印結果為:no.1
}
}
package main;
public class TestObject {
public String testString = "no.0";
}
使用 TestObject a = new TestObject(); 的時候,這個a,事實上就是一個引用(可以理解為C語言中的指標,是一個指向堆中真實物件的地址)。
所以,很自然,如果a 和 b 兩個引用相同(指向同一個物件),執行a.testString = "no.1"之後,通過b獲得的物件也改變了。
接下來我們看引數傳遞:
package main;
public class Main {
public static void main(String[] args) {
// TODO 自動生成的方法存根
TestObject a = new TestObject();
TestObject b = new TestObject();
System.out.println(a); //列印結果為: [email protected]
System.out.println(b); //列印結果為:[email protected]
System.out.println(a.equals(b)); //列印結果為:false
System.out.println(b.testString);//列印結果為:no.0
a = b;
System.out.println(a); //列印結果為 :[email protected]
System.out.println(b); //列印結果為 :[email protected]
System.out.println(a.equals(b)); //列印結果為:true
a.testString = "no.1";
System.out.println(b.testString); //列印結果為:no.1
//------------------------------------------------------
System.out.println(a);//列印結果為 : [email protected]
change(a); //列印結果為 :[email protected]
System.out.println(b.testString);//列印結果為 :no.2
}
private static void change(TestObject s) {
// TODO 自動生成的方法存根
System.out.println(s);
s.testString = "no.2";
}
}
package main;
public class TestObject {
public String testString = "no.0";
}
我們都知道,s是a的拷貝。現在s變了,a也變了,這是為什麼?其實,s是一個引用,s,a,b指向了同一個物件。所以我們通過s改變這個物件的時候,通過a,b,s獲取到的物件都會改變。所以,JAVA裡面通過引數傳遞,去改變物件是“可行的”,當然這個可行要打一個引號,因為我們不推薦這麼做。再來看這樣的引數傳遞:
package main;
public class Main {
public static void main(String[] args) {
// TODO 自動生成的方法存根
TestObject a = new TestObject();
TestObject b = new TestObject();
System.out.println(a); //列印結果為:[email protected]
System.out.println(b); //列印結果為:[email protected]
System.out.println(a.equals(b)); //列印結果為:false
System.out.println(b.testString);//列印結果為:no.0
a = b;
System.out.println(a); //列印結果為 :[email protected]
System.out.println(b); //列印結果為 :[email protected]
System.out.println(a.equals(b)); //列印結果為:true
a.testString = "no.1";
System.out.println(b.testString); //列印結果為:no.1
//------------------------------------------------------
System.out.println(a);//列印結果為 :[email protected]
change(a); //列印結果為 :[email protected]
System.out.println(b.testString);//列印結果為 :no.2
//------------------------------------------------------
System.out.println(a); //列印結果為 :[email protected]
change2(a);
System.out.println(a); //列印結果為 :[email protected]
System.out.println(b.testString);//列印結果為 :no.3
}
private static void change(TestObject s) {
System.out.println(s); //列印結果為 :[email protected]
s.testString = "no.2";
}
private static void change2(TestObject s) {
System.out.println(s);//列印結果為 :[email protected]
s.testString = "no.3";
TestObject c = new TestObject();
s = c;
System.out.println(s);//列印結果為 :[email protected]
s.testString = "no.4";
System.out.println(s.testString);//列印結果為 :no.4
}
}
package main;
public class TestObject {
public String testString = "no.0";
}
看結果,最終,s改變了嗎?s改變了,s變成了[email protected]。那a變了嗎?a沒有變,a還是[email protected]。s是不是拷貝?當然是拷貝,s變了,但是a沒有改變。
其實,當s是[email protected]的時候,通過s.testString = "no.3";改變s指向的物件的屬性的時候,a,b,s由於指向了同一個物件,所以看起來a也變了。當s變為[email protected]的時候,由於s和a,b已經不再指向同一個物件了,所以,s變了,a,b指向的物件不會有改變。
基本型別就不講了,網上都是講基本型別的引數傳遞拷貝不變性的。總結一下:
1.引數傳遞是拷貝,沒問題。
2.JAVA裡面的傳遞只有一種,值傳遞,沒問題。3.通過引用去改變堆中物件屬性的時候,只要通過一個引用改變指向物件的屬性,指向同一個物件的所有引用,看起來都會變。所以有時候看起來,方法內和方法外的引用一起改變了。 所以,在JAVA編寫過程中,儘量不要使用public屬性,而是要使用get,set方法。因為如果在你的函式中,通過引用改變了一些類的屬性,就會使得程式變得混亂而不可讀。為了後期維護的方便,請不要直接在呼叫的方法中通過引用改變引用指向物件的屬性。 於此同時,克隆也是一樣的道理,淺克隆中,克隆的物件如果存在引用,將會使得調用出現異常,因為兩個克隆物件中如果擁有同一個引用屬性,當你呼叫這個引用的時候,就會出現克隆的屬性一起改變的情況,克隆的兩個物件確實是兩個物件,但是他們的引用屬性卻指向了同一個物件。如果他們指向的物件無法被修改,那麼淺克隆完全是符合要求的。但是如果要通過這個引用去修改指向的那個物件,這其實是一定程度上違反了JAVA的基本原則(對擴充套件開放,對修改關閉)。