淺析總結 Java 內部類的一些使用與梳理

========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
網站:www.qiujuer.net
開源庫:Genius-Android
轉載請註明出處:http://blog.csdn.net/qiujuer/article/details/43282699

——學之開源,用於開源;初學者的心態,與君共勉!
========================================================

有這篇文章,純屬巧合;那天在使用中突然發現 Java 內部類中還分 static ,說實話平時都在用,但是就是沒有注意到;感覺有必要總結一下。

有必要說一下的是本文純屬淺析,如有補充還請在評論中指出,歡迎總結。

內部類的位置

public class A {
	class B {

	}

	public void pint() {
		class C {
		}
		new C();
	}

	public void pint(boolean b) {
		if (b) {
			class D {
			}
			new D();
		}
	}
}
從代碼中可以看出,內部類可以定義到很多地方,常用的是成員變量中(B),方法中也叫局部內部類(C),作用域中(D)

從上面來看似乎沒有用到過在方法中和作用域中的情況啊,這就錯了;再來看看這個:

public interface AInterface {
	void show();
}
public class B {

	public void show() {
		class Man implements AInterface {
			@Override
			public void show() {

			}

		}
		Man man = new Man();
		man.show();
	}

}
其中我們定義了兩個文件,一個文件是一個接口類,一個是B文件;在B類中,的 show()方法中我們使用了局部內部類的方式創建了類 Man ,Man class繼承接口並實現方法,隨後使用該類。

內部類的權限

爲什麼要有內部類的存在?

在我看來類主要的就是封裝、繼承、多態;當然其中的回調思想我認爲是很重要的;而內部類的出現就是爲了簡化多重繼承的問題;一個A類,並不能繼承多個其他類,但是在使用中又需要使用到其他類的方法,這個時候內部類就發揮作用了;典型的就是事件點擊的回調實現。

那麼內部類的權限究竟有多大?

至於答案是什麼,代碼上看看就知道了。

public class C {
	int a = 1;
	private int b = 2;
	protected int c = 3;
	public int d = 4;

	void a() {
		System.out.println("A:" + a);
	}

	private void b() {
		System.out.println("B:" + b);
	}

	protected void c() {
		System.out.println("C:" + c);
	}

	public void d() {
		System.out.println("D:" + d);
	}

	class D {

		void show() {
			int max = a + b + c + d;
			a();
			b();
			c();
			d();
			System.out.println("Max:" + max);
		}
	}

	public static void main(String[] args) {
		D d = new C().new D();
		d.show();
	}
}
運行結果:


可以看出,內部類 D 對類 C 具有完整的訪問權限,等於全身脫光了給你看。

那要是反過來呢?

public class C {
	class D {
		private int a = 20;
		private void a(){
			System.out.println("D.A:" + a);
		}
	}
	
	void show(){
		D d = new D();
		d.a();
		
		System.out.println("D.A:" + d.a);
	}

	public static void main(String[] args) {
		new C().show();
	}
}
運行結果:


可見也是完全可行的,也能直接訪問私有屬性 私有方法,在這裏似乎私有的限制已經失效了一般,這個讓我想起了以前看見過一個面試:在 Java 中 private 修飾何時會失效。

這完全是兩個人互相脫光光了啊~

匿名內部類

這個非常常見,特別是在按鈕點擊事件綁定中。

public class D {
	void initButton() {
		Button b1 = new Button();
		b1.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(Button v) {

			}
		});

		Button b2 = new Button();
		b2.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(Button v) {

			}
		});
	}

}
其中的:

    new OnClickListener() {

			@Override
			public void onClick(Button v) {

			}
		}
就是匿名內部類的使用方式,OnClickListener 是一個接口類,接口類是無法直接new 一個實例的;這裏也並不是那樣,而是new 了一個其他的類,該類是匿名的,也就是沒有名字,只不過該類實現了 OnClickListener接口類中的方法。

上面的添加回調部分可等同於:

