深入理解java巢狀類和內部類、匿名類
四、在外部類中定義內部類
匿名類就是沒有名字的內部類,是內部類的一種特殊情況。????????? 這句話對嗎???
前端時間在寫.net專案中,一直錯將.cs裡的兩個class當作內部類,原來是一個檔案裡的兩個類而已,這讓我想起了Java中的內部類,比較內部類,那麼還有兩個類,那就是匿名類和匿名內部類。今天我想就Java中的這三種類進行個比較。
我們知道在Java語言規範中可以做很多事,例如一個類或一個介面中可以宣告一個類或介面,在一個方法中可以宣告一個類,類與介面宣告可以巢狀任意深度等。
匿名類:
1、new <類或介面><類的主體>,匿名類的宣告是在編譯時進行的,例項化是在執行時進行的,所以在for迴圈中一個new語句會建立相同匿名類的幾個例項,而不是建立幾個不同匿名類的一個例項。
2、如果要執行的物件需要一個物件,但卻不值得建立全新的物件(可能是因為該物件只在一個方法內部使用),在這個時候使用匿名類就會非常合適,所以說,匿名類一般會在swing程式中快速建立事件處理程式。
Java程式碼- firstButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- getTxtValue().setText("第一個按鈕觸發的事件!");
- }
- });
3、從技術上說,匿名類可以被看作非靜態的內部類,所以他們具有方法內部宣告的非靜態內部類相同的許可權和限制。
內部類:
內部類顧名思義就是在一個類的內部還有一個類
Java程式碼- package com.iflytek.innerclass;
- /**
- * @author xudongwang 2012-1-11
- *
- * Email:[email protected]
- */
- public class InnerClassDemo {
- public static void main(String[] args) {
- new Outer().fun();
- }
- }
- class Outer {
- private String name = "Hello 內部類";
- class Inner {
- public void print() {
- System.out.println("name = " + name);
- }
- };
- public void fun() {
- new Inner().print();
- }
- }
內部類生成的.class檔名為:Outer$Inner.class,從上面的結構發現內部類的的缺點是“結構非常的混亂”。
Java程式碼- package com.iflytek.innerclass;
- /**
- * @author xudongwang 2012-1-11
- *
- * Email:[email protected]
- */
- public class InnerClassDemo02 {
- public static void main(String[] args) {
- new Outer02().fun();
- }
- }
- class Outer02 {
- private String name = "Hello 內部類";
- public void fun() {
- new Inner02(this).print();
- }
- public String getName() {
- return this.name;
- }
- };
- class Inner02 {
- private Outer02 out;
- public Inner02(Outer02 out) {
- this.out = out;
- }
- public void print() {
- System.out.println("name = " + this.out.getName());
- }
- };
從上可以看出內部類的優點是“可以方便的訪問外部類中的私有成員”;
如果要在外部直接使用內部類的例項化物件:
外部類.內部類 內部類物件 = 外部類例項.new 內部類例項();
Java程式碼- package com.iflytek.innerclass;
- /**
- * @author xudongwang 2012-1-11
- *
- * Email:[email protected]
- */
- public class InnerClassDemo03 {
- public static void main(String[] args) {
- Outer03 out = new Outer03();//外部類例項
- Outer03.Inner inner = out.new Inner();//例項化內部類物件
- inner.print();
- }
- }
- class Outer03{
- private String name = "Hello 內部類";
- class Inner {
- public void print() {
- System.out.println("name = " + name);
- }
- }
- }
一個內部類如果使用static關鍵字宣告的話,則此內部類就將成為外部類,可以直接通過外部類.內部類的形式訪問
Java程式碼- package com.iflytek.innerclass;
- /**
- * @author xudongwang 2012-1-11
- *
- * Email:[email protected]
- */
- public class InnerClassDemo04 {
- public static void main(String[] args) {
- Outer04.Inner inner = new Outer04.Inner();// 例項化內部類物件
- inner.print();
- }
- }
- class Outer04 {
- private static String name = "Hello 內部類";
- static class Inner {
- public void print() {
- System.out.println("name = " + name);
- }
- }
- }
內部類可以在任意的地方使用,例如方法中宣告
Java程式碼- package com.iflytek.innerclass;
- /**
- * @author xudongwang 2012-1-11
- *
- * Email:[email protected]
- */
- public class InnerClassDemo05 {
- public static void main(String[] args) {
- new Outer05().fun();
- }
- }
- class Outer05 {
- private static String name = "Hello 內部類";
- public void fun() {
- class Inner {
- public void print() {
- System.out.println("name = " + name);
- }
- }
- new Inner().print();
- }
- }
在方法中定義的內部類,可以直接訪問外部類中的各個成員,但是如果要訪問方法中的引數,則需要在引數上加上final關鍵字宣告;
Java程式碼- package com.iflytek.innerclass;
- /**
- * @author xudongwang 2012-1-11
- *
- * Email:[email protected]
- */
- public class InnerClassDemo06 {
- public static void main(String[] args) {
- new Outer06().fun(20);
- }
- }
- class Outer06 {
- private static String name = "Hello 內部類";
- public void fun(final int temp) {
- class Inner {
- public void print() {
- System.out.println("temp = " + temp);
- System.out.println("name = " + name);
- }
- }
- new Inner().print();
- }
- }
匿名類與內部的聯絡與區別:
按所在位置可以分為兩大類:
1、在類的方法中
特點:
a、可以訪問宿主類的所有元素 ;
b、儲存宿主類物件的引用,建立物件時必須有宿主類物件;
c、 不能有靜態資料;
繼續劃分:
A、本地內部類;
B、匿名內部類
兩者的區別在於本地內部類有構造方法,而匿名內部類只能例項初始化;
2、在類或介面作用域中;
繼續劃分:
A、普通內部類
B、靜態內部類
匿名內部類:
匿名內部類是在抽象類和介面的基礎之上發展起來的。
Java程式碼- package com.iflytek.innerclass;
- /**
- * @author xudongwang 2012-1-11
- *
- * Email:[email protected]
- */
- public class NoNameClass01 {
- public static void main(String[] args) {
- new X().fun2();
- }
- }
- interface A {
- public void fun();
- }
- class B implements A {
- public void fun() {
- System.out.println("Hello 準備匿名內部類");
- }
- }
- class X {
- public void fun1(A a) {
- a.fun();
- }
- public void fun2() {
- this.fun1(new B());
- }
- }
通過上面的Demo,如果現在假設B類只使用一次,那麼還有必要將其定義成一個單獨的類麼?
呵呵,此時就可以使用匿名內部類:
Java程式碼- package com.iflytek.innerclass;
- /**
- * @author xudongwang 2012-1-11
- *
- * Email:[email protected]
- */
- public class NoNameClass02 {
- public static void main(String[] args) {
- new XX().fun2();
- }
- }
- interface AA {
- public void fun();
- }
- class XX {
- public void fun1(AA a) {
- a.fun();
- }
- public void fun2() {
- this.fun1(new AA() {
- public void fun() {
- System.out.println("Hello 準備匿名內部類");
- }
- });
- }
- }
其實在真正的專案開發中匿名內部類使用的非常之少,一般在Java的圖形介面和現在的Android中使用的比較多點。
最後給一個內部類實現的簡單鏈表:
Java程式碼- package com.iflytek.innerclass;
- /**
- * @author xudongwang 2012-1-11
- *
- * Email:[email protected]
- */
- public class LinkDemo {
- public static void main(String args[]) {
- Link link = new Link();
- link.add("A");
- link.add("B");
- link.add("C");
- link.add("D");
- link.add("E");
- link.print();
- }
- };
- class Link {
- class Node {
- private String name;
- private Node next; // 單向連結串列,每個節點指向下一個節點
- public Node(String name) {
- this.name = name; // 通過構造方法為name屬性賦值
- }
- public void addNode(Node newNode) { // 增加節點
- if (this.next == null) {
- this.next = newNode; // 儲存節點
- } else {
- this.next.addNode(newNode); // 繼續向下查詢
- }
- }
- public void printNode() { // 輸出節點
- System.out.println(this.name);
- if (this.next != null) { // 此節點之後還存在其他的節點
- this.next.printNode();
- }
- }
- };
- private Node root; // 連結串列的頭
- public void add(String name) { // 增加節點
- Node newNode = new Node(name); // 定義一個新的節點
- /*
- * 如果是第一個節點,則肯定是根節點, 如果是第二個節點,則肯定放在根節點next中 如果是第三個節點,則肯定放在第二個節點的next中
- */
- if (this.root == null) {
- this.root = newNode; // 將第一個節點設定成根節點
- } else {
- // 肯定要放在最後一個節點之後
- // 通過節點.next來不斷的判斷
- this.root.addNode(newNode);
- }
- }
- public void print() {
- if (this.root != null) { // 如果根節點為空了,則沒有任何內容
- this.root.printNode();
- }
- }
- };