可能有一些小夥伴會問我,不是說詳細嗎?連個目錄都沒,你咋這麼懶呢 …嗯 其實呢,不是我懶,確實我看到許多博客文章有列個目錄,挺好的,但是呢,也有一些博客文章是沒列目錄,說實話,我更喜歡沒列目錄的,不是說更好,或者懶的藉口啊,只能說每個人都有各自喜好的寫作風格,而我是那種喜歡直接進入主題開始的,能把我帶進去的文章,所以我也希望用這種方式,讓正在看我文章的你能直接進入主題,把最核心的懟給你。
什麼是內部類:
一個類內部除了有屬性和方法,還可以有其他類,內部類也可以在方法或代碼塊中。 (往下會代碼示例)
可以簡單的理解爲 花括號裏 { } 裏面再定義一個類,那麼這個類就稱爲:內部類。 因爲理論上內部類可以在類任意位置,所以代碼塊也好,普通方法也可以。
內部類的作用:
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 父類 或 接口
上面圍繞四種內部類展開,實例內部類,靜態內部類、局部內部類、匿名內部類,因爲匿名內部類的應用是比較少,所以簡單的談談就沒有代碼示例,等有用到,而且好的例子,可以再單獨寫一篇滴。
最後:
我是一顆剽悍的種子 把我會的,認真的分享 是我寫博客一直不變的信條。如果你能看到這篇,說明咱們還是很有緣的,希望能帶給你一些許幫助,創作的不易, 把我文章的知識帶走,你的三連留下,點贊,評論,關注,是我最大的動力哦。