1. 程式人生 > >關於Java中內部類的一些概念整理

關於Java中內部類的一些概念整理

    把一個類放到另一個類的內部定義,這個定義在其他類內部的類就被稱為內部類。

    內部類的作用:

    (1).內部類提供了更好的封裝,可以把內部類隱藏在外部類之內,不允許同一個包中的其他類訪問該類。

    (2).內部類成員可以直接訪問外部類的私有資料,因為內部類被當成其外部類成員,同一個類的成員之間可以互相訪問。但外部類不能訪問內部類的實現細節。

    (3).匿名內部類適用於建立那些僅需要一次使用的類。

    (4).內部類比外部類可以多使用三個修飾符:private、protected、static——外部類不可以使用這三個修飾符。

    (5).非靜態內部類不能擁有靜態成員。

    1.非靜態內部類

    定義內部類非常簡單,只要把一個類放在另一個類內部定義即可。此處的“類內部”包括類中的任何位置,甚至在方法中也可以定義內部類(方法裡定義的內部類被稱為區域性內部類)。

    成員內部類是一種與成員變數、方法、構造器和初始化塊相似的類成員;區域性內部類和匿名內部類則不是類成員。

    成員內部類分為兩種:靜態內部類和非靜態內部類,使用static修飾的成員內部類是靜態內部類,沒有使用static修飾的成員內部類是非靜態內部類。

    因為內部類作為其外部類的成員,所以可以使用任意訪問控制符如private、protected和public等修飾。

    外部類的上一級程式單元是包,所以他只有兩個作用域:同一包內和任何位置。而內部類的上一級程式單元是外部類,它就具有4個作用域:同一個類、同一個包、父子類和任何位置,因此可以使用四中訪問控制權限。

public class Cow {
	private double weight;
	//外部類的兩個過載的構造器
	public Cow(){}
	public Cow(double weight){
		this.weight=weight;
	}
	//定義一個非靜態內部類
	@SuppressWarnings("unused")
	private class CowLeg{
		//非靜態內部類的兩個例項變數
		private double length;
		private String color;
		//非靜態內部類的兩個過載的構造器
		public CowLeg(){}
		public CowLeg(double length,String color){
			this.length=length;
			this.color=color;
		}
		public double getLength() {
			return length;
		}
		public void setLength(double length) {
			this.length = length;
		}
		public String getColor() {
			return color;
			
		}
		public void setColor(String color) {
			this.color = color;
		}
		//非靜態內部類的例項方法
		public void info(){
			System.out.println("當前牛腿顏色是:"+color+",高:"+length);
			//訪問外部類的private修飾的成員變數
			System.out.println("本牛腿所在奶牛重:"+weight);
		}
	}
	public void test(){
		CowLeg c1=new CowLeg(1.12,"黑白相間");
		c1.info();
	}
	public static void main(String[] args) {
		Cow cow=new Cow(378.9);
		cow.test();
		/*
		 * 輸出結果:
		 * 當前牛腿顏色是:黑白相間,高:1.12
                 * 本牛腿所在奶牛重:378.9
		 */
	}
}
    上面程式在編譯後生成兩個class檔案,一個是Cow.class,另一個是Cow$CowLeg.class,前者是外部類Cow的class檔案,後者是內部類CowLeg的class檔案,即成員內部類(包括靜態內部類、非靜態內部類)的class檔案總是這種形式:OuterClass$InnerClass.class。

    在非靜態內部類物件裡,儲存了一個它所寄生的外部類物件的引用(當呼叫非靜態內部類的例項方法時,必須有一個非靜態內部類例項,非靜態內部類例項必須寄生在外部類例項中)。

    當在非靜態內部類的方法內訪問某個變數時,系統優先在該方法內查詢是夠存在該名字的區域性變數,如果存在就是用該變數;如果不存在,則到該方法所在的內部類中查詢是否存在該名字的成員變數,如果存在就是用該成員變數;如果不存在,則到該內部類所在的外部類中查詢是否存在該名字的成員變數,如果存在則使用該成員變數,如果已然不存在,系統將出現編譯錯誤:提示找不到該變數。

    如果外部類成員變數、內部類成員變數與內部類裡方法的區域性變數同名,則可通過使用this、外部類類名.this作為限定來區分。

public class DiscernVariable {
	private String prop="外部類的例項變數";
	private class InClass{
		private String prop="內部類的例項變數";
		public void info(){
			String prop="區域性變數";
			//通過外部類類名.this.varname訪問外部類例項變數
			System.out.println("外部類的例項變數值:"+DiscernVariable.this.prop);
			//通過this.varname訪問內部類例項的變數
			System.out.println("內部類的例項變數值:"+this.prop);
			//直接訪問區域性變數
			System.out.println("區域性變數的值:"+prop);
		}
	}
	public void test(){
		InClass in=new InClass();
		in.info();
	}
	public static void main(String[] args) {
		new DiscernVariable().test();
		/*
		 * 輸出結果:
		 * 外部類的例項變數值:外部類的例項變數
                      內部類的例項變數值:內部類的例項變數
                      區域性變數的值:區域性變數
		 */
	}
}
    通過OuterClass.this.propName的形式訪問外部類的例項變數,通過this.propName的形式訪問非靜態內部類的例項變數。

    非靜態內部類的成員可以訪問外部類的private成員,但反過來不成立。非靜態內部類的成員只在非靜態內部類範圍內是可知的,並不能被外部類直接使用。如果外部類需要訪問非靜態內部類的成員,則必須顯式建立非靜態內部類物件來呼叫訪問其例項成員。

