Java內部類別(Inner Class),類似的概念在C++裡也有,那就是嵌套類別(Nested Class),乍看上去內部類別似乎有些多餘,它的用處對於初學者來說可能並不是那麼顯著,但隨著對它的深入了解,你會發現Java的設計者在內部類別身上的確是用心良苦。學會使用內部類,是掌握Java高級程式設計的一部分,它可以讓你更優雅地設計你的程式結構。以下從以下幾個面向來介紹:
第一次見面
public interface Contents { int value(); } public interface Destination { String readLabel(); } public class Goods { private class Content implements Contents { private int i = 11; public int value() { return i; } } protected class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } public Destination dest(String s) { return new GDestination(s); } public Contents cont() { return new Content(); } } class TestGoods { public static void main(String[] args) { Goods p = new Goods(); Contents c = p.cont(); Destination d = p.dest("Beijing"); } }
在這個例子裡類Content和GDestination被定義在了類Goods內部,並且分別有著protected和private修飾符來控制訪問級別。 Content代表著Goods的內容,而GDestination代表著Goods的目的地。它們分別實作了兩個介面Content和Destination。在後面的main方法裡,直接用Contents c和Destination d來操作,你甚至連這兩個內部類別的名字都沒看見!這樣,內部類別的第一個好處就體現出來了──隱藏你不想讓別人知道的操作,也也就是封裝性。
同時,我們也發現了在外部類別作用範圍之外得到內部類別物件的第一個方法,那就是利用其外部類別的方法來建立並回傳。上例中的cont()和dest()方法就是這麼做的。那麼還有沒有別的方法呢?當然有,其語法格式如下:
outerObject=new outerClass(Constructor Parameters); outerClass.innerClass innerObject=outerObject.new InnerClass(Constructor Parameters);
注意在建立非靜態內部類別物件時,一定要先建立起對應的外部類別物件。至於原因,也引出了我們下一個主題-
非靜態內部類別物件有著指向其外部類別物件的引用
對剛才的例子稍作修改:
public class Goods { private valueRate=2; private class Content implements Contents { private int i = 11*valueRate; public int value() { return i; } } protected class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } public Destination dest(String s) { return new GDestination(s); } public Contents cont() { return new Content(); } }
修改的部分以紅色顯示了。這裡我們為Goods類別增加了一個private成員變數valueRate,意義是貨物的價值係數,在內部類別Content的方法value()計算價值時把它乘上來。我們發現,value()可以存取valueRate,這也是內部類別的第二個好處——一個內部類別物件可以存取創建它的外部類別物件的內容, 甚至包括私有變數!這是一個非常有用的特性,為我們在設計時提供了更多的想法和捷徑。要實現這個功能,內部類別物件就必須有指向外部類別物件的參考。 Java編譯器在創建內部類別物件時,隱式的把其外部類別物件的引用也傳了進去並且一直保存著。這樣就使得內部類別物件始終可以存取其外部類別對象,同時這也是為什麼在外部類別作用範圍之外向要建立內部類別物件必須先建立其外部類別物件的原因。
有人會問,如果內部類別裡的一個成員變數與外部類別的一個成員變數同名,也也就是外部類別的同名成員變數被封鎖了,怎麼辦?沒事,Java裡用以下格式表達外部類別的引用:
outerClass.this
有了它,我們就不怕這種屏蔽的情況了。
靜態內部類別
和普通的類別一樣,內部類別也可以有靜態的。不過和非靜態內部類別相比,差異就在於靜態內部類別沒有了指向外部的引用。這其實和C++中的巢狀類別很相像了,Java內部類別與C++嵌套類別最大的不同就在於是否有指向外部的引用這一點上,當然從設計的角度以及以它一些細節來講還有差別。
除此之外,在任何非靜態內部類別中,都不能有靜態數據,靜態方法或另一個靜態內部類別(內部類別的巢狀可以不只一層)。不過靜態內部類別中卻可以擁有這一切。這也算是兩者的第二個差別吧。
局部內部類別
是的,Java內部類別也可以是局部的,它可以定義在一個方法甚至一個程式碼區塊之內。
public class Goods1 { public Destination dest(String s) { class GDestination implements Destination { private String label; private GDestination(String whereTo) { label = whereTo; } public String readLabel() { return label; } } return new GDestination(s); } public static void main(String[] args) { Goods1 g= new Goods1(); Destination d = g.dest("Beijing"); } }
上面就是一個例子。在方法dest中我們定義了一個內部類,最後由這個方法傳回這個內部類別的物件。如果我們在用一個內部類別的時候只需要創建它的一個物件並創造給外部,就可以這樣做。當然,定義在方法中的內部類別可以使設計多樣化,用途絕不僅僅在這一點上。
下面有一個更怪的例子:
public class Goods2{ private void internalTracking(boolean b) { if(b) { class TrackingSlip { private String id; TrackingSlip(String s) { id = s; } String getSlip() { return id; } } TrackingSlip ts = new TrackingSlip("slip"); String s = ts.getSlip(); } } public void track() { internalTracking(true); } public static void main(String[] args) { Goods2 g= new Goods2(); g.track(); } }
你不能在if之外創建這個內部類別的對象,因為這已經超出了它的作用域。不過在編譯的時候,內部類別TrackingSlip和其他類別一樣同時被編譯,只不過它由它自己的作用域,超出了這個範圍就無效,除此之外它和其他內部類別並沒有區別。
匿名內部類別
java的匿名內部類別的語法規則看起來有些古怪,不過如同匿名數組一樣,當你只需要創建一個類別的物件而且用不上它的名字時,使用內部類別可以使程式碼看起來簡潔清楚。它的文法規則是這樣的:
new interfacename(){......}; 或 new superclassname(){......};
下面接著前面繼續舉例:
public class Goods3 { public Contents cont(){ return new Contents(){ private int i = 11; public int value() { return i; } }; } }
这里方法cont()使用匿名内部类直接返回了一个实现了接口Contents的类的对象,看上去的确十分简洁。
在java的事件处理的匿名适配器中,匿名内部类被大量的使用。例如在想关闭窗口时加上这样一句代码:
frame.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } });
有一点需要注意的是,匿名内部类由于没有名字,所以它没有构造函数(但是如果这个匿名内部类继承了一个只含有带参数构造函数的父类,创建它的时候必须带上这些参数,并在实现的过程中使用super关键字调用相应的内容)。如果你想要初始化它的成员变量,有下面几种方法:
如果是在一个方法的匿名内部类,可以利用这个方法传进你想要的参数,不过记住,这些参数必须被声明为final。
将匿名内部类改造成有名字的局部内部类,这样它就可以拥有构造函数了。
在这个匿名内部类中使用初始化代码块。
为什么需要内部类?
java内部类有什么好处?为什么需要内部类?
首先举一个简单的例子,如果你想实现一个接口,但是这个接口中的一个方法和你构想的这个类中的一个方法的名称,参数相同,你应该怎么办?这时候,你 可以建一个内部类实现这个接口。由于内部类对外部类的所有内容都是可访问的,所以这样做可以完成所有你直接实现这个接口的功能。
不过你可能要质疑,更改一下方法的不就行了吗?
的确,以此作为设计内部类的理由,实在没有说服力。
真正的原因是这样的,java中的内部类和接口加在一起,可以的解决常被C++程序员抱怨java中存在的一个问题——没有多继承。实际上,C++的多继承设计起来很复杂,而java通过内部类加上接口,可以很好的实现多继承的效果。
java内部类总结
(1) 在方法间定义的非静态内部类:
● 外围类和内部类可互相访问自己的私有成员。
● 内部类中不能定义静态成员变量。
在外部类作用范围之外向要创建内部类对象必须先创建其外部类对象
(2) 在方法间定义的静态内部类:
● 只能访问外部类的静态成员。
静态内部类没有了指向外部的引用
(3) 在方法中定义的局部内部类:
● 该内部类没有任何的访问控制权限
● 外围类看不见方法中的局部内部类的,但是局部内部类可以访问外围类的任何成员。
● 方法体中可以访问局部内部类,但是访问语句必须在定义局部内部类之后。
● 局部内部类只能访问方法体中的常量,即用final修饰的成员。
(4) 在方法中定义的匿名内部类:
● 没有构造器,取而代之的是将构造器参数传递给超类构造器
当你只需要创建一个类的对象而且用不上它的名字时,使用匿名内部类可以使代码看上去简洁清楚。
更多Java中的内部类和匿名类 相关文章请关注PHP中文网!