亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Java > 正文

深入理解Java:內部類

2019-11-06 06:24:25
字體:
來源:轉載
供稿:網友

學習java的同學注意了?。。?nbsp;學習過程中遇到什么問題或者想獲取學習資源的話,歡迎加入Java學習交流群,群號碼:523047986  我們一起學Java!

什么是內部類?

內部類是指在一個外部類的內部再定義一個類。內部類作為外部類的一個成員,并且依附于外部類而存在的。內部類可為靜態,可用PRotected和private修飾(而外部類只能使用public和缺省的包訪問權限)。內部類主要有以下幾類:成員內部類、局部內部類、靜態內部類、匿名內部類

內部類的共性

(1)、內部類仍然是一個獨立的類,在編譯之后內部類會被編譯成獨立的.class文件,但是前面冠以外部類的類名和$符號 。

(2)、內部類不能用普通的方式訪問。

(3)、內部類聲明成靜態的,就不能隨便的訪問外部類的成員變量了,此時內部類只能訪問外部類的靜態成員變量 。

(4)、外部類不能直接訪問內部類的的成員,但可以通過內部類對象來訪問

 

內部類是外部類的一個成員,因此內部類可以自由地訪問外部類的成員變量,無論是否是private的 。

因為當某個外圍類的對象創建內部類的對象時,此內部類會捕獲一個隱式引用,它引用了實例化該內部對象的外圍類對象。通過這個指針,可以訪問外圍類對象的全部狀態。

通過反編譯內部類的字節碼, 分析之后主要是通過以下幾步做到的: 1 編譯器自動為內部類添加一個成員變量, 這個成員變量的類型和外部類的類型相同, 這個成員變量就是指向外部類對象的引用; 2 編譯器自動為內部類的構造方法添加一個參數, 參數的類型是外部類的類型, 在構造方法內部使用這個參數為1中添加的成員變量賦值; 3 在調用內部類的構造函數初始化內部類對象時, 會默認傳入外部類的引用。

為什么需要內部類?

其主要原因有以下幾點:

內部類方法可以訪問該類定義所在的作用域的數據,包括私有的數據

內部類可以對同一個包中的其他類隱藏起來,一般的非內部類,是不允許有 private 與protected權限的,但內部類可以

可是實現多重繼承

當想要定義一個回調函數且不想編寫大量代碼時,使用匿名內部類比較便捷

使用內部類最吸引人的原因是:

每個內部類都能獨立地繼承自一個(接口的)實現,所以無論外圍類是否已經繼承了某個(接口的)實現,對于內部類都沒有影響。大家都知道Java只能繼承一個類,它的多重繼承在我們沒有學習內部類之前是用接口來實現的。但使用接口有時候有很多不方便的地方。比如我們實現一個接口就必須實現它里面的所有方法。而有了內部類就不一樣了。它可以使我們的類繼承多個具體類或抽象類。

大家看下面的例子:

復制代碼
public class Example1 {    public String name(){       return "liutao";   }}public class Example2 {    public int age(){        return 25;    }}public class MainExample {   private class test1 extends Example1{        public String name(){          return super.name();        }    }    private class test2 extends Example2 {       public int age(){         return super.age();       }    }   public String name(){    return new test1().name();    }   public int age(){       return new test2().age();   }   public static void main(String args[]){       MainExample mi=new MainExample();       System.out.println("姓名:"+mi.name());       System.out.println("年齡:"+mi.age());   }}復制代碼

成員內部類:

即在一個類中直接定義的內部類, 成員內部類與普通的成員沒什么區別,可以與普通成員一樣進行修飾和限制。成員內部類不能含有static的變量和方法。