public class Outer {
	private int outProp=9;
	class Inner{
		private int inProp=5;
		public void acessOuterProp(){
			//非靜態內部類可以直接訪問外部類的private成員變數
			System.out.println("外部類的outProp值:"+outProp);
		}
	}
	public void accessInnerProp(){
		//外部類不能直接訪問非靜態內部類的例項變數
		//下面程式碼編譯錯誤
		//System.out.println("內部類的inProp值:"+inProp);
		//如需訪問內部類的例項變數,必須顯式建立內部類物件
		System.out.println("內部類的inProp值:"+new Inner().inProp);
	}
	public static void main(String[] args) {
		Outer out=new Outer();
		out.accessInnerProp();
		/*
		 * 輸出結果:
		 * 內部類的inProp值:5
		 */
	}
}
    非靜態內部類物件和外部類物件的關係:非靜態內部類物件必須寄生在外部類物件裡,而外部類物件則不必一定有非靜態內部類物件寄生其中。簡單地說,如果存在一個非靜態內部類物件,則一定存在一個被它寄生的外部類物件那個。但外部類物件存在時,外部類物件裡不一定寄生非靜態內部類物件。因此外部類物件訪問非靜態內部類成員時,可能非靜態普通內部類物件根本不存在。而非靜態內部類物件訪問外部類成員時,外部類物件一定存在。

    根據靜態成員不能訪問非靜態成員的規則,外部類的靜態方法、靜態程式碼塊不能訪問非靜態內部類,包括不能使用非靜態內部類定義變數、建立例項等。總之,不允許在外部類的靜態成員中直接使用非靜態內部類。

    Java不允許在非靜態內部類中定義靜態變數。

    非靜態內部類中不能有靜態方法、靜態成員變數、靜態初始化塊。

    非靜態內部類中不可以有靜態初始化塊,但可以包含普通初始化塊。非靜態被不累普通初始化塊的作用於外部類的初始化塊作用完全相同。

    2.靜態內部類

    如果使用static來修飾一個內部類,則這個內部類就屬於外部類本身,而不屬於外部類的某個物件。因此使用static修飾的內部類被稱為類內部類,有的地方也成為靜態內部類。

    static關鍵字的作用是把類的成員變成類相關,即static修飾的成員屬於整個類,而不屬於單個物件。外部類的上一級程式單元是包,所以不可使用static修飾;而內部類的上一級程式單元是外部類,使用static修飾可以將內部類變成外部類相關,而不是外部類例項相關,因此static關鍵字不可修飾外部類,但可修飾內部類。

    靜態內部類可以包含靜態成員,也可以包含非靜態成員。根據靜態成員不能訪問非靜態成員的規則,非靜態內部類不能訪問外部類的例項成員,只能訪問外部類的類成員。即使是靜態內部類的例項方法也不能訪問外部類的例項成員,只能訪問外部類的就靜態成員。

    靜態內部類可以包含靜態成員,也可以包含非靜態成員。根據靜態成員不能訪問非靜態成員的規則,靜態內部類不能訪問外部類的例項成員,只能訪問外部類的類成員。即使是靜態內部類的例項方法也不能訪問外部類的例項成員,只能訪問外部類的靜態成員。

    靜態內部類是外部類的一個靜態成員,因此外部類的所有方法、所有初始化塊中可以使用靜態內部類來定義變數、建立物件等。

    外部類依然不能直接訪問靜態內部類的成員,但可以使用靜態內部類的類名來作為呼叫者來訪問靜態內部類的類成員,也可以使用靜態內部類物件作為呼叫者來訪問靜態內部類的例項成員。

public class AccessStaticInnerClass {
	static class StaticInnerClass{
		private static int prop1=5;
		private int prop2=9;
	}
	public void accessInnerProp(){
		//System.out.println(prop1);
		//上面程式碼出現錯誤,應改為如下形式
		//通過類名訪問靜態內部類的類成員
		System.out.println(StaticInnerClass.prop1);
		//System.out.println(prop2);
		//上面程式碼出現錯誤,應該為如下形式
		//通過例項訪問靜態內部類的例項成員
		System.out.println(new StaticInnerClass().prop2);
	}
}
    Java還允許在接口裡定義內部類,接口裡定義的內部類預設使用public static修飾——介面內部類只能是靜態內部類。

    如果為介面內部類指定訪問控制符,則只能指定public訪問控制符;如果定義介面內部類暫時省略訪問控制符,則該內部類預設是public訪問控制權限。