可以將一個類別的定義放在另一個類別的定義內部,這就是內部類別。
內部類別是非常有用的特性但又比較難理解使用的特性(鄙人到現在都沒有怎麼使用過內部類,對內部類別也只是略知一二)。
第一次見面
內部類別我們從外面看是非常容易理解的,無非就是在一個類別的內部定義一個類別。
public class OuterClass { private String name ; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } class InnerClass{ public InnerClass(){ name = "chenssy"; age = 23; } } }
在這裡InnerClass就是內部類,對於初學者來說內部類實在是使用的不多,鄙人菜鳥一個同樣沒有怎麼使用過(貌似僅僅只在做swing 註冊事件中使用過),但是隨著編程能力的提高,我們會領悟到它的魅力所在,它可以使用能夠更加優雅的設計我們的程式結構。在使用內部類別之間我們需要明白為什麼要使用內部類別,內部類別能夠為我們帶來什麼樣的好處。
一、為什麼要使用內部類別
為什麼要使用內部類別?在《Think in Java》中有這樣一句話:使用內部類別最吸引人的原因是:每個內部類別都能獨立地繼承一個(介面的)實現,所以無論外圍類別是否已經繼承了某個(接口的)實現,對於內部類別都沒有影響。
在我們程式設計中有時候會存在一些使用介面很難解決的問題,而這個時候我們可以利用內部類別提供的、可以繼承多個具體的或是抽象的類別的能力來解決這些程式設計問題。可以這樣說,介面只是解決了部分問題,而內部類別使得多重繼承的解決方案變得更加完整。
public interface Father { } public interface Mother { } public class Son implements Father, Mother { } public class Daughter implements Father{ class Mother_ implements Mother{ } }
其實對於這個實例我們確實是看不出來使用內部類別存在何種優點,但是如果Father、Mother不是接口,而是抽象類別或者俱體類別呢?這時候我們就只能使用內部類別才能實現多重繼承了。
其實使用內部類別最大的優點就在於它能夠非常好的解決多重繼承的問題,但是如果我們不需要解決多重繼承問題,那麼我們自然可以使用其他的編碼方式,但是使用內部類別還能夠為我們帶來以下特性(摘自《Think in java》):
1、內部類別可以用多個實例,每個實例都有自己的狀態訊息,並且與其他外圍物件的資訊相互獨立。
2、在單一外圍類別中,可以讓多個內部類別以不同的方式實現同一個接口,或繼承同一個類別。
3、創建內部類別物件的時刻並不依賴於外圍類別物件的建立。
4、內部類別並沒有令人困惑的「is-a」關係,他就是一個獨立的實體。
5、內部類別提供了更好的封裝,除了該外圍類,其他類別都不能訪問。
二、內外部類別基礎
在這個部分中主要介紹內部類別如何使用外部類別的屬性與方法,以及使用.this與.new。
當我們在創建一個內部類別的時候,它無形中就與外圍類有了一種聯繫,依賴於這種聯繫,它可以無限制地訪問外圍類的元素。
public class OuterClass { private String name ; private int age; /**省略getter和setter方法**/ public class InnerClass{ public InnerClass(){ name = "chenssy"; age = 23; } public void display(){ System.out.println("name:" + getName() +" ;age:" + getAge()); } } public static void main(String[] args) { OuterClass outerClass = new OuterClass(); OuterClass.InnerClass innerClass = outerClass.new InnerClass(); innerClass.display(); } } -------------- Output: name:chenssy ;age:23
在這個應用程式中,我們可以看到內部了InnerClass可以對外圍類OuterClass的屬性進行無縫的訪問,儘管它是private修飾的。這是因為當我們在創建某個外圍類別的內部類別物件時,此時內部類別物件必定會捕獲一個指向那個外圍類別物件的引用,只要我們在存取外圍類別的成員時,就會用這個引用來選擇外圍類的成員。
其實在這個應用程式中我們也看到如何來引用內部類別:引用內部類別我們需要指明這個物件的類型:OuterClasName.InnerClassName。同時如果我們需要建立某個內部類別對象,必須要利用外部類別的物件透過.new來建立內部類別:OuterClass.InnerClass innerClass = outerClass.new InnerClass();。
同時如果我們需要產生外部類別物件的引用,可以使用OuterClassName.this,這樣就能夠產生一個正確引用外部類別的引用了。當然這點實在編譯期就知道了,沒有任何運行時的成本。
public class OuterClass { public void display(){ System.out.println("OuterClass..."); } public class InnerClass{ public OuterClass getOuterClass(){ return OuterClass.this; } } public static void main(String[] args) { OuterClass outerClass = new OuterClass(); OuterClass.InnerClass innerClass = outerClass.new InnerClass(); innerClass.getOuterClass().display(); } } ------------- Output: OuterClass...
到这里了我们需要明确一点,内部类是个编译时的概念,一旦编译成功后,它就与外围类属于两个完全不同的类(当然他们之间还是有联系的)。对于一个名为OuterClass的外围类和一个名为InnerClass的内部类,在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$InnerClass.class。
在Java中内部类主要分为成员内部类、局部内部类、匿名内部类、静态内部类。
三、成员内部类
成员内部类也是最普通的内部类,它是外围类的一个成员,所以他是可以无限制的访问外围类的所有 成员属性和方法,尽管是private的,但是外围类要访问内部类的成员属性和方法则需要通过内部类实例来访问。
在成员内部类中要注意两点,第一:成员内部类中不能存在任何static的变量和方法;第二:成员内部类是依附于外围类的,所以只有先创建了外围类才能够创建内部类。
public class OuterClass { private String str; public void outerDisplay(){ System.out.println("outerClass..."); } public class InnerClass{ public void innerDisplay(){ //使用外围内的属性 str = "chenssy..."; System.out.println(str); //使用外围内的方法 outerDisplay(); } } /*推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 */ public InnerClass getInnerClass(){ return new InnerClass(); } public static void main(String[] args) { OuterClass outer = new OuterClass(); OuterClass.InnerClass inner = outer.getInnerClass(); inner.innerDisplay(); } } -------------------- chenssy... outerClass...
推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时 。
四、局部内部类
有这样一种内部类,它是嵌套在方法和作用于内的,对于这个类的使用主要是应用与解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类,局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法和属性中被使用,出了该方法和属性就会失效。
对于局部内部类实在是想不出什么好例子,所以就引用《Think in java》中的经典例子了。
定义在方法里:
public class Parcel5 { public Destionation destionation(String str){ class PDestionation implements Destionation{ private String label; private PDestionation(String whereTo){ label = whereTo; } public String readLabel(){ return label; } } return new PDestionation(str); } public static void main(String[] args) { Parcel5 parcel5 = new Parcel5(); Destionation d = parcel5.destionation("chenssy"); } }
定义在作用域内:
public class Parcel6 { 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("chenssy"); String string = ts.getSlip(); } } public void track(){ internalTracking(true); } public static void main(String[] args) { Parcel6 parcel6 = new Parcel6(); parcel6.track(); } }
五、匿名内部类
在做Swing编程中,我们经常使用这种方式来绑定事件
button2.addActionListener( new ActionListener(){ public void actionPerformed(ActionEvent e) { System.out.println("你按了按钮二"); } });
我们咋一看可能觉得非常奇怪,因为这个内部类是没有名字的,在看如下这个例子:
public class OuterClass { public InnerClass getInnerClass(final int num,String str2){ return new InnerClass(){ int number = num + 3; public int getNumber(){ return number; } }; /* 注意:分号不能省 */ } public static void main(String[] args) { OuterClass out = new OuterClass(); InnerClass inner = out.getInnerClass(2, "chenssy"); System.out.println(inner.getNumber()); } } interface InnerClass { int getNumber(); } ---------------- Output: 5
这里我们就需要看清几个地方
1、 匿名内部类是没有访问修饰符的。
2、 new 匿名内部类,这个类首先是要存在的。如果我们将那个InnerClass接口注释掉,就会出现编译出错。
3、 注意getInnerClass()方法的形参,第一个形参是用final修饰的,而第二个却没有。同时我们也发现第二个形参在匿名内部类中没有使用过,所以当所在方法的形参需要被匿名内部类使用,那么这个形参就必须为final。
4、 匿名内部类是没有构造方法的。因为它连名字都没有何来构造方法。
PS:由于篇幅有限,对匿名内部类就介绍到这里,有关更多关于匿名内部类的知识,我就会在下篇博客(java提高篇-----详解匿名内部类)做详细的介绍,包括为何形参要定义成final,怎么对匿名内部类进行初始化等等,敬请期待……
六、静态内部类
在java提高篇-----关键字static中提到Static可以修饰成员变量、方法、代码块,其他它还可以修饰内部类,使用static修饰的内部类我们称之为静态内部类,不过我们更喜欢称之为嵌套内部类。静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围内,但是静态内部类却没有。没有这个引用就意味着:
1、 它的创建是不需要依赖于外围类的。
2、 它不能使用任何外围类的非static成员变量和方法。
public class OuterClass { private String sex; public static String name = "chenssy"; /** *静态内部类 */ static class InnerClass1{ /* 在静态内部类中可以存在静态成员 */ public static String _name1 = "chenssy_static"; public void display(){ /* * 静态内部类只能访问外围类的静态成员变量和方法 * 不能访问外围类的非静态成员变量和方法 */ System.out.println("OutClass name :" + name); } } /** * 非静态内部类 */ class InnerClass2{ /* 非静态内部类中不能存在静态成员 */ public String _name2 = "chenssy_inner"; /* 非静态内部类中可以调用外围类的任何成员,不管是静态的还是非静态的 */ public void display(){ System.out.println("OuterClass name:" + name); } } /** * @desc 外围类方法 * @author chenssy * @data 2013-10-25 * @return void */ public void display(){ /* 外围类访问静态内部类:内部类. */ System.out.println(InnerClass1._name1); /* 静态内部类 可以直接创建实例不需要依赖于外围类 */ new InnerClass1().display(); /* 非静态内部的创建需要依赖于外围类 */ OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2(); /* 方位非静态内部类的成员需要使用非静态内部类的实例 */ System.out.println(inner2._name2); inner2.display(); } public static void main(String[] args) { OuterClass outer = new OuterClass(); outer.display(); } } ---------------- Output: chenssy_static OutClass name :chenssy chenssy_inner OuterClass name:chenssy
上面这个例子充分展现了静态内部类和非静态内部类的区别。
到這裡內部類別的介紹基本上就結束了!對於內部類其實本人認識也只是皮毛,逼近菜鳥一枚,認知有限!我會利用這幾天時間好好研究內部類!
以上就是 java提高篇(七)-----詳解內部類的內容,更多相關內容請關注PHP中文網(www.php.cn)!