public class Outer {    private static int i = 1;    private int j = 10;    private int k = 20;    public static void outer_f1() {}    public void outer_f2() {}    // 成員內部類中,不能定義靜態成員    // 成員內部類中,可以訪問外部類的所有成員    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);            // 在內部類中訪問內部類自己的變量也可以用this.變量名            System.out.println(this.j);            // 在內部類中訪問外部類中與內部類同名的實例變量用外部類名.this.變量名            System.out.println(Outer.this.j);            // 如果內部類中沒有與外部類同名的變量,則可以直接用變量名訪問外部類變量            System.out.println(k);            outer_f1();            outer_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();//該語句的輸出結果和下面三條語句的輸出結果一樣        // 如果要直接創建內部類的對象,不能想當然地認為只需加上外圍類Outer的名字,        // 就可以按照通常的樣子生成內部類的對象,而是必須使用此外圍類的一個對象來        // 創建其內部類的一個對象:        // Outer.Inner outin = out.new Inner()        // 因此,除非你已經有了外圍類的一個對象,否則不可能生成內部類的對象。因為此        // 內部類的對象會悄悄地鏈接到創建它的外圍類的對象。如果你用的是靜態的內部類,        // 那就不需要對其外圍類對象的引用。        Outer out = new Outer();        Outer.Inner outin = out.new Inner();        outin.inner_f1();    }}

局部內部類:

在方法中定義的內部類稱為局部內部類。與局部變量類似,局部內部類不能有訪問說明符,因為它不是外圍類的一部分,但是它可以訪問當前代碼塊內的常量,和此外圍類所有的成員。

需要注意的是:

(1)、方法內部類只能在定義該內部類的方法內實例化,不可以在此方法外對其實例化。

(2)、方法內部類對象不能使用該內部類所在方法的非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);                // 可以訪問外部類的局部變量(即方法內的變量),但是變量必須是final的                System.out.println(j);                // System.out.println(i);                // 如果內部類中有與外部類同名的變量,直接用變量名訪問的是內部類的變量                System.out.println(s);                // 用this.變量名訪問的也是內部類變量                System.out.println(this.s);                // 用外部類名.this.內部類變量名訪問的是外部類變量                System.out.println(Outer.this.s);            }        }        new Inner(k);    }    public static void main(String[] args) {        // 訪問局部內部類必須先有外部類對象        Outer out = new Outer();        out.f(3);    }}

 

靜態內部類(嵌套類):

如果你不需要內部類對象與其外圍類對象之間有聯系,那你可以將內部類聲明為static。這通常稱為嵌套類(nested class)。想要理解static應用于內部類時的含義,你就必須記住,普通的內部類對象隱含地保存了一個引用,指向創建它的外圍類對象。然而,當內部類是static的時,就不是這樣了。嵌套類意味著:

1. 要創建嵌套類的對象,并不需要其外圍類的對象。

2. 不能從嵌套類的對象中訪問非靜態的外圍類對象。

public class Outer {    private static int i = 1;    private int j = 10;    public static void outer_f1() {}    public void outer_f2() {}    // 靜態內部類可以用public,protected,private修飾    // 靜態內部類中可以定義靜態或者非靜態的成員    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();    }}

生成一個靜態內部類不需要外部類成員:這是靜態內部類和成員內部類的區別。靜態內部類的對象可以直接生成:Outer.Inner in = new Outer.Inner();而不需要通過生成外部類對象來生成。這樣實際上使靜態內部類成為了一個頂級類(正常情況下,你不能在接口內部放置任何代碼,但嵌套類可以作為接口的一部分,因為它是static 的。只是將嵌套類置于接口的命名空間內,這并不違反接口的規則)

 

匿名內部類:

簡單地說:匿名內部類就是沒有名字的內部類。什么情況下需要使用匿名內部類?如果滿足下面的一些條件,使用匿名內部類是比較合適的:只用到類的一個實例。類在定義后馬上用到。類非常?。⊿UN推薦是在4行代碼以下)給類命名并不會導致你的代碼更容易被理解。在使用匿名內部類時,要記住以下幾個原則:

  匿名內部類不能有構造方法。

  匿名內部類不能定義任何靜態成員、方法和類。

  匿名內部類不能是public,protected,private,static。

  只能創建匿名內部類的一個實例。

     一個匿名內部類一定是在new的后面,用其隱含實現一個接口或實現一個類。

  因匿名內部類為局部內部類,所以局部內部類的所有限制都對其生效。

 

