1. 程式人生 > >靜態巢狀類(Static Nested Class)和內部類(Inner Class)

靜態巢狀類(Static Nested Class)和內部類(Inner Class)

一. 什麼是巢狀類及內部類?
可以在一個類的內部定義另一個類, 這種類稱為巢狀類(nested classes),它有兩種型別:
靜態巢狀類和非靜態巢狀類.靜態巢狀類使用很少, 最重要的是非靜態巢狀類, 也即是被稱作為
內部類(inner).巢狀類從JDK1.1開始引入.其中inner類又可分為三種:
(1) 在一個類(外部類)中直接定義的內部類;
(2) 在一個方法(外部類的方法)中定義的內部類;
(3) 匿名內部類.
下面, 我將說明這幾種巢狀類的使用及注意事項.

二. 靜態巢狀類
如下所示程式碼為定義一個靜態巢狀類

package inner;

/**
* @author: Joho Woo
* @email:
[email protected]

* @version: 1.0
* @time: 2007-11-12 下午07:52:29
* @description:
*/
public class StaticTest
{
   private static String name = "woobo";
   private String num = "X001";

   static class Person
   {
     private String address = "China";
     public String mail = "[email protected]
";//內部類公有成員

     public void display()
     {
       //System.out.println(num);//不能直接訪問外部類的非靜態成員
       System.out.println(name);//只能直接訪問外部類的靜態成員
       System.out.println("Inner " + address);//訪問本內部類成員。
     }
   }

   public void printInfo()
   {
     Person person = new Person();
     person.display();

     //System.out.println(mail);//不可訪問
     //System.out.println(address);//不可訪問

     System.out.println(person.address);//可以訪問內部類的私有成員
     System.out.println(person.mail);//可以訪問內部類的公有成員

   }

   public static void main(String[] args)
   {
     StaticTest staticTest = new StaticTest();
     staticTest.printInfo();
   }
}


在靜態巢狀類內部, 不能訪問外部類的非靜態成員, 這是由Java語法中"靜態方法不能直接訪問非靜態成員"所限定.若想訪問外部類的變數, 必須通過其它方法解決, 由於這個原因, 靜態巢狀類使用很少.注意, 外部類訪問內部類的的成員有些特別, 不能直接訪問, 但可以通過內部類例項來訪問, 這是因為靜態巢狀內的所有成員和方法預設為靜態的了.同時注意, 內部靜態類Person只在類StaticTest 範圍內可見, 若在其它類中引用或初始化, 均是錯誤的.

三. 在外部類中定義內部類
如下所示程式碼為在外部類中定義兩個內部類及它們的呼叫關係:

class Outer
{
   int outer_x = 100;
   private class InnerOne
   {
     // 私有的內部類
     public int inner_y = 10;
     private int inner_z = 9;
     int inner_m = 5;

     public void display()
     {
       System.out.println("display outer_x:" + outer_x);
     }
     private void display2()
     {
       System.out.println("display outer_x:" + outer_x);
     }
   }

   public InnerOne getInnerOne()
   {
     // 即使是對外公開的方法,外部類也無法呼叫
     return new InnerOne();
   }
 
   class InnerTwo
   {
     InnerOne innerx = getInnerOne();// 可以訪問
     public void show()
     {
       // System.out.println(inner_y); // 不可訪問Innter的y成員
       // System.out.println(Inner.inner_y);   // 不可直接訪問Inner的任何成員和方法
       innerx.display();// 可以訪問
       innerx.display2();// 可以訪問
       System.out.println(innerx.inner_y);// 可以訪問
       System.out.println(innerx.inner_z);// 可以訪問
       System.out.println(innerx.inner_m);// 可以訪問
     }
   }
 
   void test()
   {
     InnerOne inner = new InnerOne();// 可以訪問
     inner.display();
     inner.display2();
     // System.out.println("Inner y:" + inner_y); // 不能訪問內部內變數
     System.out.println("Inner y:" + inner.inner_y);// 可以訪問
     System.out.println("Inner z:" + inner.inner_z);// 可以訪問
     System.out.println("Inner m:" + inner.inner_m);// 可以訪問
   
     InnerTwo innerTwo = new InnerTwo();
     innerTwo.show();
   }
}

public class Test
{
   public static void main(String args[])
   {
     Outer outer = new Outer();
     // Outer.Inner a=outer.getInner();
     // Inner類是私有的,外部類不能訪問, 如果Inner類是public ,則可以.
     outer.test();
   }
}



內部類Inner及InnterTwo只在類Outer的作用域內是可知的, 如果類Outer外的任何程式碼嘗試初始化類Inner或使用它, 編譯就不會通過.同時, 內部類的變數成員只在內部內內部可見, 若外部類或同層次的內部類需要訪問, 需採用示例程式
中的方法, 不可直接訪問內部類的變數.

四. 在方法中定義內部類
如下所示程式碼為在方法內部定義一個內部類:

package inner;

/**
* @author: Joho Woo
* @email: [email protected]
* @version: 1.0
* @time: 2007-11-12 下午08:25:33
* @description:
*/
public class FunOuter
{
   int out_x = 100;