public class D {
	void initButton1() {
		Button b1 = new Button();
		b1.setOnClickListener(new Listener1());

		Button b2 = new Button();
		b2.setOnClickListener(new Listener2());
	}

	class Listener1 implements OnClickListener {

		@Override
		public void onClick(Button v) {

		}
	}

	class Listener2 implements OnClickListener {

		@Override
		public void onClick(Button v) {

		}
	}

}
這裏就是先建立類,繼承自接口;而後賦值到 Button 中。

要說兩者的區別與好處,這個其實看具體的使用情況吧;如果你的按鈕很多,但是爲了避免建立太多類;那麼可以建立一個回調類,然後都賦值給所有的按鈕,不過最後就是需要在 onClick方法中進行判斷是那個按鈕進行的點擊。

匿名內部類的使用地方很多;具體的使用應視使用情況而定~

靜態內部類/靜態嵌套類

這個其實並不應該叫做內部類了,因爲其並不具備內部類的完全權限,在使用上與一般的類基本一樣;那爲什麼會有這個的存在?

在我看來這個類的存在是爲其包括類服務;意思是可以單獨服務,不被外面的類所知曉;如這樣:

public class E {
	private void show(){
		new A();
	}
	
	private static class A{
		
	}
}
其中類 A 使用了 static ,所以是靜態嵌套類,在這裏使用private 修飾;那麼該類只能在 E 類中進行實例化;無法在 其他文件中實例化。

這樣的情況使用外面的類能行麼?不行吧?也許你會說在 E.java 文件夾中建立 A.java ,並使用protected修飾;但是在同樣的包下,或者繼承的類中同樣能訪問了;這也只是其中一個較爲特殊的情況。

我們來看看權限

public class E {
	int a1 = 0;
	private int a2 = 0;
	protected int a3 = 0;
	public int a4 = 0;
	
	private void show(){
		A a =new A();
		System.out.print("b1:"+a.b1);
		System.out.print("b2:"+a.b2);
		System.out.print("b3:"+a.b3);
		System.out.print("b4:"+a.b4);
		
	}
	
	private static class A{
		int b1 = 0;
		private int b2 = 0;
		protected int b3 = 0;
		public int b4 = 0;
		
		private void print(){
			System.out.print("a1:"+a1);
			System.out.print("a2:"+a2);
			System.out.print("a3:"+a3);
			System.out.print("a4:"+a4);

		}
	}
}
在這個中的結果是怎樣?


從圖片中可以看出,其權限級別是單方向的;靜態嵌套類 A 對其包含類 E 完全透明;但 E 並不對 A 透明。

再來看看方法:


可以看出同樣的情況;這個是爲什麼呢?爲什麼就是多一個 static 的修飾就這麼完全不同?其是很好理解,兩個獨立的類;本來就無法直接使用,必須有引用才能調用其屬性與方法。

我們或許可以這麼調整一下就OK

public class E {
	int a1 = 0;
	private int a2 = 0;
	protected int a3 = 0;
	public int a4 = 0;

	private void show() {
		A a = new A();
		System.out.print("b1:" + a.b1);
		System.out.print("b2:" + a.b2);
		System.out.print("b3:" + a.b3);
		System.out.print("b4:" + a.b4);

		a.b1();
		a.b2();
		a.b3();
		a.b4();
	}

	void a1() {

	}

	private void a2() {

	}

	protected void a3() {

	}

	public void a4() {

	}

	private static class A {
		int b1 = 0;
		private int b2 = 0;
		protected int b3 = 0;
		public int b4 = 0;

		void b1() {

		}

		private void b2() {

		}

		protected void b3() {

		}

		public void b4() {

		}

		private void print(E e) {
			System.out.print("a1:" + e.a1);
			System.out.print("a2:" + e.a2);
			System.out.print("a3:" + e.a3);
			System.out.print("a4:" + e.a4);

			e.a1();
			e.a2();
			e.a3();
			e.a4();
		}
	}
}
在其靜態類中傳遞一個 E 的引用進去就能解決問題了:


