超詳細深入淺出Java內部類(通俗易懂+代碼示例)

可能有一些小夥伴會問我,不是說詳細嗎?連個目錄都沒,你咋這麼懶呢 …嗯 其實呢,不是我懶,確實我看到許多博客文章有列個目錄,挺好的,但是呢,也有一些博客文章是沒列目錄,說實話,我更喜歡沒列目錄的,不是說更好,或者懶的藉口啊,只能說每個人都有各自喜好的寫作風格,而我是那種喜歡直接進入主題開始的,能把我帶進去的文章,所以我也希望用這種方式,讓正在看我文章的你能直接進入主題,把最核心的懟給你。

什麼是內部類:

一個類內部除了有屬性和方法,還可以有其他類,內部類也可以在方法或代碼塊中。 (往下會代碼示例)

可以簡單的理解爲 花括號裏 { } 裏面再定義一個類,那麼這個類就稱爲:內部類。 因爲理論上內部類可以在類任意位置,所以代碼塊也好,普通方法也可以。

內部類的作用:

1.增強封裝,把內部類放在外部類當中,不允許其它類訪問這個內部類。

2.增加了代碼一個維護性,不涉及到其他類。

3.內部類可以直接訪問外部類當中的成員。

內部類分爲四種:

  • 實例內部類:直接定義在類當中的一個類,在類前面沒有任何一個修飾符。
  • 靜態內部類:在內部類前面加上一個static。
  • 局部內部類:定義在方法當中的內部類。
  • 匿名內部類:屬於局部的一種特殊情況。

實例內部類

//外部類
//外部類
 class Outer {
	 String str = "一顆剽悍的種子";
	//內部類
	class Inner{
		void fun() {
			System.out.println(str);
		}
	}
}
public class Test2{
	public static void main(String[] args) {
		Outer out = new Outer();
		//實例內部類
		out.new Inner().fun();
	}
}

運行結果
在這裏插入圖片描述
上面代碼示例有個細節:就是我們提到過的內部類的作用, 內部類可以訪問外部類的成員。

實例化內部類的格式:

new 外部類().new 內部類 ();

代碼本身很簡單,只要理解類的語法和對象實例化方式就可以了,瞭解它的底層纔有趣,創建內部類的時候發生了什麼? 在創建內部類的時候,會有一個外部類的引用。

我們可以用代碼編譯後的class文件通過我們的反編譯工具來看。

class Outer$Inner
{
	final Outer this$0;

	void fun()
	{
		System.out.println(str);
	}

	Outer$Inner()
	{
		this$0 = Outer.this;
		super();
	}
}

從上面的代碼可以看到,內部類當中除了有自己的引用,還有外部類的引用。在堆中相當於外部類有一個地址,內部類有兩個地址,一個是自己內部類的,一個是指向外部類的地址。

靜態內部類

有接觸過Java 靜態這個知識點的小夥伴應該都知道,談到靜態無非就是對static關鍵字的理解和運用,這麼說你可能會好懂一些。

說得更直接點就是內部類前面加上一個 static 就是靜態內部類。

//外部類
 class Outer {
	static String str = "一顆剽悍的種子";
	//靜態內部類
	static class Inner{
		void fun() {
			System.out.println(str);
		}
	}
}
public class Test2{
	public static void main(String[] args) {
				new Outer.Inner().fun();;
	}
}

運行結果
在這裏插入圖片描述
注意點:可以看到上面成員也是用static聲明靜態,是因爲聲明static無法直接調用非static變量或方法。

爲什麼?

我們都知道static是最先執行,在加載字節碼就會自動調用,而且在主方法main之前,比構造方法早,此時非static屬性和方法還沒有完成初始化呢。

還有與上面非 static 定義內部類有兩點不同:

1.靜態內部類的創建格式不需要實例化,具體格式如下:

new 外部類(). 內部類 ();

2.靜態內部類當中沒有外部類的引用。

爲什麼?

因爲加上static 靜態內部類是屬於外部類的,上面所說也可以看出它沒有用new 而是通過 .類的方式。

還是通過我們的反編譯查看class文件,有碼有真相。

static class Outer$Inner
{
	void fun()
	{
		System.out.println(Outer.str);
	}
	Outer$Inner()
	{
	}
}

局部內部類

開頭說什麼是內部類當中,有說到內部類不僅可以定義在外部類當中,也可以定義在方法當中,而定義在方法當中的類稱爲局部內部類

//外部類
class Outer {
	void fun_1() {
	String  name = "一顆剽悍的種子";
		 class Inner{
			void fun_2() {
				System.out.print(name);
			}
		}
		 Inner in = new Inner();
		 in.fun_2();
	}
}
public class Test2{
	public static void main(String[] args) {
		new Outer().fun_1();
	}
}

運行結果
在這裏插入圖片描述

局部內部類有什麼侷限性呢?

1.局部內部類只能在定義方法中使用。 可以看到代碼示例,在方法中創建內部類並且調用內部類裏的方法,後續實例化只要調用外部類裏的 fun_1 方法就可以。

2.不能使用 public、protected、private 修飾 。 這一條是對應第一條的,你想想,你已經在方法中創建了,只在方法中使用,你還用這些權限幹嘛 ???

3.局部類當中不能使用static變量。 上面靜態內部類時說過,static是屬於外部類的,所以在方法裏的內部類定義static變量會報錯。

除了局部內部類的侷限性,還可以看到在代碼實例中,內部類是可以直接使用方法中的變量的。

(在jdk1.7之前內部類想訪問方法中的變量是需要加上final的,我們知道final是用來聲明常量的,fianl需要設置好值而且不能修改。)

jdk1.8版本之後不需要,正好我的jdk1.8 所以就不用加final了,但是真實情況下,通過反編譯工具看class文件會發現編譯時已經幫我們自動加上了。

(你會發現隨着語言的發展,讓你更易於開發的同時,你離本質也更加的遙遠)

class Outer$1Inner
{

	final Outer this$0;
	private final String val$name;

	void fun_2()
	{
		System.out.print(val$name);
	}

	Outer$1Inner()
	{
		this$0 = final_outer;
		val$name = String.this;
		super();
	}
}

到這裏可能有小夥伴會問,爲啥內部聲明變量需要加上final,這裏就不展開了,因爲涉及到 final 在方法區以及代碼運行後的堆棧關係,下次有機會再寫一篇。

匿名內部類

根據字面意思就知道沒有命名的內部類,那麼匿名內部類就是沒有構造器的,因爲我們都知道類的構造器需要跟類同樣的命名。

new 父類 或 接口

上面圍繞四種內部類展開,實例內部類,靜態內部類、局部內部類、匿名內部類,因爲匿名內部類的應用是比較少,所以簡單的談談就沒有代碼示例,等有用到,而且好的例子,可以再單獨寫一篇滴。

最後:

我是一顆剽悍的種子 把我會的,認真的分享 是我寫博客一直不變的信條。如果你能看到這篇,說明咱們還是很有緣的,希望能帶給你一些許幫助,創作的不易, 把我文章的知識帶走,你的三連留下,點贊,評論,關注,是我最大的動力哦。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章