   public void test()
   {
     class Inner
     {
       String inner_x = "x";
       void display()
       {
         System.out.println(out_x);
       }
     }
     Inner inner = new Inner();
     inner.display();
   }
   public void showStr(String str)
   {
     // public String str1 = "test Inner";
     // 不可定義, 只允許final修飾
     // static String str4 = "static Str";
     // 不可定義, 只允許final修飾
     String str2 = "test Inner";
     final String str3 = "final Str";
     class InnerTwo
     {
       public void testPrint()
       {
         System.out.println(out_x);
         // 可直接訪問外部類的變數
         // System.out.println(str); // 不可訪問本方法內部的非final變數
// System.out.println(str2); // 不可訪問本方法內部的非final變數
         System.out.println(str3); // 只可訪問本方法的final型變數成員
       }
     }
     InnerTwo innerTwo = new InnerTwo();
     innerTwo.testPrint();
   }
   public void use()
   {
     // Inner innerObj = new Inner();//此時Inner己不可見了
     // System.out.println(Inner.x);//此時Inner己不可見了
   }
   public static void main(String[] args)
   {
     FunOuter outer = new FunOuter();
     outer.test();
   }
}



從上面的例程我們可以看出定義在方法內部的內部類的可見性更小, 它只在方法內部
可見, 在外部類(及外部類的其它方法中)中都不可見了.同時, 它有一個特點, 就是方法內的內部類連本方法的成員變數都不可訪問, 它只能訪問本方法的final型成員.同時另一個需引起注意的是方法內部定義成員, 只允許final修飾或不加修飾符, 其它像static等均不可用.

五. 匿名內部類
如下所示程式碼為定義一個匿名內部類:匿名內部類通常用在Java的事件處理上

package inner;

/**
* @author: Joho Woo
* @email: [email protected]
* @version: 1.0
* @time: 2007-11-12 下午09:01:05
* @description:
*/
import java.applet.*;
import java.awt.event.*;

public class AnonymousInnerClassDemo extends Applet
{
   public void init()
   {
     addMouseListener(new MouseAdapter()
     {
       public void mousePressed(MouseEvent me)
       {
         showStatus("Mouse Pressed!");
       }
     });
   }
   public void showStatus(String str)
   {
     System.out.println(str);
   }
}


在上面的例子中, 方法addMouseListener接受一個物件型的引數表示式, 於是, 在引數裡, 我們定義了一個匿名內部類,這個類是一個MouseAdapter型別的類, 同時在這個類中定義了一個繼承的方法mousePressed, 整個類做為一個引數.這個類沒有名稱, 但是當執行這個表示式時它被自動例項化.同時因為, 這個匿名內部類是定義在AnonymousInnerClassDemo
類內部的, 所以它可以訪問它的方法showStatus.這同前面的內部類是一致的.

六. 內部類使用的其它的問題
通過以上, 我們可以清楚地看出內部類的一些使用方法, 同時, 在許多時候, 內部類是在如Java的事件處理. 或做為值物件來使用的.同時, 我們需注意最後一個問題, 那就是, 內部類同其它類一樣被定義, 同樣它也可以繼承外部其它包的類和實現外部其它地方的介面.同樣它也可以繼承同一層次的其它的內部類,甚至可以繼承外部類本身.下面我們給出最後一個例子做為結束:

public class Layer
{
   // Layer類的成員變數
   private String testStr = "testStr";

   // Person類, 基類
   class Person
   {
     String name;
     Email email;

     public void setName(String nameStr)
     {
       this.name = nameStr;
     }
     public String getName()
     {
       return this.name;
     }
public void setEmail(Email emailObj)
     {
       this.email = emailObj;
     }
     public String getEmail()
     {
       return this.email.getMailStr();
     }

     // 內部類的內部類, 多層內部類
     class Email
     {
       String mailID;
       String mailNetAddress;

       Email(String mailId, String mailNetAddress)
       {
         this.mailID = mailId;
         this.mailNetAddress = mailNetAddress;
       }
       String getMailStr()
       {
         return this.mailID + "@" + this.mailNetAddress;
       }
     }
   }

   // 另一個內部類繼承外部類本身
   class ChildLayer extends Layer
   {
     void print()
     {
       System.out.println(super.testStr);// 訪問父類的成員變數
     }
   }

   // 另個內部類繼承內部類Person
   class OfficePerson extends Person
   {
     void show()
     {
       System.out.println(name);
       System.out.println(getEmail());
     }
   }

   // 外部類的測試方法
   public void testFunction()
   {
     // 測試第一個內部類
     ChildLayer childLayer = new ChildLayer();
     childLayer.print(); // 測試第二個內部類
     OfficePerson officePerson = new OfficePerson();
     officePerson.setName("abner chai");
     // 注意此處, 必須用物件.new 出來物件的子類物件
     // 而不是Person.new Email(...)
     // 也不是new Person.Email(...)
     officePerson
         .setEmail(officePerson.new Email("josserchai", "yahoo.com"));
     officePerson.show();
   }
   public static void main(String[] args)
   {
     Layer layer = new Layer();
     layer.testFunction();
   }
}