1、成員內部類: 即作爲外部類的一個成員存在,與外部類的屬性、方法並列。
注意:成員內部類中不能定義靜態變量,但可以訪問外部類的所有成員。
- public class Outer{
- private static int i = 1;
- private int j=10;
- private int k=20;
- public static void outer_f1(){
- //do more something
- System.out.println("outer's outer_f1");
- }
- public void out_f2(){
- //do more something
- }
- //成員內部類
- class Inner{
- //static int inner_i =100; //內部類中不允許定義靜態變量
- int j=100;//內部類中外部類的實例變量可以共存
- int inner_i=1;
- void inner_f1(){
- System.out.println(i);//外部類的變量如果和內部類的變量沒有同名的,則可以直接用變量名訪問外部類的變量
- System.out.println(j);//在內部類中訪問內部類自己的變量直接用變量名
- System.out.println(this.j);//也可以在內部類中用"this.變量名"來訪問內部類變量
- //訪問外部類中與內部類同名的實例變量可用"外部類名.this.變量名"。
- System.out.println(k);//外部類的變量如果和內部類的變量沒有同名的,則可以直接用變量名訪問外部類的變量
- outer_f1(); //可以直接訪問外部類的靜態方法
- // outer_f2(); //compile error,訪問外部類非靜態方法,必須使用外部類的實例,如下一句
- Outer.this.out_f2();
- }
- }
- //外部類的非靜態方法訪問成員內部類
- public void outer_f3(){
- Inner inner = new Inner();
- inner.inner_f1();
- }
- //外部類的靜態方法訪問成員內部類,與在外部類外部訪問成員內部類一樣
- public static void outer_f4(){
- //step1 建立外部類對象
- Outer out = new Outer();
- //***step2 根據外部類對象建立內部類對象***
- Inner inner=out.new Inner();
- //step3 訪問內部類的方法
- inner.inner_f1();
- }
- public static void main(String[] args){
- outer_f4();
- }
- }
⑴ 內部類作爲外部類的成員,可以訪問外部類的私有成員或屬性。(即使將外部類聲明爲PRIVATE,但是對於處於其內部的內部類還是可見的。)
⑵ 用內部類定義在外部類中不可訪問的屬性。這樣就在外部類中實現了比外部類的private還要小的訪問權限。
注意:內部類是一個編譯時的概念,一旦編譯成功,就會成爲完全不同的兩類。對於一個名爲outer的外部類和其內部定義的名爲inner的內部類。編譯完成後出現outer.class和outer$inner.class兩類。
2、局部內部類: 即在方法中定義的內部類,與局部變量類似,在局部內部類前不加修飾符public或private,其範圍爲定義它的代碼塊。
注意:局部內部類中不可定義靜態變量,可以訪問外部類的局部變量(即方法內的變量),但是變量必須是final的。
- public class Outer {
- private int s = 100;
- private int out_i = 1;
- public void f(final int k) {
- final int s = 200;
- int i = 1;
- final int j = 10;
- class Inner { // 定義在方法內部
- int s = 300;// 可以定義與外部類同名的變量
- // static int m = 20;//不可以定義靜態變量
- Inner(int k) {
- inner_f(k);
- }
- int inner_i = 100;
- void inner_f(int k) {
- System.out.println(out_i);// 如果內部類沒有與外部類同名的變量,在內部類中可以直接訪問外部類的實例變量
- System.out.println(k);// *****可以訪問外部類的局部變量(即方法內的變量),但是變量必須是final的*****
- // System.out.println(i); //compile error,i必須是final的
- System.out.println(s);// 如果內部類中有與外部類同名的變量,直接用變量名訪問的是內部類的變量
- System.out.println(this.s);// 用"this.變量名" 訪問的也是內部類變量
- System.out.println(Outer.this.s);// 用外部"外部類類名.this.變量名"
- // 訪問的是外部類變量
- }
- } //inner
- new Inner(k);
- }
- public static void main(String[] args) {
- // 訪問局部內部類必須先有外部類對象
- Outer out = new Outer();
- out.f(3);
- }
- }
注意:
執行結果:
1
3
300
300
100
在類外不可直接生成局部內部類(保證局部內部類對外是不可見的)。要想使用局部內部類時需要生成對象,對象調用方法,在方法中才能調用其局部內部類。通過內部類和接口達到一個強制的弱耦合,用局部內部類來實現接口,並在方法中返回接口類型,使局部內部類不可見,屏蔽實現類的可見性。
3、靜態內部類: 靜態內部類定義在類中,任何方法外,用static定義。
注意:靜態內部類中可以定義靜態或者非靜態的成員
- public class Outer {
- private static int i = 1;
- private int j = 10;
- public static void outer_f1() {
- }
- public void outer_f2() {
- }
- // 靜態內部類可以用public,protected,private修飾
- // 靜態內部類中可以定義靜態或者非靜態的成員
- static class Inner {
- static int inner_i = 100;
- int inner_j = 200;
- static void inner_f1() {
- System.out.println("Outer.i " + i);// 靜態內部類只能訪問外部類的靜態成員
- outer_f1();// 包括靜態變量和靜態方法
- }
- void inner_f2() {
- // System.out.println("Outer.i"+j);// error 靜態內部類不能訪問外部類的非靜態成員
- // outer_f2();//error 包括非靜態變量和非靜態方法
- }
- }
- public void outer_f3() {
- // 外部類訪問內部類的靜態成員:內部類.靜態成員
- System.out.println(Inner.inner_i); //100
- Inner.inner_f1(); //Outer.i 1
- // 外部類訪問內部類的非靜態成員:實例化內部類即可
- Inner inner = new Inner();
- inner.inner_f2();
- }
- public static void main(String[] args) {
- new Outer().outer_f3();
- }
- }
執行結果:
100
Outer.i 1
注意:*******生成(new)一個靜態內部類不需要外部類成員:這是靜態內部類和成員內部類的區別。靜態內部類的對象可以直接生成:
Outer.Inner in=new Outer.Inner();
而不需要通過生成外部類對象來生成。這樣實際上使靜態內部類成爲了一個頂級類。靜態內部類可用private,protected,public,abstract等來修飾*******
例子:
對於兩個類,擁有相同的方法:
- class People
- {
- run();
- }
- interface Machine{
- run();
- }
- class Robot extends People implement Machine
此時run()不可直接實現。
注意:當類與接口(或者是接口與接口)發生方法命名衝突的時候,此時必須使用內部類來實現。用接口不能完全地實現多繼承,用接口配合內部類才能實現真正的多繼承。
4、匿名內部類
匿名內部類是一種特殊的局部內部類,它是通過匿名類實現接口。
IA被定義爲接口。
IA I=new IA(){};
匿名內部類的特點:
1,一個類用於繼承其他類或是實現接口,並不需要增加額外的方法,只是對繼承方法的事先或是覆蓋。
2,只是爲了獲得一個對象實例,不需要知道其實際類型。
3,類名沒有意義,也就是不需要使用到。
- public class Outer {
- private static int i = 1;
- private int j = 10;
- public static void outer_f1(){
- }
- public void outer_f2(){
- }
- // 靜態內部類可以用public,protected,private修飾
- // 靜態內部類中可以定義靜態或者非靜態的成員
- static class Inner{
- static int inner_i = 100;
- int inner_j = 200;
- static void inner_f1(){
- System.out.println("Outer.i"+i);//靜態內部類只能訪問外部類的靜態成員
- outer_f1();//包括靜態變量和靜態方法
- }
- void inner_f2(){
- // System.out.println("Outer.i"+j);//靜態內部類不能訪問外部類的非靜態成員
- // outer_f2();//包括非靜態變量和非靜態方法
- }
- }
- public void outer_f3(){
- // 外部類訪問內部類的靜態成員:內部類.靜態成員
- System.out.println(Inner.inner_i);
- Inner.inner_f1();
- // 外部類訪問內部類的非靜態成員:實例化內部類即可
- Inner inner = new Inner();
- inner.inner_f2();
- }
- public static void main(String[] args) {
- new Outer().outer_f3();
- }
- }
內部類總結:
1.首先,把內部類作爲外部類的一個特殊的成員來看待,因此它有類成員的封閉等級:private ,protected,默認(friendly),public 它有類成員的修飾符: static,final,abstract
2.非靜態內部類nested inner class,內部類隱含有一個外部類的指針this,因此,它可以訪問外部類的一切資源(當然包括private)
外部類訪問內部類的成員,先要取得內部類的對象,並且取決於內部類成員的封裝等級。
非靜態內部類不能包含任何static成員.
3.靜態內部類:static inner class,不再包含外部類的this指針,並且在外部類裝載時初始化.
靜態內部類能包含static或非static成員.
靜態內部類只能訪問外部類static成員.
外部類訪問靜態內部類的成員,循一般類法規。對於static成員,用類名.成員即可訪問,對於非static成員,只能
用對象.成員進行訪問
4.對於方法中的內部類或塊中內部類只能訪問塊中或方法中的final變量。
類成員有兩種static , non-static,同樣內部類也有這兩種
non-static 內部類的實例,必須在外部類的方法中創建或通過外部類的實例來創建(OuterClassInstanceName.new innerClassName(ConstructorParameter)),並且可直接訪問外部類的信息,外部類對象可通過OuterClassName.this來引用
static 內部類的實例, 直接創建即可,沒有對外部類實例的引用。
內部類不管static還是non-static都有對外部類的引用
non-static 內部類不允許有static成員
方法中的內部類只允許訪問方法中的final局部變量和方法的final參數列表,所以說方法中的內部類和內部類沒什麼區別。但方法中的內部類不能在方法以外訪問,方法中不可以有static內部類
匿名內部類如果繼承自接口,必須實現指定接口的方法,且無參數
匿名內部類如果繼承自類,參數必須按父類的構造函數的參數傳遞
爲什麼需要內部類?
Java 內部類有什麼好處?爲什麼需要內部類?
首先舉一個簡單的例子,如果你想實現一個接口,但是這個接口中的一個方法和你構想的這個類中的一個方法的名稱,參數相同,你應該怎麼辦?這時候,你可以建一個內部類實現這個接口。由於內部類對外部類的所有內容都是可訪問的,所以這樣做可以完成所有你直接實現這個接口的功能。
不過你可能要質疑,更改一下方法的不就行了嗎?
的確,以此作爲設計內部類的理由,實在沒有說服力。
真正的原因是這樣的,java 中的內部類和接口加在一起,可以的解決常被 C++ 程序員抱怨 java 中存在的一個問題??沒有多繼承。實際上,C++ 的多繼承設計起來很複雜,而 java 通過內部類加上接口,可以很好的實現多繼承的效果。
內部類:一個內部類的定義是定義在另一個內部的類。
原因是:
1.一個內部類的對象能夠訪問創建它的對象的實現,包括私有數據。
2.對於同一個包中的其他類來說,內部類能夠隱藏起來。
3.匿名內部類可以很方便的定義回調。
4.使用內部類可以非常方便的編寫事件驅動程序。
1.內部類
提起 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 和其他類一樣同時被編譯,只不過它由它自己的作用域,超出了這個範圍就無效,除此之外它和其他內部類並沒有區別。
2.匿名類
匿名類是不能有名稱的類,所以沒辦法引用他們。必須在創建時,作爲new語句的一部分來聲明他們。
這就要採用另一種形式的new語句,如下所示:
new <類或接口> <類的主體>
這種形式的new語句聲明一個新的匿名類,他對一個給定的類進行擴展,或實現一個給定的接口。他還創建那個類的一個新實例,並把他作爲語句的結果而返回。要擴展的類和要實現的接口是new語句的操作數,後跟匿名類的主體。
假如匿名類對另一個類進行擴展,他的主體能夠訪問類的成員、覆蓋他的方法等等,這和其他任何標準的類都是相同的。假如匿名類實現了一個接口,他的主體必須實現接口的方法。
注意匿名類的聲明是在編譯時進行的,實例化在運行時進行。這意味着for循環中的一個new語句會創建相同匿名類的幾個實例,而不是創建幾個不同匿名類的一個實例。
從技術上說,匿名類可被視爲非靜態的內部類,所以他們具備和方法內部聲明的非靜態內部類相同的權限和限制。
假如要執行的任務需要一個對象,但卻不值得創建全新的對象(原因可能是所需的類過於簡單,或是由於他只在一個方法內部使用),匿名類就顯得很有用。匿名類尤其適合在Swing應用程式中快速創建事件處理程式。
java 代碼:
- interface pr {
- void print1();
- }
- public class noNameClass {
- public pr dest() {
- return new pr() {
- public void print1() {
- System.out.println("Hello world!!");
- }
- };
- }
- }
- public static void main(String args[]) {
- noNameClass c = new noNameClass();
- pr hw = c.dest();
- hw.print1();
- }
pr 也可以是一個類,但是你外部調用的方法必須在你的這個類或接口中聲明,外部不能調用匿名類內部的方法。
Java 中內部匿名類用的最多的地方也許就是在 Frame 中加入 Listner 了吧。
java 代碼:
- import java.awt.*;
- import java.awt.event.*;
- public class QFrame extends Frame {
- public QFrame() {
- this.setTitle(\"my application\");
- addWindowListener(new WindowAdapter() {
- public void windowClosing(WindowEvent e) {
- dispose();
- System.exit(0);
- }
- });
- this.setBounds(10,10,200,200);
- }
- }
內部匿名類,就是建立一個內部的類,但沒有給你命名,也就是沒有引用實例的變量。
- new WindowAdapter() {
- public void windowClosing(WindowEvent e) {
- dispose();
- System.exit(0);
- }
- }
new 是建立一個 WindowAdapter 對象,後面一個 {} 表示這個括號中的操作作用於這個默認的對名象,而上面的 Java 程序中後面是一個函數體。
這個用法的作用是:創建一個對象的實例,並且 override 它的一個函數。
打開 WindowAdapter 的代碼可以發現。它是一個抽象類。它是對 WindowListener 接口的一個實現。
Frame.addWindowListner(); 的參數是一個 WindowListner ,而實現上是傳一個從WindowAdapter 派生出的一個匿名類。
有一點需要注意的是,匿名內部類由於沒有名字,所以它沒有構造函數(但是如果這個匿名內部類繼承了一個只含有帶參數構造函數的父類,創建它的時候必須帶上這些參數,並在實現的過程中使用 super 關鍵字調用相應的內容)。如果你想要初始化它的成員變量,有下面幾種方法:
1. 如果是在一個方法的匿名內部類,可以利用這個方法傳進你想要的參數,不過記住,這些參數必須被聲明爲 final 。
2. 將匿名內部類改造成有名字的局部內部類,這樣它就可以擁有構造函數了。
3. 在這個匿名內部類中使用初始化代碼塊。