菜雞的Java筆記 第二十六 內部類 innerClass
/* innerClass
從實際的開發來看,真正寫到內部類的時候是在很久以後了,短期內如果是自己編寫程式碼,幾乎是見不到內部類出現的
講解它的目的第一個是為了解釋概念,另外一個就是也是為後期的一些複雜程式做鋪墊
所謂的內部類指的是在一個類的內部(外部類)定義類結構的一種處理形式
*/
/* 內部類的基本概念
類的組成永遠都是隻有兩點:成員(Field),方法(Method),但是幾乎所有的程式裡面鬥不會對巢狀的結構有任何的限定
所以內部類指的是在一個類的內部可以繼續巢狀其他類結構的一種形式程式碼
並且從理論上來講可以一直巢狀下去
下面首先來看一下內部類的基本形式
範例:觀察內部類程式碼
class Outer{ // 外部類 private String info = "Hello World!": class lnner{ // 內部類 public void print(){ System,out.println(Outer.this.info): // 輸出info 屬性內容 } } public void fun(){ lnner in = new lnner(): //例項化內部類物件 in.print(): //呼叫內部類的方法 } } public class innerClass{ public static void main(String args[]){ Outer out = new Outer(): out.fun(): } } // 結果 Hello World!
類本身的核心組成就應該是屬性和方法,但是如果引用了內部類,就相當於破壞了這樣的程式結構
所以內部類所帶來的最為嚴重的問題就在於程式結構的破壞,但是破壞了那麼也應該有一些收益
那麼內部類的好處在哪裡呢?為了觀察出內部類的好處,那麼下面將內部類拿出來,變為兩個類
範例:內部類拿出來
class Outer{ // 外部類 private String info = "Hello World!": public void fun(){ // 將當前物件傳遞到內部類之中 lnner in = new lnner(this): //例項化內部類物件 in.print(): // 呼叫內部類的方法 } // 為in定義一個getter方法 public String getlnfo(){ return info: } } class lnner{ // 內部類 private Outer temp: // 此處應該接收外部類例項化好的Outer類物件 public lnner(Outer temp){ this.temp = temp: } public void print(){ // 此處需要訪問外部類中的私有屬性,所以不能夠直接輸出屬性,需要Outer類提供有getter // 方法應該由物件進行呼叫,所以現在需要有一個例項化物件 // 相當於就是外部的out。getlnfo() System,out.println(this.temp.getlnfo()): // 輸出info 屬性內容 } } public class innerClass{ public static void main(String args[]){ Outer out = new Outer(): out.fun(): } } // 結果 Hello World!
程式碼修改完成之後實際上就可以感受到內部類的最大好處是可以直接進行外部類中私有屬性的直接訪問
那麼清楚內部類的基本定義之後,實際上現在又會存在有一下幾個問題:
1.在編寫程式碼一直強調:只要是屬性的訪問,一定要加上“this ”,這個時候由於屬性是外部類中的
所以要想操作this 那麼就必須使用“外部類.this.屬性” ( 上面: Outer.this.info)
如果內部類中的方法直接採用了“this.屬於”表示的是什麼,只是本類(內部類)中的屬性
2.以上的程式碼發現內部類可以方便訪問外部類中的私有屬性,同時發現外部類中的fun()方法裡面也佔著內部類的物件
那麼內部類的私有屬性外部類也可以直接利用物件訪問
class Outer{ // 外部類 private String info = "Hello World!": class lnner{ // 內部類 private String msg = "+++++++++":// 內部類的私有屬性 public void print(){ System,out.println(Outer.this.info): // 輸出info 屬性內容 } } public void fun(){ lnner in = new lnner(): //例項化內部類物件 in.print(): // 呼叫內部類的方法 System.out.println(in.msg):// 內部類物件直接呼叫屬性 } } public class innerClass{ public static void main(String args[]){ Outer out = new Outer(): out.fun(): } } /* 結果 Hello World! ++++++++++++ */
內部類與外部類之間的私有屬性都是可以直接進行訪問的,這一點要比分為兩個類更加的方便
3.現在發現例項化內部類的物件操作都是在外部類中的fun()方法裡面完成的
那麼如果現在不希望通過外部類的方法操作內部類,那麼也可以根據一下的語法,直接在其他類例項化內部類物件
語法結構:
外部類名稱.內部類名稱 物件名稱 = new 外部類().內部類()
內部類的標準的名稱應該是“外部類.內部類”,只不過“.”不可能直接出現在檔案中
所以java 將其進行了轉換,在檔名稱中使用“$”來代替“.”
class Outer{ // 外部類 private String info = "Hello World!": class lnner{ // 內部類 public void print(){ System,out.println(Outer.this.info): // 輸出info 屬性內容 } } } public class innerClass{ public static void main(String args[]){ Outer.lnner in = new Outer().new lnner(): in.print(): } } // 結果 Hello World!
4.內部類中可以利用private定義私有內部類:
class Outer{ // 外部類 private String info = "Hello World!": private class lnner{ // 內部類 public void print(){ System,out.println(Outer.this.info): // 輸出info 屬性內容 } } } public class innerClass{ public static void main(String args[]){ Outer.lnner in = new Outer().new lnner(): in.print(): } } // 結果: 出錯
對於內部類的概念與它的結構有個先期的認識即可
*/
/* 內部類定義深入
在之前使用的都是普通類的形式進行了內部類的定義,實際上在內部結構上也可以應用在抽象類和介面上
範例:在抽象類上定義內部類
abstract class AbstractOuter{ // 抽象類的外部類 public abstract void peintOuter(); abstract class AbstractInner{ // 抽象內部類 public abstract void printInner(); } class Inner{ // 普通的內部類 public void print(){ System.out.println("加油!"); } } } class OuterImpl extends AbstractOuter{ // 繼承了外部抽象類 public void peintOuter(){ new Inner().print(); // 例項化內部類的物件呼叫方法 } } public class innerClass{ public static void main(String args[]){ AbstractOuter out = new OuterImpl(); out.peintOuter(); } }
在定義內部抽象類的時候是不會影響到子類的,也就是說子類只需要覆寫它所繼承的抽象類中的抽象方法即可
範例:定義內部抽象類的子類
abstract class AbstractOuter{ // 抽象類的外部類 public abstract void peintOuter(); abstract class AbstractInner{ // 抽象內部類 public abstract void printInner(); } class Inner{ // 普通的內部類 public void print(){ System.out.println("加油!"); } } } class OuterImpl extends AbstractOuter{ // 繼承了外部抽象類 public void peintOuter(){ new Inner().print(); // 例項化內部類的物件呼叫方法 } class InnerImpl extends AbstractInner{ // 內部抽象類 public void printInner(){ new Inner().print(); } } } public class innerClass{ public static void main(String args[]){ AbstractOuter.AbstractInner in = new OuterImpl(),new InnerImpl(); in.peintOuter(); } }
除了內部抽象類之外也可以實現內部的介面定義
範例:定義內部介面
interface IOuter{ public void peintOuter(); interface IInner{ public void printInner(); } class B{ public void print(){ System.out.println("++++++++"); } } } class OuterImpl implements IOuter{ public void peintOuter(){ class InnerImpl implements IInner{ public void printInner(){ new B().print(); } } } } public class innerClass{ public static void main(String args[]){ IOuter.IInner in = new OuterImpl().new InnerImpl(); in.printInner(); } }
內部類完整的打破了程式結構定義要求,也就是說程式裡面沒有對內部類的結構做出任何的限制,只要該定義符合語法要求即可使用
*/
/* 利用static定義內部類
利用static定義的屬性或者是方法,是不受到類的控制的,也就是說相當於是一個局外的結構
所以如果內部類上用了static定義,那麼就表示此內部類變為了一個外部類,但是需要注意的是,如果它使用了static定義,那麼只能夠訪問外部類中的static屬性
範例:利用static定義內部類
class Outer{ // 外部類 private static String info = "Hello World!": static class lnner{ // 內部類 public void print(){ System.out.println(info): // 輸出info 屬性內容 } } } public class innerClass{ public static void main(String args[]){ } }
那麼既然現在static的內部類變為了外部類,外部類就可以被其他類直接進行操作
但是這個時候的例項化物件格式:
外部類名稱.內部類名稱 物件名稱 = new 外部類.內部類():
加上了static之後,那麼實際上也就表示這個內部類的名字就成了“外部類.內部類”
範例:例項化內部類物件
class Outer{ // 外部類 private static String info = "Hello World!": static class lnner{ // 內部類 public void print(){ System.out.println(info): // 輸出info 屬性內容 } } } public class innerClass{ public static void main(String args[]){ Outer.lnner oi = new Outer.lnner(): oi.print(): } } // 結果 Hello World!
以後見到程式類庫中出現有“Xxxx.Xxxx”就表示內部類
對於 static 定義內部類的形式在實際開發之中使用很多,但是也可以在介面上使用,即:介面內部可以使用 static 定義外部介面
範例:
interface IOuter{ static interface IInner{ // 靜態的內部介面 public void print(); } } class InnerImpl implements IOuter.IInner{ public void print(){ System.ou.println("+++++++++"); } } public class innerClass{ public static void main(String args[]){ IOuter.IInner oi = new InnerImpl(): oi.print(): } }
*/
/* 方法中定義內部類
理論上內部類可以在任何的位置上定義,包括:程式碼塊中,類中,方法中。所以在實際的開發之中,有可能直接在方法裡使用內部類
範例:觀察方法中定義內部類
class Outer{ // 外部類 private String info = "Hello World!": public void dun(int temp){ // 方法中的引數 double sal = 9000.0: // 方法中定義的普通變數 class lnner{ // 內部類,方法中定義 public void print(){ System.out.println("Outer類中的info屬性= "+Outer.this.info): System.out.println("fun()方法中的引數,temp = "+temp): System.out.println("fun()方法定義的臨時變數,sal ="+sal): } } new lnner().print():// 例項化內部類物件,呼叫方法輸出 } } public class innerClass{ public static void main(String args[]){ Outer out = new Outer():// 例項化Outer類物件 out.fun(100): } } /* 結果 Outer類中的info屬性= Hello World! fun()方法中的引數,temp = 100 fun()方法定義的臨時變數,sal = 9000.0 */
注意:現在使用的是JDK1.8版本
那麼這個版本的好處是避免了一些複雜的細節,之所以會避免是為了整體的設計考慮的,而以上的程式碼,在JDK1.7時就會出現問題
因為在JDK1.7以前有一個約定,如果一個方法中定義了內部類,那麼這個內部類要想訪問方法的引數或者是定義的變數,則引數或者是變數前必須加上final
之所以可以不加final了,主要是因為那個Lamde與方法引用一起出現所造成的新局面
*/
/* anonymousInternal 匿名內部類
匿名內部類的使用
內容
匿名內部類 = 沒有名字的內部類
任何的技術出現都有它可以解決的問題,所以下面首先來分析一下沒有匿名內部類的情況
範例:觀察程式程式碼的問題
interface A{// 定義了一個介面 public void ptintA(); } class X implements A{ public void ptintA(){ System.out.println("AAAAAAAAAAAAAAA"); } } publlic class anonymousInternal{ publlic static void main(String args[]){ A a = new X(); a.ptintA(); } }
有了一個介面,而後為介面定義了一個子類,在主類中利用物件的向上轉型進行物件例項化操作
但是現在有一個問題出現了,本程式中為介面A定義了一個子類X,但是假設說此時的這個X子類只使用唯一的一次
那麼有必要將其定義成一個單獨的類嗎?那麼此時的設計就有些誇張了。
那麼就可以利用匿名內部類的概念來解決,而且匿名內部類必須以抽象類或介面為前提進行使用
範例;使用匿名內部類
interface A{// 定義了一個介面 public void ptintA(); } public class anonymousInternal{ public static void main(String args[]){ A a = new A(){ // 匿名內部類 public void ptintA(){ System.out.println("***********************"); } } a.ptintA(); } }
現在的基本感覺此語法很槽糕,程式的結構很混亂
範例:
abstract class AbstractA{ private String mdg = "+++++++++++++"; public String getMsg(){ return this.msg; }public abstract void print(); } public class anonymousInternal{ public static void main(String args[]){ AbstractA a = new AbstractA(){ // 匿名內部類 public void ptint(){ System.out.println(this.getMsg()); } } a.ptintA(); } }
匿名內部類的最大特點是可以減少類的定義個數,所以在以後的開發之中會大量的使用匿名內部類來進行某些介面子類的定義
這一點在後面講解的多執行緒開發之中非常有用處,同時匿名內部類也是 Lembda 表示式的原始雛形
總結
可以看懂匿名內部類就夠了,其他的慢慢隨著程式碼的編寫經驗的增長自然就會了
*/
/* 總結
1.不要去考慮怎麼用,一般的開發很少會用到,但是會使用
2.內部類先看明白它的語法形式
*/