概念介紹
- 內(nèi)部類
在Java中,可以將一個(gè)類定義在另一個(gè)類里面或者一個(gè)方法里面,這樣的類稱為內(nèi)部類。內(nèi)部類是一種非常有用的特性,因?yàn)樗试S你把一些邏輯相關(guān)的類組織在一起,并控制位于內(nèi)部的類的可視性。
內(nèi)部類分類
廣泛意義上的內(nèi)部類一般分為以下幾種類型:
- 成員內(nèi)部類
最普通的內(nèi)部類,它定義在另一個(gè)類的內(nèi)部,如下所示
//外部類
class Out {
private int age = 12;
//內(nèi)部類
class In {
public void print() {
System.out.println(age);
}
}
}
public class NormalInnerClass {
public static void main(String[] args) {
Out.In in = new Out().new In();
in.print();
//或者采用下種方式訪問(wèn)
/*
Out out = new Out();
Out.In in = out.new In();
in.print();
*/
}
}
- 局部?jī)?nèi)部類
局部?jī)?nèi)部類是定義在一個(gè)方法或者一個(gè)作用域里面的類,它和成員內(nèi)部類的區(qū)別在于局部?jī)?nèi)部類的訪問(wèn)僅限于方法內(nèi)或者該作用域內(nèi)。
interface extendInnerClass{
public int setAge();
}
//外部類
class Out {
private int age = 12;
public extendInnerClass getInnerClass(){
// 局部?jī)?nèi)部類
class innerClass implements extendInnerClass{
public int setAge(){
age++;
return age;
}
}
return new innerClass();
}
}
public class NormalInnerClass {
public static void main(String[] args) {
Out out = new Out();
System.out.println(out.getInnerClass().setAge());
}
}
注意,局部?jī)?nèi)部類就像是方法里面的一個(gè)局部變量一樣,是不能有public、protected、private以及static修飾符的。
- 靜態(tài)內(nèi)部類
class Out3 {
private static int age = 12;
static class In {
public void print() {
System.out.println(age);
}
}
}
public class staticInnerClass {
public static void main(String[] args) {
Out3.In in = new Out3.In();
in.print();
}
}
可以看到,如果用static 將內(nèi)部?jī)?nèi)靜態(tài)化,那么內(nèi)部類就只能訪問(wèn)外部類的靜態(tài)成員變量,具有局限性
其次,因?yàn)閮?nèi)部類被靜態(tài)化,因此Out.In可以當(dāng)做一個(gè)整體看,可以直接new 出內(nèi)部類的對(duì)象(通過(guò)類名訪問(wèn)static,生不生成外部類對(duì)象都沒(méi)關(guān)系)
- 匿名內(nèi)部類
interface anonymous{
public void getAnonymousAge();
}
class Out4 {
private static int age = 12;
public anonymous getAnonymous(){
return new anonymous(){
@Override
public void getAnonymousAge() {
System.out.println(age);
}
};
}
}
public class anonymousInnerClass {
public static void main(String[] args) {
Out4 out = new Out4();
out.getAnonymous().getAnonymousAge();
}
}
匿名內(nèi)部類是唯一一種沒(méi)有構(gòu)造器的類。正因?yàn)槠錄](méi)有構(gòu)造器,所以匿名內(nèi)部類的使用范圍非常有限,大部分匿名內(nèi)部類用于接口回調(diào)。匿名內(nèi)部類在編譯的時(shí)候由系統(tǒng)自動(dòng)起名為Outter$1.class。一般來(lái)說(shuō),匿名內(nèi)部類用于繼承其他類或是實(shí)現(xiàn)接口,并不需要增加額外的方法,只是對(duì)繼承方法的實(shí)現(xiàn)或是重寫(xiě)。
深入理解內(nèi)部類
為什么成員內(nèi)部類可以無(wú)條件訪問(wèn)外部類的成員?
編譯器會(huì)默認(rèn)為成員內(nèi)部類添加了一個(gè)指向外部類對(duì)象的引用,那么這個(gè)引用是如何賦初值的呢?
雖然我們?cè)诙x的內(nèi)部類的構(gòu)造器是無(wú)參構(gòu)造器,編譯器還是會(huì)默認(rèn)添加一個(gè)參數(shù),該參數(shù)的類型為指向外部類對(duì)象的一個(gè)引用,所以成員內(nèi)部類中的Outter this&0 指針便指向了外部類對(duì)象,因此可以在成員內(nèi)部類中隨意訪問(wèn)外部類的成員。從這里也間接說(shuō)明了成員內(nèi)部類是依賴于外部類的,如果沒(méi)有創(chuàng)建外部類的對(duì)象,則無(wú)法對(duì)Outter this&0引用進(jìn)行初始化賦值,也就無(wú)法創(chuàng)建成員內(nèi)部類的對(duì)象了。為什么局部?jī)?nèi)部類和匿名內(nèi)部類只能訪問(wèn)局部final變量?
示例代碼:
public class Test {
public static void main(String[] args) {
}
public void test(final int a) {
new Thread(){
public void run() {
System.out.println(a);
};
}.start();
}
}
如果局部變量的值在編譯期間就可以確定,則直接在匿名內(nèi)部里面創(chuàng)建一個(gè)拷貝。如果局部變量的值無(wú)法在編譯期間確定,則通過(guò)構(gòu)造器傳參的方式來(lái)對(duì)拷貝進(jìn)行初始化賦值。
為了解決數(shù)據(jù)不一致性問(wèn)題,java編譯器就限定必須將變量a限制為final變量,不允許對(duì)變量a進(jìn)行更改(對(duì)于引用類型的變量,是不允許指向新的對(duì)象),這樣數(shù)據(jù)不一致性的問(wèn)題就得以解決了。
- 靜態(tài)內(nèi)部類有特殊的地方嗎?
從前面可以知道,靜態(tài)內(nèi)部類是不依賴于外部類的,也就說(shuō)可以在不創(chuàng)建外部類對(duì)象的情況下創(chuàng)建內(nèi)部類的對(duì)象。另外,靜態(tài)內(nèi)部類是不持有指向外部類對(duì)象的引用的,這個(gè)讀者可以自己嘗試反編譯class文件看一下就知道了,是沒(méi)有Outter this&0引用的。
常考知識(shí)點(diǎn)
Static Nested Class 和 Inner Class的不同?
在方法外部定義的內(nèi)部類前面可以加上static關(guān)鍵字,從而成為Static Nested Class,它不再具有內(nèi)部類的特性,所有,從狹義上講,它不是內(nèi)部類。Static Nested Class與普通類在運(yùn)行時(shí)的行為和功能上沒(méi)有什么區(qū)別,只是在編程引用時(shí)的語(yǔ)法上有一些差別,它可以定義成public、protected、默認(rèn)的、private等多種類型,而普通類只能定義成public和默認(rèn)的這兩種類型。在外面引用Static Nested Class類的名稱為“外部類名.內(nèi)部類名”。在外面不需要?jiǎng)?chuàng)建外部類的實(shí)例對(duì)象,就可以直接創(chuàng)建Static Nested Class,例如,假設(shè)Inner是定義在Outer類中的Static Nested Class,那么可以使用如下語(yǔ)句創(chuàng)建Inner類:
Outer.Inner inner = new Outer.Inner();
由于static Nested Class不依賴于外部類的實(shí)例對(duì)象,所以,static Nested Class能訪問(wèn)外部類的非static成員變量。當(dāng)在外部類中訪問(wèn)Static Nested Class時(shí),可以直接使用Static Nested Class的名字,而不需要加上外部類的名字了,在Static Nested Class中也可以直接引用外部類的static的成員變量,不需要加上外部類的名字。
在靜態(tài)方法中定義的內(nèi)部類也是Static Nested Class,這時(shí)候不能在類前面加static關(guān)鍵字,靜態(tài)方法中的Static Nested Class與普通方法中的內(nèi)部類的應(yīng)用方式很相似,它除了可以直接訪問(wèn)外部類中的static的成員變量,還可以訪問(wèn)靜態(tài)方法中的局部變量,但是,該局部變量前必須加final修飾符。內(nèi)部類可以引用它的包含類的成員嗎?有沒(méi)有什么限制?
完全可以。如果不是靜態(tài)內(nèi)部類,那沒(méi)有什么限制!
如果你把靜態(tài)嵌套類當(dāng)作內(nèi)部類的一種特例,那在這種情況下不可以訪問(wèn)外部類的普通成員變量,而只能訪問(wèn)外部類中的靜態(tài)成員,例如,下面的代碼:
class Outer {
static int x;
static class Inner {
void test() {
syso(x);
}
}
}
參考文獻(xiàn)
[1]java中的內(nèi)部類總結(jié)
[2]Java中為什么要使用內(nèi)部類
[3]Java內(nèi)部類詳解