前言
這幾天趁著時間多多,回顧并總結(jié)出來超全面的Java內(nèi)部類知識;Java內(nèi)部類老實說我們在開發(fā)的時候用的不多,然而正是因為用的不多,久而久之我們就忘了Java內(nèi)部類,所以才想寫這一篇博客,相信看了這篇博客之后,你絕對敢說學(xué)會了Java內(nèi)部類,如果遇到面試的時候,吹給面試官聽,很可能面試官就會對你刮目相看(面試官內(nèi)心獨白:這個小伙子不錯喲,Java內(nèi)部類用的不多都這么熟悉,那么經(jīng)常使用的技術(shù)是不是非常熟練?)。
下面先貼出一張超全的Java內(nèi)部類知識圖譜
上面思維導(dǎo)圖已經(jīng)大部分說明了Java內(nèi)部類,但是還是想把這篇博客的大綱列出來,這樣會更清晰一些。
Java內(nèi)部類知識大綱
- 什么是內(nèi)部類
- 內(nèi)部類間接體現(xiàn)Java多繼承?
- 四大內(nèi)部類
- 成員內(nèi)部類
- 靜態(tài)內(nèi)部類
- 局部(方法)內(nèi)部類
- 匿名內(nèi)部類
什么是內(nèi)部類
將一個類定義在另一個類的內(nèi)部,這就是內(nèi)部類。內(nèi)部類和普通類一樣,都是類,都可以定義屬性、方法 (包括構(gòu)造方法,靜態(tài)方法等等)。通常將內(nèi)部類分為4種:成員內(nèi)部類、靜態(tài)內(nèi)部類、局部(方法)內(nèi)部類、匿名內(nèi)部類。在這四種內(nèi)部類之中,有的內(nèi)部類可以定義靜態(tài)成員,有些內(nèi)部類就不能定義靜態(tài)成員,再下面將會一一說明。
內(nèi)部類間接體現(xiàn)Java多繼承?
我們都知道在Java中,一個類繼承另一個類,就會繼承那個類(父類、基類)公有成員(屬性、方法),如果外部類繼承一個類,內(nèi)部類也繼承一個類(內(nèi)部類也是類,可以繼承類,或者實現(xiàn)接口),又因為內(nèi)部類可以直接訪問外部類的成員(屬性、方法),所以內(nèi)部類也可以訪問外部類繼承父類的成員,所以說內(nèi)部類的出現(xiàn)間接體現(xiàn)Java多繼承(雖然最多是繼承兩個類,外部類一個,內(nèi)部類一個)。這個是我個人的見解,如果有不同的想法看法,可以評論交流一下下。
四種內(nèi)部類
雖然內(nèi)部類和普通的類一樣,都可以繼承類,實現(xiàn)接口,而且都可以定義成員(屬性,方法),但是它們之間還是有區(qū)別的;
比如成員內(nèi)部類就不能定義靜態(tài)成員(靜態(tài)變量,靜態(tài)方法),而靜態(tài)內(nèi)部類就可以定義靜態(tài)成員,下面將會一一介紹這四大內(nèi)部類,而且都會附帶完整的代碼說明,只講理論不給代碼都是不貼心的。
成員內(nèi)部類
我們在學(xué)面向?qū)ο蟮臅r候,應(yīng)該都知道static這個關(guān)鍵字,被static修飾(屬性、方法)就是類級別的了,也就是類成員(不依賴于對象,被該類所有對象共享),那么反過來不被static修飾就是對象成員了,所以成員內(nèi)部類就是不能被static修飾的,但是可以被四大權(quán)限修飾符修飾(public、private、...)。
外部類與成員內(nèi)部類
下面給出成員內(nèi)部類代碼,下面還會給出成員內(nèi)部類總結(jié),而這個總結(jié)就是從這個代碼里濃縮出來的(因為語法錯誤編譯器會報紅)。
代碼可能看起來有點長,可以復(fù)制到IDEA中慢慢細(xì)讀,相信會有收獲的。
/**
* 外部類內(nèi)部使用成員內(nèi)部類
* 1.成員內(nèi)部類可以繼承類,實現(xiàn)接口
* 2.成員內(nèi)部類不能創(chuàng)建靜態(tài)成員(靜態(tài)變量,靜態(tài)方法)
* 3.不能在外部類靜態(tài)方法內(nèi)部創(chuàng)建成員內(nèi)部類對象
* 4.如果外部類屬性與內(nèi)部類屬性同名時,
* 直接調(diào)用是訪問內(nèi)部類屬性,通過外部類名.this.屬性名訪問的是外部類屬性
* 其他類內(nèi)部使用成員內(nèi)部類
* 第一種方式:
* //創(chuàng)建外部類對象
* MemberInnerClass outer = new MemberInnerClass();
* //創(chuàng)建內(nèi)部類對象
* MemberInnerClass.InnerClass inner = outer.new InnerClass();
* 第二種方式:
* //鏈?zhǔn)絼?chuàng)建內(nèi)部類對象
* MemberInnerClass.InnerClass innerClass = new MemberInnerClass().new InnerClass();
*/
@Data
public class MemberInnerClass {
private Integer age = 22;
private String name = "xq";
private static String country = "中國";
public void outerMethod() {
System.out.println("我是外部類的成員方法!");
}
public static void outerStaticMethod() {
System.out.println("我是外部類的靜態(tài)方法!");
}
public class InnerClass {
/**
* 成員變量
*/
private Integer age = 18;
/**
* 成員內(nèi)部類不允許定義靜態(tài)變量
*/
// public static String name; //報錯
/**
* 構(gòu)造器
*/
public InnerClass() {
}
/**
* 內(nèi)部類成員方法,訪問外部類信息(屬性、方法)
*/
public void innerCallOuter() {
//當(dāng)內(nèi)部類屬性和外部類屬性不同名時,直接調(diào)用即可
System.out.println(name);
//當(dāng)內(nèi)部類屬性和外部類屬性同名時,訪問的是內(nèi)部類屬性
System.out.println("內(nèi)部類age屬性:" + age);
//當(dāng)內(nèi)部類屬性和外部類屬性同名時,可通過外部類名.this.屬性名
System.out.println("外部類age屬性:" + MemberInnerClass.this.age);
System.out.println("外部類靜態(tài)變量:" + country);
//訪問外部類的方法
outerMethod();
outerStaticMethod();
}
/**
* 成員內(nèi)部類不允許定義靜態(tài)方法,報錯
*/
/*public static void innerStaticMethod(){}*/
}
/**
* 外部類靜態(tài)方法不能創(chuàng)建內(nèi)部類對象,
* 也就是在外部類靜態(tài)方法內(nèi)訪問不了內(nèi)部類信息
* @param args
*/
/*public static void main(String[] args) {
InnerClass innerClass = new InnerClass();
}*/
/**
* 外部類非靜態(tài)方法創(chuàng)建內(nèi)部類對象,訪問內(nèi)部類信息
*/
public void outerCallInner() {
InnerClass innerClass = new InnerClass();
innerClass.innerCallOuter();
}
/**
* 外部類靜態(tài)方法,創(chuàng)建外部類對象,調(diào)用外部類成員方法(內(nèi)部訪問內(nèi)部類信息)
*
* @param args
*/
public static void main(String[] args) {
MemberInnerClass memberInnerClass = new MemberInnerClass();
memberInnerClass.outerCallInner();
}
}
其他類使用成員內(nèi)部類
public class MemberInnerClassTest {
public static void main(String[] args) {
//創(chuàng)建外部類對象
MemberInnerClass outer = new MemberInnerClass();
outer.outerCallInner();
System.out.println("=========================");
//創(chuàng)建內(nèi)部類對象
MemberInnerClass.InnerClass inner = outer.new InnerClass();
inner.innerCallOuter();
System.out.println("========================");
//鏈?zhǔn)絼?chuàng)建內(nèi)部類對象
MemberInnerClass.InnerClass innerClass = new MemberInnerClass().new InnerClass();
innerClass.innerCallOuter();
}
}
成員內(nèi)部類總結(jié)
- 成員內(nèi)部類可以被任何的訪問修飾符修飾。
- 成員內(nèi)部類的內(nèi)部不能定義靜態(tài)成員。
- 成員內(nèi)部類也是類,可以繼承類,可以實現(xiàn)接口,方法也可以重寫,重載,this和super隨便使用。
- 成員內(nèi)部類可以直接使用外部類的任何信息,如果屬性或者方法發(fā)生沖突,調(diào)用外部類.this.屬性或者方法。
- 其它類如何訪問成員內(nèi)部類,被public修飾的成員內(nèi)部類,可以被不同包的其他類訪問;其他情況和普通類一樣...
靜態(tài)內(nèi)部類
靜態(tài)內(nèi)部類就是static修飾的內(nèi)部類,也可以被四大權(quán)限修飾符修飾。
外部類定義以及使用靜態(tài)內(nèi)部類
下面代碼以及注釋非常清晰說明了靜態(tài)內(nèi)部類的特性。
/**
* 外部類&靜態(tài)內(nèi)部類
*/
public class StaticInnerClass {
//和內(nèi)部類屬性同名
private int age = 22;
private String outer = "outerClass";
private static String country = "china";
static {
System.out.println("外部類靜態(tài)代碼塊...");
}
public void outerMethod() {
System.out.println("我是外部類的成員方法!");
}
public static void outerStaticMethod() {
System.out.println("我是外部類的靜態(tài)方法!");
}
/**
* 靜態(tài)內(nèi)部類,需要使用static修飾
*/
public static class InnerClass {
private int age = 18;
private String inner = "innerClass";
//靜態(tài)內(nèi)部類可以定義靜態(tài)變量
private static String country = "中國";
static {
System.out.println("內(nèi)部類靜態(tài)代碼塊...");
}
public void innerMethod() {
//靜態(tài)內(nèi)部類不能訪問外部類非靜態(tài)成員屬性
// System.out.println("outer:"+outer); 報錯
System.out.println("inner:" + inner);
System.out.println("靜態(tài)內(nèi)部類age屬性:" + age);
//靜態(tài)類內(nèi)部不能通過這種方式訪問外部類的同名屬性
// System.out.println("外部類age屬性:"+StaticOuterClass.this.age);
System.out.println("靜態(tài)內(nèi)部類static屬性:" + country);
System.out.println("外部類static屬性:" + cn.zwq.innerclass.StaticInnerClass.country);
//靜態(tài)內(nèi)部類不能調(diào)用外部類成員方法
// outerMethod(); 報錯
//靜態(tài)內(nèi)部類可以調(diào)用外部類靜態(tài)方法
outerStaticMethod();
}
/**
* 靜態(tài)內(nèi)部類可以定義靜態(tài)方法
*/
public static void innerStaticMethod() {
// outerMethod(); 報錯
outerStaticMethod();
}
public static void main(String[] args) {
//訪問靜態(tài)內(nèi)部類靜態(tài)屬性
System.out.println(cn.zwq.innerclass.StaticInnerClass.InnerClass.country);
//訪問靜態(tài)內(nèi)部類靜態(tài)方法
cn.zwq.innerclass.StaticInnerClass.InnerClass.innerStaticMethod();
}
}
}
其他類使用靜態(tài)內(nèi)部類
創(chuàng)建靜態(tài)內(nèi)部類對象和創(chuàng)建成員內(nèi)部類對象稍微不同,可以和上面稍微對比一下就清晰了。
public class StaticInnerClassTest {
public static void main(String[] args) {
//創(chuàng)建靜態(tài)內(nèi)部類對象,和創(chuàng)建成員內(nèi)部類稍微不同
StaticInnerClass.InnerClass innerClass = new StaticInnerClass.InnerClass();
//訪問靜態(tài)內(nèi)部類方法(靜態(tài)、非靜態(tài))
innerClass.innerMethod();
innerClass.innerStaticMethod();
//直接調(diào)用靜態(tài)內(nèi)部類靜態(tài)屬性:外部類.靜態(tài)內(nèi)部類.靜態(tài)屬性(非私有的)
StaticInnerClass.InnerClass.innerStaticMethod();
}
}
靜態(tài)內(nèi)部類總結(jié)
- 靜態(tài)內(nèi)部類使用static修飾,可以定義非靜態(tài)成員,也可以定義靜態(tài)成員。
- 靜態(tài)內(nèi)部類中的方法(成員方法、靜態(tài)方法)只能訪問外部類的靜態(tài)成員,不能訪問外部類的非靜態(tài)成員。
- 靜態(tài)內(nèi)部類可以被4大權(quán)限修飾符修飾,被public修飾而任意位置的其他類都可以訪問,被private修飾只能被外部類內(nèi)部訪問。
- 靜態(tài)內(nèi)部類內(nèi)部的靜態(tài)成員,可以直接使用外部類.靜態(tài)內(nèi)部類.靜態(tài)成員訪問。
局部內(nèi)部類
局部內(nèi)部類是定義在方法內(nèi)部的,我們可以想一下,以前定義方法的時候,有哪些限制?
- 首先呢,是變量不能使用權(quán)限修飾符修飾,而局部內(nèi)部類就是方法內(nèi)部定義的變量,所以局部內(nèi)部類也不能使用權(quán)限修飾符修飾。
- 這里先列舉一條限制,下面還會給出更加詳細(xì)的總結(jié)。
/**
* 局部內(nèi)部類
*/
public class LocalInnerClass {
//和局部內(nèi)部類屬性同名
private int age = 22;
private String outer = "outerClass";
private static String country = "china";
public void outerMethod() {
System.out.println("我是外部類的成員方法!");
}
public static void outerStaticMethod() {
System.out.println("我是外部類的靜態(tài)方法!");
}
/**
* 外部類成員方法,內(nèi)部定義局部內(nèi)部類
*/
public void localInnerClass() {
String name = "java";
name = "javaEE";
//報錯,局部內(nèi)部類不能被權(quán)限修飾符修飾
/*public class InnerClass{
}*/
class InnerClass {
private String inner = "inner";
private int age = 18;
//報錯,局部內(nèi)部類不能定義靜態(tài)成員(屬性、方法)
// private static String country = "中國";
/*public static void innerStaticMethod(){
}*/
public void innerMethod() {
//報錯,因為局部內(nèi)部類訪問方法定義的變量,該變量必須是final修飾的
// System.out.println(name);報錯
System.out.println("局部內(nèi)部類inner屬性:" + inner);
//訪問外部類信息
System.out.println("外部類outer屬性:"+outer);
System.out.println("局部內(nèi)部類age屬性:"+age);
System.out.println("外部類age屬性:"+LocalInnerClass.this.age);
System.out.println("外部類靜態(tài)屬性country:"+country);
outerMethod();
outerStaticMethod();
}
}
/*
局部內(nèi)部類只能在聲明的方法內(nèi)部使用
*/
InnerClass innerClass = new InnerClass();
innerClass.innerMethod();
System.out.println(innerClass.age);
System.out.println(innerClass.inner);
}
}
局部內(nèi)部類總結(jié)
- 局部內(nèi)部類不能被權(quán)限修飾符修飾。
- 局部內(nèi)部類只能在方法內(nèi)部使用。
- 局部內(nèi)部類不能定義靜態(tài)成員。
- 局部內(nèi)部類可以直接訪問方法內(nèi)部的局部變量和方法參數(shù)。
- 局部內(nèi)部類可以訪問外部類的靜態(tài)成員、非靜態(tài)成員。
局部內(nèi)部類注意點(重點)
如果局部內(nèi)部類訪問方法內(nèi)部的局部變量、方法形參,那么就要求這些局部變量、方法形參被final修飾,否則會報錯。
下圖很好的說明了這個問題:
匿名內(nèi)部類
- 首先匿名內(nèi)部類也是內(nèi)部類的一種,只不過它沒名字。
- 匿名內(nèi)部類最常用的使用場景就是快速創(chuàng)建抽象類或接口的實例。
- 如果某個類判定只使用一次,那么就不要通過new關(guān)鍵字創(chuàng)建那個類的對象,而是使用匿名內(nèi)部類的方式。
- 匿名內(nèi)部類的格式:
new 實現(xiàn)接口() | 父類構(gòu)造器(實參列表){ //匿名內(nèi)部類類體部分 };
5.接下來使用匿名內(nèi)部類創(chuàng)建Runnable接口的實例,創(chuàng)建Thread類的實例。
public static void main(String[] args) {
//使用匿名內(nèi)部類創(chuàng)建接口實例
Runnable runnable = new Runnable() {
@Override
public void run() {
}
};
//使用匿名內(nèi)部類創(chuàng)建Thread類實例
Thread thread = new Thread(runnable,"小小線程"){
};
}
好了,到這里Java內(nèi)部類已經(jīng)說完了,相信你看了這篇之后,之后面試被問到,或者筆試題考到Java內(nèi)部類題目,都可以輕松解決了。
如果感覺OK的話,可以關(guān)注或者點贊博主我一下下,感謝!