下面的代碼展示的是,如果你的基類需要一個有參數的構造器,應該怎么辦:復制代碼
public class Parcel7 {    public Wrapping wrap(int x) {        // Base constructor call:        return new Wrapping(x) { // Pass constructor argument.            public int value() {                return super.value() * 47;            }        }; // Semicolon required    }    public static void main(String[] args) {        Parcel7 p = new Parcel7();        Wrapping w = p.wrap(10);    }}復制代碼只需簡單地傳遞合適的參數給基類的構造器即可,這里是將x 傳進new Wrapping(x)。在匿名內部類末尾的分號,并不是用來標記此內部類結束(C++中是那樣)。實際上,它標記的是表達式的結束,只不過這個表達式正巧包含了內部類罷了。因此,這與別的地方使用的分號是一致的。 如果在匿名類中定義成員變量或者使用帶參數的構造函數,你同樣能夠對其執行初始化操作:復制代碼
public class Parcel8 {    // Argument must be final to use inside    // anonymous inner class:    public Destination dest(final String name, String city) {        return new Destination(name, city) {            private String label = name;            public String getName() {                return label;            }        };    }    public static void main(String[] args) {        Parcel8 p = new Parcel8();        Destination d = p.dest("Tanzania", "gz");    }    abstract class Destination {        Destination(String name, String city) {            System.out.println(city);        }        abstract String getName();    }}復制代碼注意這里的形參city,由于它沒有被匿名內部類直接使用,而是被抽象類Inner的構造函數所使用,所以不必定義為final。  

內部類的重載問題

