Java內(nèi)部類及其實(shí)例化
在 Java 中,允許在一個(gè)類(或方法、語(yǔ)句塊)的內(nèi)部定義另一個(gè)類,稱為內(nèi)部類(Inner Class),有時(shí)也稱為嵌套類(Nested Class)。
內(nèi)部類和外層封裝它的類之間存在邏輯上的所屬關(guān)系,一般只用在定義它的類或語(yǔ)句塊之內(nèi),實(shí)現(xiàn)一些沒(méi)有通用意義的功能邏輯,在外部引用它時(shí)必須給出完整的名稱。
使用內(nèi)部類的主要原因有:
內(nèi)部類可以訪問(wèn)外部類中的數(shù)據(jù),包括私有的數(shù)據(jù)。
內(nèi)部類可以對(duì)同一個(gè)包中的其他類隱藏起來(lái)。
當(dāng)想要定義一個(gè)回調(diào)函數(shù)且不想編寫大量代碼時(shí),使用匿名(anonymous)內(nèi)部類比較便捷。
減少類的命名沖突。
請(qǐng)看下面的例子:
public class Outer { private int size; public class Inner { private int counter = 10; public void doStuff() { size++; } } public static void main(String args[]) { Outer outer = new Outer(); Inner inner = outer.new Inner(); inner.doStuff(); System.out.println(outer.size); System.out.println(inner.counter); // 編譯錯(cuò)誤,外部類不能訪問(wèn)內(nèi)部類的變量 System.out.println(counter); }}
這段代碼定義了一個(gè)外部類 Outer,它包含了一個(gè)內(nèi)部類 Inner。將錯(cuò)誤語(yǔ)句注釋掉,編譯,會(huì)生成兩個(gè) .class 文件:Outer.class 和 Outer$Inner.class。也就是說(shuō),內(nèi)部類會(huì)被編譯成獨(dú)立的字節(jié)碼文件。
內(nèi)部類是一種編譯器現(xiàn)象,與虛擬機(jī)無(wú)關(guān)。編譯器將會(huì)把內(nèi)部類翻譯成用 $ 符號(hào)分隔外部類名與內(nèi)部類名的常規(guī)類文件,而虛擬機(jī)則對(duì)此一無(wú)所知。
注意:必須先有外部類的對(duì)象才能生成內(nèi)部類的對(duì)象,因?yàn)閮?nèi)部類需要訪問(wèn)外部類中的成員變量,成員變量必須實(shí)例化才有意義。
內(nèi)部類是 Java 1.1 的新增特性,有些程序員認(rèn)為這是一個(gè)值得稱贊的進(jìn)步,但是內(nèi)部類的語(yǔ)法很復(fù)雜,嚴(yán)重破壞了良好的代碼結(jié)構(gòu), 違背了Java要比C++更加簡(jiǎn)單的設(shè)計(jì)理念。
內(nèi)部類看似增加了―些優(yōu)美有趣,實(shí)屬?zèng)]必要的特性,這是不是也讓Java開始走上了許多語(yǔ)言飽受折磨的毀滅性道路呢?本教程并不打算就這個(gè)問(wèn)題給予一個(gè)肯定的答案。
Java靜態(tài)內(nèi)部類、匿名內(nèi)部類、成員式內(nèi)部類和局部?jī)?nèi)部類
內(nèi)部類可以是靜態(tài)(static)的,可以使用 public、protected 和 private 訪問(wèn)控制符,而外部類只能使用 public,或者默認(rèn)。
成員式內(nèi)部類
在外部類內(nèi)部直接定義(不在方法內(nèi)部或代碼塊內(nèi)部)的類就是成員式內(nèi)部類,它可以直接使用外部類的所有變量和方法,即使是 private 的。外部類要想訪問(wèn)內(nèi)部類的成員變量和方法,則需要通過(guò)內(nèi)部類的對(duì)象來(lái)獲取。
請(qǐng)看下面的代碼:
public class Outer{ private int size; public class Inner { public void dostuff() { size++; } } public void testTheInner() { Inner in = new Inner(); in.dostuff(); }}
成員式內(nèi)部類如同外部類的一個(gè)普通成員。
成員式內(nèi)部類可以使用各種修飾符,包括 public、protected、private、static、final 和 abstract,也可以不寫。
若有 static 修飾符,就為類級(jí),否則為對(duì)象級(jí)。類級(jí)可以通過(guò)外部類直接訪問(wèn),對(duì)象級(jí)需要先生成外部的對(duì)象后才能訪問(wèn)。
非靜態(tài)內(nèi)部類中不能聲明任何 static 成員。
內(nèi)部類可以相互調(diào)用,例如:
class A { // B、C 間可以互相調(diào)用 class B {} class C {}}
成員式內(nèi)部類的訪問(wèn)
內(nèi)部類的對(duì)象以成員變量的方式記錄其所依賴的外層類對(duì)象的引用,因而可以找到該外層類對(duì)象并訪問(wèn)其成員。該成員變量是系統(tǒng)自動(dòng)為非 static 的內(nèi)部類添加的,名稱約定為“outClassName.this”。
1) 使用內(nèi)部類中定義的非靜態(tài)變量和方法時(shí),要先創(chuàng)建外部類的對(duì)象,再由“outObjectName.new”操作符創(chuàng)建內(nèi)部類的對(duì)象,再調(diào)用內(nèi)部類的方法,如下所示:
public class Demo{ public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); inner.dostuff(); }}class Outer{ private int size; class Inner{ public void dostuff() { size++; } }}
2) static 內(nèi)部類相當(dāng)于其外部類的 static 成員,它的對(duì)象與外部類對(duì)象間不存在依賴關(guān)系,因此可直接創(chuàng)建。示例如下:
public class Demo{ public static void main(String[] args) { Outer.Inner inner = new Outer.Inner(); inner.dostuff(); }}class Outer{ private static int size; static class Inner { public void dostuff() { size++; System.out.println("size=" + size); } }}
運(yùn)行結(jié)果:
size=1
3) 由于內(nèi)部類可以直接訪問(wèn)其外部類的成分,因此當(dāng)內(nèi)部類與其外部類中存在同名屬性或方法時(shí),也將導(dǎo)致命名沖突。所以在多層調(diào)用時(shí)要指明,如下所示:
public class Outer{ private int size; public class Inner{ private int size; public void dostuff(int size){ size++; // 局部變量 size; this.size; // 內(nèi)部類的 size Outer.this.size++; // 外部類的 size } }}
局部?jī)?nèi)部類
局部?jī)?nèi)部類(Local class)是定義在代碼塊中的類。它們只在定義它們的代碼塊中是可見的。
局部類有幾個(gè)重要特性:
局部類可以是 abstract 的。
請(qǐng)看下面的代碼:
public class Outer { public static final int TOTAL_NUMBER = 5; public int id = 123; public void func() { final int age = 15; String str = "http://www.weixueyuan.net"; class Inner { public void innerTest() { System.out.println(TOTAL_NUMBER); System.out.println(id); // System.out.println(str);不合法,只能訪問(wèn)本地方法的final變量 System.out.println(age); } } new Inner().innerTest(); } public static void main(String[] args) { Outer outer = new Outer(); outer.func(); }}
運(yùn)行結(jié)果:
512315
匿名內(nèi)部類
匿名內(nèi)部類是局部?jī)?nèi)部類的一種特殊形式,也就是沒(méi)有變量名指向這個(gè)類的實(shí)例,而且具體的類實(shí)現(xiàn)會(huì)寫在這個(gè)內(nèi)部類里面。
注意:匿名類必須繼承一個(gè)父類或?qū)崿F(xiàn)一個(gè)接口。
不使用匿名內(nèi)部類來(lái)實(shí)現(xiàn)抽象方法:
abstract class Person { public abstract void eat();}class Child extends Person { public void eat() { System.out.println("eat something"); }}public class Demo { public static void main(String[] args) { Person p = new Child(); p.eat(); }}
運(yùn)行結(jié)果:
eat something
可以看到,我們用Child繼承了Person類,然后實(shí)現(xiàn)了Child的一個(gè)實(shí)例,將其向上轉(zhuǎn)型為Person類的引用。但是,如果此處的Child類只使用一次,那么將其編寫為獨(dú)立的一個(gè)類豈不是很麻煩?
這個(gè)時(shí)候就引入了匿名內(nèi)部類。使用匿名內(nèi)部類實(shí)現(xiàn):
abstract class Person { public abstract void eat();}public class Demo { public static void main(String[] args){ // 繼承 Person 類 new Person() { public void eat() { System.out.println("eat something"); } }.eat(); }}
可以看到,匿名類繼承了 Person 類并在大括號(hào)中實(shí)現(xiàn)了抽象類的方法。
內(nèi)部類的語(yǔ)法比較復(fù)雜,實(shí)際開發(fā)中也較少用到,本教程不打算進(jìn)行深入講解,各位讀者也不應(yīng)該將內(nèi)部類作為學(xué)習(xí)Java的重點(diǎn)。
新聞熱點(diǎn)
疑難解答
圖片精選