前言
學習Java已經有一段日子了,最近的日子筆者在重新對java進行再學習。不過這個階段的學習
筆者不會再著眼於具體的文法哪些細枝末節的東西了,這個階段的學習中筆者將會對以前學習的
模糊的,遺漏的知識概念做一些相關性的總結。今天,恰好看到內部類這塊了,記得以前對內部類
的使用就有一些模糊,因此專門就內部類做一些總結。 內部類概念
所謂內部類就是指在一個外部類中再定義一個類(內部類),這樣內部類就作為一個成員依附於
外部類而存在。不過在使用內部類的時候需要注意的是內部類可以static,protect,private,但是
外部類只能使用public和預設的包存取權限.
若非這兩種編譯出錯:
Illegal modifier for the class Outer; only public, abstract & final are permitted
package com.kiritor;public class Outer {private String type;public String getType() {return type;}public void setType(String type) {this.type = type;}class Inner {private String type;public String getType() {return type;}public void setType(String type) {this.type = type;}}}
內部類的意義
簡單看來內部類好像就是一種程式碼後置機制:將類至於其他類的內部.不過內部類遠不止這樣
它瞭解外部類,能夠和外部類進行必要的通訊。
1、封裝一些別人不想知道的操作.
2、內部類可以訪問建立它的外部類對象的內容,甚至包括了private的變數.
內部類同樣可以實現介面,繼承基類,這使得java中的多繼承變得完整.。我們可以使用內部類
的方式類比出多繼承的效果。
通過內部類繼承基類,外部類建立內部類對象,並使用內部類提供的方法,這樣就變相的實現了
多繼承的效果.
public class Graduate{ private Graduate_stu gaduate_stu= new Graduate_stu(); private Graduate_emp graduate_emp = new Graduate_emp(); private class Graduate_stu extends Student{ public void getName() { .... } } private class Graduate_emp extends Employee{ public double getMoney() { return 0.0; } } public void getName() { gaduate_stu.getName(); } public double getMoney() { graduate_emp .getMoney(); } } 只是用代碼簡單的類比了一下!
內部類還有一個頗具吸引力的特點,那就是內部類和外部類對於介面的繼承是"分離"的
相互不存在影響的,基於現實的情況,有時候我們實現一個介面,但是介面的方法在此類
中已經有定義了,對於這種情況,我們就可以使用內部類實現該介面,實現其方法,因為內
部類對於外部類的成員是可訪問的,因此使用內部類的方法就解決可該問題。 內部類的分類
我們現在知道內部類是放在外部類中的,根據內部類不同的"位置"和特性,內部類可以
分為以下幾類:
● 成員內部類
● 局部內部類
● 靜態內部類(嵌套類)
● 匿名內部類
對於其具體的特性和用法下面介紹 成員內部類
內部類作為外部類的一個成員存在,與外部類的方法,屬性並列.
package com.kiritor;public class Outer {private String type="Outer Class";private static int flag = 1;public String getType() {return type;}public void setType(String type) {this.type = type;}@Overridepublic String toString() {System.out.println(""+getType()+":"+this.flag);Inner inner = new Inner();inner.toString();//外部類的非靜態方法訪問內部類的方法//外部類的靜態方法訪問與其是一樣的,不做示範了.return super.toString();}class Inner {private String type="Inner Class"; private int flag=2;//這裡成員內部類中不允許定義靜態成員public String getType() {return type;}public void setType(String type) {this.type = type;}public String toString() {System.out.println(""+getType()+":"+this.flag);System.out.println(""+Outer.this.getType()+Outer.this.flag);//若有變數重名,通過此種方式訪問return super.toString();}}public static void main(String[] args) {Outer outer = new Outer();outer.toString();Outer.Inner inner = outer.new Inner();//通過此種方式new innerinner.toString();}}
Tips:在建立一個內部類的時候除非你已經有了一個外部類對象,否則不可能產生
方法內部類對象,因為內部類對象會悄悄的連結到建立他的外部類的對象,沒有外部類對象
自然也就不可能產生內部類對象了,不過還需注意的是內部類是一個在編譯時間的概念,一旦編譯
通過,就會成為完全不同的兩個類,也就是會出現Outer,class和Outer$Inner,class兩個位元組碼
檔案。
局部內部類
在方法中定義的內部類稱為局部內部類.它與局部變數類似,因此局部內部類是不能有訪問
修飾符的,因為它不是外部類成員,但是他可以訪問當前方法中的代碼塊的常量,和外部類的所有
成員.
package com.kiritor;public class Outer {private String type = "Outer Class";private static int flag = 1;public String getType() {return type;}public void setType(String type) {this.type = type;}@Overridepublic String toString() {System.out.println("" + getType() + ":" + this.flag);return super.toString();}public void innerInfo() {final String innerFinal = "可以存取方法體內的常量";class Inner {private String type = "Inner Class";private int flag = 2;// 這裡成員內部類中不允許定義靜態成員public String getType() {return type;}public void setType(String type) {this.type = type;}public String toString() {System.out.println("" + getType() + ":" + this.flag+ innerFinal);System.out.println("" + Outer.this.getType() + Outer.this.flag);// 若有變數重名,通過此種方式訪問return super.toString();}}new Inner().toString();//注意是通過這種方式調用內部類的方法的!}public static void main(String[] args) {Outer outer = new Outer();outer.toString();outer.innerInfo();}}
靜態內部類(嵌套類)
前面兩種內部類和變數類似,這裡的變數就是成員變數,和局部變數,可以參照進行對比
如果你不需要內部類對象與其外部類對象之間有聯絡,那你可以將內部類聲明為static的,這就
是靜態內部類.我們需要明白的是:普通的內部類對象隱含的儲存了一個外部類對象的引用。
但是當內部類為static的時候這種“特性”也就沒有了,這意味著:
1、建立靜態內部類對象的時候並不需要外部類對象
2、不能通過靜態內部類對象訪問非靜態外部類對象了。
看例子:
package com.kiritor;public class Outer { private String type = "Outer Class"; private static int flag = 1; public String getType() { return type; } public void setType(String type) { this.type = type; } @Override public String toString() { System.out.println("" + getType() + ":" + this.flag); Inner.info();//外部類訪問內部類的靜態成員:內部類.靜態成員(靜態方法) Inner inner = new Inner();//這裡產生一個內部類對象不再需要通過外部類對象了 inner.toString();//外部類訪問靜態內部類的非靜態成員或方法必須new一個對象 System.out.println(inner.type); return super.toString(); } static class Inner { private String type = "Inner Class"; private int flag = 2; private static String info="Inner Class 2"; //靜態內部類中可以有非靜態方法、屬性 public String getType() { return type; } public void setType(String type) { this.type = type; } public static void info() { System.out.println(info); } public String toString() { System.out.println("" + getType() + ":" + this.flag ); //System.out.println("" + Outer.getType() + Outer.this.flag);// 若有變數重名,通過此種方式訪問 return super.toString(); } } public static void main(String[] args) { Outer outer = new Outer(); outer.toString(); }} 可以看出的是:產生一個內部類對象不再需要通過一個外部類對象了,這也是靜態內部類和
成員內部類的區別:Outer.Inner in = new Outer.Inner();
匿名內部類
簡單的說匿名內部類就是沒有名字的類了,這在GUI編程裡面是較為常見的,給個例子:
package com.kiritor;import java.awt.event.MouseEvent;import java.awt.event.MouseListener;import javax.swing.JButton;import javax.swing.JFrame;public class MyFrame extends JFrame{private JButton button = null;public MyFrame() {this.setSize(200, 200);this.setVisible(true);button = new JButton("匿名內部類");button.addMouseListener(new MouseListener() {//一個匿名的類@Overridepublic void mouseReleased(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mousePressed(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseExited(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseEntered(MouseEvent e) {// TODO Auto-generated method stub}@Overridepublic void mouseClicked(MouseEvent e) {// TODO Auto-generated method stub}});this.add(button);} } 對於匿名內部類,筆者現就不做總結了,之後會找個時間理解一下.
內部類的相關問題
下面討論的是內部類中的一些有趣的問題!
內部類能否被"重載"、“繼承”? 內部類重載問題
假設如果你建立了一個外部類,並定義了一個內部類,之後繼承外部類並重新定義內部類
的時候會發生什麼呢?
package com.kiritor;class Outer {public Outer() {System.out.print("Outer:");new Inner();}class Inner {public Inner() {System.out.println("Inner");}}}public class Outer2 extends Outer {class Inner {public Inner() {System.out.println("outer2:Inner");}}public static void main(String[] args) {new Outer2();}} 看一看輸出情況:Outer:Inner
預設的構造器Outer2()是編譯器自動產生的,他會先調用父類的構造器,通過結果可以看出
雖然建立的是子類對象,但是 內部類並不是使用的"重載"過的.這說明檔你繼承了某個外部類
的時候,內部類並未發生特別變化,當然明確的繼承某個內部類的方式除外!
package com.kiritor;class Outer {public Outer() {System.out.print("Outer:");new Inner();}class Inner {public Inner() {System.out.println("Inner");}}}public class Outer2 extends Outer {class Inner extends com.kiritor.Outer.Inner{public Inner() {System.out.println("outer2:Inner");}}public Outer2() {new Inner();}public static void main(String[] args) {new Outer2();}} 明確繼承之後的輸出結果為:
內部類的繼承問題
有時候我們只是需要繼承內部類,但是內部類的構造器又必須用到外部對象的引用
, 因此在繼承一個內部類的時候就有點特別了,主要的問題在於外部類對象的引用必須
初始化,而在被繼承類中並不存在,也就是單一繼承內部類的時候,沒有內部類與其外部類
的一種關聯.
可以使用一下方式解決:
package com.kiritor;import com.kiritor.Outer.Inner;class Outer {public Outer() {System.out.print("Outer:");new Inner();}class Inner {public Inner() {System.out.println("Inner");}}}public class Inner2 extends Outer.Inner { Inner2(Outer outer) { outer.super(); //構造器只能是這種方式的 System.out.println("只能為此種構造器"); } public static void main(String[] args) {new Inner2(new Outer());}} 輸出結果為:
Outer:Inner
Inner
只能為此種構造器
可以看出的是,Inner2隻是整合了內部類,但是其預設的構造器並不能用,而且僅僅傳遞一個
外部類的引用還不夠,還必須首先調用外部類的構造方法.這樣才提供了內部類與外部類對象
的引用關聯,才能夠通過編譯的.