可以看出其中現在並沒有報錯了;能正常運行。

兩者之間的隱藏區別

但是最開始上面的內部類是怎麼回事?難道是鬧鬼了?上面的內部類沒有傳遞引用的啊;爲啥加上一個 static 就不行了?

在這裏我們需要看看字節碼,我們先建立一個簡單的內部類:

public class F {
	
	class A{
		
	}
}
這個夠簡單吧?別說這個都難了;汗~

然後我們找到 class 文件,然後查看字節碼:

在這裏分別查看了 F 類的字節碼和 F$A 類的字節碼。

其中有這樣的一句: final F this$0; 這句是很重要的一句,這句出現的地方在其內部類中,意思是當你 new 一個內部類的時候就同時傳遞了當前類進去;所以在內部類中能具有當前類的完全權限,能直接使用所有的東西;就是因爲在隱藏情況下已經傳遞了當前類進去。

那麼我們再看看一個簡單的靜態內部類:

public class G {
	static class A {

	}
}
與上面的區別唯一就是在於添加了一個 static 。此時我們看看字節碼:



可以看出其中無論是 G 類,還是 G$A 類的初始化中都沒有其他多餘的部分,也沒有進行隱藏的傳遞進去當前類;所以這樣的情況下並不具備訪問權限,需要我們傳遞引用進去,可以通過接口也可以完全傳遞進去,具體取決於個人。所以加了static類的內部類除了在權限上比一般的類更加開放(與其包含類)外,與一般的類在使用上是一樣的;所以準確的說應該叫做靜態嵌套類。

初始化的區別

一個類中,同時包含了內部類與靜態內部類,那麼其初始化應該是怎麼樣的呢?

都是直接 new ?還是看看代碼:

public class H {
	int a = 1;

	public class A {
		public void Show() {
			System.out.print("a:" + a);
		}
	}

	public static class B {
		public void Show(H h) {
			System.out.print("a:" + h.a);
		}
	}

	public static void main(String[] args) {
		H h = new H();
		//A a = new A();
		A a1 = h.new A();
		B b = new B();
		//B b1 = h.new B();
		B b3 = new H.B();
	}
}
其中註釋了的兩種方式是不允許的方式,也就是無法正常運行。

A 因爲有一個隱藏的引用,所以必須是H 的實例才能進行初始化出A 類;而B 類則是因爲是在H 類中以靜態方式存在的類,所以需要 new H.B();之所以能直接使用new B(),與該 main 方法在 H 類中有關,因爲本來就在 H類中,所以直接使用 H類的靜態屬性或者方法可以不加上:“H.”  在前面。

內部類的繼承

直接繼承的情況:


可以看出報錯了,爲什麼?因爲需要傳遞一個 H 類進去,所以我們在繼承的時候需要顯示的指明:

public class I extends H.A{
	public I(H h){
		h.super();
	}
}
也就是在構造方法中,傳遞一個 H 的引用進去,並調用 H 實例的 super() 方法,才能進行實例化。
使用的話應該這樣:

	public static void main(String[] args) {
        H h = new H();
        I i = new I(h);
    }

而,如果是繼承其靜態嵌套類,則不需要這樣:

public class J extends H.B{

}
就這樣就OK。

哎,差不多了~~整個內部類的東西差不多就是這些了,寫了我3個小時42分鐘~汗!!!!

如果有沒有寫到的地方,還請補充~~

不對的地方還請指正~~


========================================================
作者:qiujuer
博客:blog.csdn.net/qiujuer
網站:www.qiujuer.net
開源庫:Genius-Android
轉載請註明出處:http://blog.csdn.net/qiujuer/article/details/43282699

——學之開源,用於開源;初學者的心態,與君共勉!
========================================================

發佈了82 篇原創文章 · 獲贊 709 · 訪問量 66萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章