 如果你創建了一個內部類,然后繼承其外圍類并重新定義此內部類時,會發生什么呢?也就是說,內部類可以被重載嗎?這看起來似乎是個很有用的點子,但是“重載”內部類就好像它是外圍類的一個方法,其實并不起什么作用: 復制代碼
class Egg {       private Yolk y;        protected class Yolk {              public Yolk() {                     System.out.println("Egg.Yolk()");              }       }        public Egg() {              System.out.println("New Egg()");              y = new Yolk();       }} public class BigEgg extends Egg {       public class Yolk {              public Yolk() {                     System.out.println("BigEgg.Yolk()");              }       }        public static void main(String[] args) {              new BigEgg();       }}復制代碼輸出結果為:New Egg()Egg.Yolk() 缺省的構造器是編譯器自動生成的,這里是調用基類的缺省構造器。你可能認為既然創建了BigEgg 的對象,那么所使用的應該是被“重載”過的Yolk,但你可以從輸出中看到實際情況并不是這樣的。這個例子說明,當你繼承了某個外圍類的時候,內部類并沒有發生什么特別神奇的變化。這兩個內部類是完全獨立的兩個實體,各自在自己的命名空間內。當然,明確地繼承某個內部類也是可以的:復制代碼
class Egg2 {       protected class Yolk {              public Yolk() {                     System.out.println("Egg2.Yolk()");              }               public void f() {                     System.out.println("Egg2.Yolk.f()");              }       }        private Yolk y = new Yolk();        public Egg2() {              System.out.println("New Egg2()");       }        public void insertYolk(Yolk yy) {              y = yy;       }        public void g() {              y.f();       }} public class BigEgg2 extends Egg2 {       public class Yolk extends Egg2.Yolk {              public Yolk() {                     System.out.println("BigEgg2.Yolk()");              }               public void f() {                     System.out.println("BigEgg2.Yolk.f()");              }       }        public BigEgg2() {              insertYolk(new Yolk());       }        public static void main(String[] args) {              Egg2 e2 = new BigEgg2();              e2.g();       }}復制代碼輸出結果為:Egg2.Yolk()New Egg2()Egg2.Yolk()BigEgg2.Yolk()BigEgg2.Yolk.f() 現在BigEgg2.Yolk 通過extends Egg2.Yolk 明確地繼承了此內部類,并且重載了其中的方法。Egg2 的insertYolk()方法使得BigEgg2 將它自己的Yolk 對象向上轉型,然后傳遞給引用y。所以當g()調用y.f()時,重載后的新版的f()被執行。第二次調用Egg2.Yolk()是BigEgg2.Yolk 的構造器調用了其基類的構造器??梢钥吹皆谡{用g()的時候,新版的f()被調用了。 

內部類的繼承問題

因為內部類的構造器要用到其外圍類對象的引用,所以在你繼承一個內部類的時候,事情變得有點復雜。問題在于,那個“秘密的”外圍類對象的引用必須被初始化,而在被繼承的類中并不存在要聯接的缺省對象。要解決這個問題,需使用專門的語法來明確說清它們之間的關聯:復制代碼
class WithInner {        class Inner {                Inner(){                        System.out.println("this is a constructor in WithInner.Inner");                };        }} public class InheritInner extends WithInner.Inner {        // ! InheritInner() {} // Won't compile        InheritInner(WithInner wi) {                wi.super();                System.out.println("this is a constructor in InheritInner");        }         public static void main(String[] args) {                WithInner wi = new WithInner();                InheritInner ii = new InheritInner(wi);        }}復制代碼輸出結果為:this is a constructor in WithInner.Innerthis is a constructor in InheritInner 可以看到,InheritInner 只繼承自內部類,而不是外圍類。但是當要生成一個構造器時,缺省的構造器并不算好,而且你不能只是傳遞一個指向外圍類對象的引用。此外,你必須在構造器內使用如下語法:enclosingClassReference.super();這樣才提供了必要的引用,然后程序才能編譯通過。 

為什么非靜態內部類中不能有static修飾的屬性,但卻可以有常量?

如:

public class InnerClassDemo{    int x;    class A{        static  int a = 0;//這樣寫是不合法的.        static final int b=0;//這樣寫是合法的     }}

定義一個靜態的域或者方法,要求在靜態環境或者頂層環境,即如果加上 static class A變成靜態內部類就ok非靜態內部類 依賴于一個外部類對象,而靜態域/方法是不依賴與對象——僅與類相關(細說了,就是加載靜態域時,根本沒有外部類對象)因此,非靜態內部類中不能定義靜態域/方法,編譯過不了。

而常量之所以可以(不論有無static),因為java在編譯期就確定所有常量,放到所謂的常量池當中。常量的機制和普通變量不一樣。

學習Java的同學注意了!?。?nbsp;學習過程中遇到什么問題或者想獲取學習資源的話,歡迎加入Java學習交流群,群號碼:523047986  我們一起學Java!


上一篇:Java Output Format

下一篇:排序算法(Java)

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
97超级碰碰碰久久久| 91亚洲精华国产精华| 91中文在线观看| 色综合色综合久久综合频道88| 国内精品小视频在线观看| 欧美激情一级精品国产| 国产精品久久色| 热re99久久精品国产66热| 日本乱人伦a精品| 亚洲成avwww人| 日韩视频免费大全中文字幕| 欧美剧在线观看| 日韩中文字幕免费视频| 欧美丝袜一区二区三区| 亚洲黄在线观看| 热久久视久久精品18亚洲精品| 亚洲视频免费一区| 日本精品中文字幕| 亚洲人成电影网站| 久久久国产一区二区三区| 欧美巨猛xxxx猛交黑人97人| 久久成人一区二区| 欧美在线观看网站| 国产精品专区一| 日本免费久久高清视频| 欧美激情一二三| 欧美成人精品在线观看| 日韩av片永久免费网站| 国产欧美日韩亚洲精品| 午夜精品一区二区三区在线播放| 欧美情侣性视频| 亚洲999一在线观看www| 亚洲自拍高清视频网站| 亚洲国产欧美一区| 国产99久久精品一区二区| 欧美一级视频一区二区| 亚洲va欧美va国产综合久久| 国产精品高清在线| 亚洲一区二区黄| 日韩在线免费高清视频| 久久久久久欧美| 国产精品r级在线| 亚洲一区二区三区毛片| 久久中国妇女中文字幕| 色噜噜狠狠狠综合曰曰曰| 欧美理论在线观看| 国产精品丝袜白浆摸在线| 91精品国产91久久久久久吃药| 日韩有码视频在线| 538国产精品一区二区在线| 欧美重口另类videos人妖| 国产精品永久在线| 91极品视频在线| 97国产精品人人爽人人做| 日韩激情视频在线播放| 亚洲国产成人久久综合一区| 亚洲欧美日韩精品久久亚洲区| 国产成人精品在线| 午夜精品久久17c| 亚洲在线www| 亚洲电影在线观看| 欧美日韩亚洲精品内裤| 91精品啪aⅴ在线观看国产| 欧美高清视频免费观看| 久久精品国产精品| 久久精品最新地址| 91亚洲精品一区| 久久人人爽人人爽爽久久| 国产精品久久999| 欧美片一区二区三区| 亚洲自拍偷拍网址| 精品在线观看国产| 精品久久久久久久久久| 久久成人18免费网站| 亚洲精品视频在线观看视频| 亚洲欧美日韩一区二区三区在线| 日韩欧美国产中文字幕| 北条麻妃一区二区三区中文字幕| 国产成人亚洲综合| 国产精品久久久久久久久男| 国产成人精品综合久久久| 日韩av片免费在线观看| 欧美性xxxxxxxxx| 亚洲国产女人aaa毛片在线| 日韩视频免费在线| 97国产精品人人爽人人做| 精品丝袜一区二区三区| 欧美一级高清免费播放| 中文字幕视频一区二区在线有码| 亚洲女人天堂成人av在线| 久久久久国色av免费观看性色| 亚洲视频电影图片偷拍一区| 欧美高清激情视频| 日韩在线观看av| 国产精品偷伦视频免费观看国产| 欧美国产日韩一区二区三区| 97在线视频免费播放| 26uuu国产精品视频| 一本色道久久88精品综合| 国产成人小视频在线观看| 成人精品一区二区三区电影黑人| 欧美激情国产高清| 日产精品久久久一区二区福利| 久久九九全国免费精品观看| 中文字幕在线亚洲| 5252色成人免费视频| 亚洲香蕉成视频在线观看| 91精品国产自产91精品| 97精品一区二区三区| 九九精品在线视频| 亚洲成色www8888| 国产精品久久久久久婷婷天堂| 日韩欧美成人精品| 亚洲国产精品va在线| 欧美日韩国产黄| 热门国产精品亚洲第一区在线| 精品久久久久久久大神国产| 日韩在线中文视频| 91国自产精品中文字幕亚洲| 国产性猛交xxxx免费看久久| 欧美日韩日本国产| 久久欧美在线电影| 亚洲精品小视频在线观看| 成人国产精品日本在线| 久久免费国产视频| 97碰在线观看| 亚洲毛茸茸少妇高潮呻吟| 欧美老少配视频| 欧美日韩在线一区| 亚洲国产精品电影在线观看| 亚洲第一区第一页| 日韩av资源在线播放| 久久久久久com| 国产一区二区三区在线播放免费观看| 国产精品久久久久久久久久小说| 欧美亚洲伦理www| 久久夜色精品国产欧美乱| 国产区亚洲区欧美区| 精品国产91乱高清在线观看| 欧美成人免费全部观看天天性色| 亚洲自拍偷拍在线| 亚洲一区二区三区乱码aⅴ蜜桃女| 精品成人69xx.xyz| 精品视频久久久久久久| 国产有码在线一区二区视频| 亚洲精品av在线播放| 欧美日韩亚洲成人| 色综合伊人色综合网| 欧美亚洲视频在线看网址| 欧美极品在线视频| 美女视频黄免费的亚洲男人天堂| 欧美成人免费va影院高清| 欧美亚洲在线视频| www.国产一区| 精品久久久久久久久久ntr影视| 欧美精品一二区| 最近2019中文免费高清视频观看www99| 欧美性猛交xxxxx免费看| 国产精品羞羞答答| 国产丝袜精品第一页| 亚洲午夜色婷婷在线| 一区二区三区动漫| 欧美成人黑人xx视频免费观看| 国产精品男女猛烈高潮激情|