天天看點

關于不可變類

參考:http://blog.csdn.net/cjmiou/article/details/40816013

package shishi;

public class shishi1 {
	private int a;
	private String hae = " ?";
	public void set() {
		String a  = "hea";
		hae = a;
	}
	public static void change(shishi1 b,shishi1 c) {
//		int hh = b.a;
//		b.a = c.a;
//		c.a = hh;
		b = c;
		System.out.println("a =" + b.a + " " +"b =" + c.a +" " +
		b + "  ---  " + c );
	}
	
	public static void change(StringBuilder b,int c) {
		b.append(" hello");
		c += 3;
		System.out.println("a = " + b + "  b = " + c );
	}
	
	public void say() {
		System.out.println(this.getClass()+"  say"+"shishi1" + a);
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		StringBuilder a = new StringBuilder("hello");
		int b = 5;
		String ab = "safsfsfsf";
		int con = ab.length();
		while( con--  >0) {
			System.out.println(ab.charAt(con));
		}
		shishi1 a1 = new shishi1();
		a1.a = 123;
		shishi1 b1 = new shishi1();
		b1.a = 132;
		change(a1,b1);
		System.out.println("a =" + a1.a + " " +"b =" + b1.a + " "
			+ a1 +"   "+ b1 );
		change(a,b);
		System.out.println("a = " + a + "  b = " + b );
		a1.set();
		System.out.println(a1.hae);
		
	}

}
           

先說結論:在方法中無法改變原參數的(交換之類的,屬于形參的值傳遞,隻會改變形參的引用指向),是以跟該類是否是final class無關,好多人說String類對象無法在方法中進行交換是因為String類是final class,答:并不是,因為Java的傳遞方式隻有值傳遞,也就是說當方法被調用時,形參中被傳遞進了原參數的值,對于String對象而言,傳進來的是堆中的字元串的位址。而形參進行交換時,隻是将形參中存的位址值交換了,與原參數無關,原參數沒變化,是以原參數中所存的位址值沒變化,依然指向原來的堆中字元串位址。

再提一下關于不可變類:

不可變類:建立該類的執行個體後,該執行個體的執行個體變量無法被改變,即不可變類,其不提供更改屬性值的方法,并使用 private final修飾該類的成員變量,private使得子類不可見,final使得不可改,一般提供一個帶參數的構造器,以便于初始化

如果需要建立自定義的不可變類,可遵守如下規則:

(1)使用private final 修飾符來修飾該類的屬性

(2)提供帶參數構造器,用于根據傳入參數來初始化類裡的屬性。

(3)僅為該類的屬性提供getter方法,不要為該類提供setter方法,因為普通方法無法修改final修飾的屬性

(4)如果有必要,重寫Object類中的hashCode和equals方法。在equals方法根據關鍵屬性來作為兩個對象相等的标準,除此之外,還應該保證兩個用equals方法判斷為相等的對象的hashCode也相等。

(5)如果該類中含有引用類型成員變量,如果該成員變量可變,則該類必須要保護該可變變量,比如不利用已有的該可變類對象,而是建立一個可變類對象,比如

public class Person{

    private final Name name;//Name類可變

    public Person(Name name){

        this.name = new Name(name.getFirstName().name.getLastName());//重新建立一個name,保證即使傳入的參數被改變,Person依然不變,因為Person中的name變量是建立的,不同于傳入的那個,且無法從外界獲得,也就無法更改了

    }

}

注意:final class不一定是不可變類!!!!final class僅僅代表不可繼承!!!!但是String,Integer等包裝類都是不可變類,他們都是final class

關于為什麼不可變類标準要聲明為 final class,就是為了禁止繼承,防止可變的子類對象向上轉型為父類對象,然後作為參數傳入了形參為父類的方法中,這樣,原本為不可變的父類設計的方法就有了漏洞,根據裡氏代換原則,子類能夠替代父類功能,不可變父類無法保證其子類是不可變類,是以幹脆禁止繼承,保證了不可變性!另一個原因,父類的方法設定成了final可以保證不會被重寫,但是子類仍然可以重載此方法,此時不可變性便被破壞了,是以一定要final class。

關于Integer,由于緩存池的存在,通過new Integer()出來的對象每次都是新的,而通過valueOf則首先檢查緩存池,若緩存池中有,則直接傳回緩存池中的那個元素。緩存池中隻有-128~127

例子如瘋狂Java講義 185頁内容,

Integer in1 = new Integer(6);

Integer in2 = new Integer(6);//新的對象,in1!=in2

Integer in3 = Integer.valueOf(6);//将6放入緩存

Integer in4 = Integer.valueOf(6);//直接從緩存中取,in4 == in3

Integer,和String由于是不可變類,是以在方法中改變他們無效,他們隻是指向了另一個對象,而不是對原對象進行改變,

等同于直接改變指向例如 b=c,是以無法改變原參數的引用,

StringBuffer和StringBuilder可以改變原參數指向的位址的存儲的值,

但是形參更改指向對于原參數沒有影響,比如:b = c;這樣對于原參數沒有任何影響,

對于自定義的類來說,與StringBuffer和StringBuilder類似,在方法中直接改變指向是無效的,比如第12行,對原參數無影響,但是改變指向位址所存的值時,是有效的,

與StringBuffer和StringBuilder一樣,比如10~12行

以上都針對形參值傳遞問題,對于set方法之類的對象調用方法,比如12~15行,48行a1.set();

其中對于對象a1的操作是對于a1本身的操作,(等同于this,隻不過省略了),可以對本對象的變量進行引用位址更改和指派