一個類內部除了有屬性和方法,還可以有其他類,內部類也可以在方法或程式碼塊中。 (往下會程式碼示例)
可以簡單的理解為花括號裡 { } 裡面再定義一個類,那麼這個類就稱為:內部類。 因為理論上內部類可以在類任意位置,所以程式碼塊也好,普通方法也可以。
內部類的作用: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 父類 或 介面
上面圍繞四種內部類展開,例項內部類,靜態內部類、區域性內部類、匿名內部類,因為匿名內部類的應用是比較少,所以簡單的談談就沒有程式碼示例,等有用到,而且好的例子,可以再單獨寫一篇滴。