Java基础篇:内部类详解
目錄:
一、內部類的好處:
二、成員內部類:
三、局部內部類:
四、靜態內部類:
五、匿名內部類:
六、總結:
內部類:可以將一個類的定義放在另一個類的定義內部,這就是內部類。
內部類是一個編譯時概念,編譯后外部類及其內部類會生成兩個獨立的class文件:?OuterClass.class和OuterClass$InnerClass.class。
在Java中內部類主要分為四種:成員內部類、局部內部類、匿名內部類、靜態內部類。
?
一、內部類的好處:
《Thinking in Java》中有這樣一句話:使用內部類最吸引人的原因是:每個內部類都能獨立地繼承一個(接口的)實現,所以無論外部類是否已經繼承了某個(接口的)實現,對于內部類都是沒有影響的。
1、使用內部類最大的優點就是可以實現多重繼承問題:
Java中只能繼承一個類,多重繼承在沒有內部類之前是使用接口來實現的。但使用接口有時候有很多不方便的地方。比如我們實現一個接口就必須實現它里面的所有方法。而有了內部類就不一樣了,它可以使我們的類繼承多個具體類或抽象類。
2、可以解決同時 繼承類和實現接口 的時候,類和接口中出現同名方法的情況:
3、內部類提供了更好的封裝和隱藏,除了外部類,其他類都不能訪問內部類。而且,一般外部類,是不允許有private和protected權限的,但是內部類可以。
4、內部類中的屬性和方法,即使是外部類也不能直接訪問,相反,內部類擁有外部類所有元素的訪問權限,可以直接訪問外部類的屬性和方法,即便是private。
5、內部類可以有多個實例,每個實例都有自己的狀態信息,并且與其他外部類對象的信息相互獨立。
6、在單個外部類中,可以讓多個內部類以不同的方式實現同一個接口,或者繼承同一個類,不受外部類是否繼承接口影響。
7、內部類并沒有令人迷惑的“is-a”關系,他就是一個獨立的實體。
8、有利于回調函數的編寫。
?
二、成員內部類:
(1)成員內部類作為外部類的一個成員存在,與外部類的屬性、方法并列,可以無限制的訪問外部類的所有成員,即使是private的也可以訪問。但是外部類要訪問內部類的成員屬性和方法則要通過內部類實例來訪問。
(2)成員內部類不能存在任何static的靜態變量和方法;
(3)成員內部類是依附于外部類的,所以只有先創建了外部類才能夠創建內部類。
(4)用內部類定義在外部類中不可訪問的屬性,這樣就在外部類中實現了比外部類的private還要小的訪問權限。
public class OuterClass {private String str;public void outerDisplay(){System.out.println("outerClass...");}public class InnerClass{public void innerDisplay(){//使用外部類的屬性str = "chenssy...";System.out.println(str);//使用外部類的方法outerDisplay();}}/*推薦使用getxxx()來獲取成員內部類,尤其是該內部類的構造函數無參數時 */public InnerClass getInnerClass(){return new InnerClass();}public static void main(String[] args) {OuterClass outer = new OuterClass();OuterClass.InnerClass inner = outer.getInnerClass();inner.innerDisplay();} } -------------------- chenssy... outerClass...?
三、局部內部類:
(1)即在方法中或者作用域中定義的內部類,局部內部類與成員內部類基本一致,只是他們的作用域不同,局部內部類只能在該方法中被使用,出了該方法就會失效。
(2)局部內部類的使用主要是應用與解決比較復雜的問題,想創建一個類來輔助我們的解決方案,但又不希望這個類是公共可用的,所以就產生了局部內部類。
(3)與局部變量相似,在局部內部類前面不能使用private、protected、public等訪問修飾說明符修飾,也不能使用static修飾,但可以使用final和???abstract修飾。
(4)局部內部類中不可定義static靜態變量,static方法中定義的內部類可以訪問外部類定義的static成員。
(5)局部內部類可以訪問外部類的局部變量(即方法內的變量),但是變量必須是final。
(6)在類外不可直接生成局部內部類(保證局部內部類對外是不可見的)。要想使用局部內部類時,需要生成對象,對象調用方法,在方法中才能調用局部內部類。
//定義在方法中: public class Parcel5 {public Destionation destionation(String str){class PDestionation implements Destionation{private String label;private PDestionation(String whereTo){label = whereTo;}public String readLabel(){return label;}}return new PDestionation(str);}public static void main(String[] args) {Parcel5 parcel5 = new Parcel5();Destionation d = parcel5.destionation("chenssy");} } //定義在作用域中: public class Parcel6 {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("chenssy");String string = ts.getSlip();}}public void track(){internalTracking(true);}public static void main(String[] args) {Parcel6 parcel6 = new Parcel6();parcel6.track();} }?
四、靜態內部類:
(1)靜態內部類定義在類中、 任何方法外,用static修飾,在靜態內部類中可以定義靜態或者非靜態的成員。
(2)靜態內部類與非晶體啊內部類之間存在一個最大的區別:非靜態內部類在編譯完成之后會隱含地保存著一個引用,該引用是指向創建它的外部類,但是靜態內部類去沒有。
(3)靜態內部類的創建不依賴外部類,可以直接創建,而不需要通過生成外部類對象來生成。
Outer.Inner in = new Outer.Inner();
(4)靜態內部類可以定義靜態或者非靜態的成員,但是不可以訪問外部類的非static成員變量和方法,而內部類則都可以;
(5)靜態內部類可以用public,protected,private修飾。
public class OuterClass {private String sex;public static String name = "chenssy";/***靜態內部類*/static class InnerClass1{/* 在靜態內部類中可以存在靜態成員 */public static String _name1 = "chenssy_static";public void display(){/* * 靜態內部類只能訪問外部類的靜態成員變量和方法* 不能訪問外圍類的非靜態成員變量和方法*/System.out.println("OutClass name :" + name);}}/*** 非靜態內部類*/class InnerClass2{/* 非靜態內部類中不能存在靜態成員 */public String _name2 = "chenssy_inner";/* 非靜態內部類中可以調用外部類的任何成員,不管是靜態的還是非靜態的 */public void display(){System.out.println("OuterClass name:" + name);}}/*** @desc 外部類方法* @return void*/public void display(){/* 外部類訪問靜態內部類:內部類. */System.out.println(InnerClass1._name1);/* 靜態內部類 可以直接創建實例不需要依賴于外圍類 */new InnerClass1().display();/* 非靜態內部的創建需要依賴于外圍類 */OuterClass.InnerClass2 inner2 = new OuterClass().new InnerClass2();/* 訪問非靜態內部類的成員需要使用非靜態內部類的實例 */System.out.println(inner2._name2);inner2.display();}public static void main(String[] args) {OuterClass outer = new OuterClass();outer.display();} } ---------------- Output: chenssy_static OutClass name :chenssy chenssy_inner OuterClass name:chenssy?
五、匿名內部類:
1、定義類的最終目的是創建一個類的實例,但是如果某個類的實例只是用一次,則可以將類的定義與類的創建放到一起完成,或者說在定義類的同時就創建一個類。以這種方法定義的沒有名字的類稱為匿名內部類。
(1)匿名內部類是沒有訪問修飾符的;
(2)匿名內部類所在方法的形參需要被匿名內部類使用時,這個形參必須是final;
(3)匿名內部類是沒有構造方法的,因為他沒有類名。由于不知道類名,也不能使用關鍵字來創建該類的實例。實際上匿名內部類的定義、構造、和第一次使用都發生在同樣一個地方。
(4)匿名內部類中不能存在任何靜態變量和方法;
(5)匿名內部類必須繼承一個抽象類或者實現一個接口,一般隱式的繼承某一個父類或者實現某一個接口,不需要使用extends和implements關鍵字。但不能同時繼承類和實現接口。
(6)一個匿名內部類一定是在new的后面,用其隱含地實現一個接口或者實現一個類,沒有類名,根據多態,我們使用其父類名。因為他是局部內部類,那么局部內部類的所有限制都對其生效。匿名內部類是唯一一種無構造方法類。
(7)大部分匿名內部類是用于接口回調用的,匿名內部類在編譯的時候由系統自動起名Out$1.class。如果一個對象編譯時的類型是接口,那么其運行的類型為實現這個接口的類。
(8)匿名內部類不能是抽象的,它必須要實現繼承的類或者實現的接口的所有抽象方法。
public class OuterClass {public InnerClass getInnerClass(final int num,String str2){return new InnerClass(){int number = num + 3;public int getNumber(){return number;}}; /* 注意:分號不能省 */}public static void main(String[] args) {OuterClass out = new OuterClass();InnerClass inner = out.getInnerClass(2, "chenssy");System.out.println(inner.getNumber());} }interface InnerClass {int getNumber(); }---------------- Output: 52、當方法中的參數需要被匿名內部類使用時,為什么使用要使用final?
https://blog.csdn.net/chenssy/article/details/13170015
在內部類編譯成功后,他會產生一個class文件,該class文件與外部類并不是同一個class文件,僅僅保留對外部類的引用。當外部類傳入的參數需要被內部類調用時,從java程序角度來看是直接被調用:
public class OuterClass {public void display(final String name,String age){class InnerClass{void display(){System.out.println(name);}}} }從上面代碼中看,好像參數應該是被內部類直接調用?其實不然,在Java編譯之后,實際操作如下:
public class OuterClass$InnerClass {public InnerClass(String name,String age){this.InnerClass$name = name;this.InnerClass$age = age;}public void display(){System.out.println(this.InnerClass$name + "----" + this.InnerClass$age );} }從上面代碼來看,內部類并不是直接調用方法傳遞的參數,而是利用自身的構造器對傳入的參數進行備份,自己內部方法調用的實際是自己的屬性而不是外部方法傳遞進來的參數。
直到這里還沒有解釋為什么是final?在內部類中的屬性和外部方法的參數兩者從外表上看是同一個東西,但實際上卻不是,所以他們兩者是可以任意變化的,也就是說在內部類中我對屬性的改變并不會影響到外部的形參,而然這從程序員的角度來看這是不可行的,畢竟站在程序的角度來看這兩個根本就是同一個,如果內部類該變了,而外部方法的形參卻沒有改變這是難以理解和不可接受的,所以為了保持參數的一致性,就規定使用final來避免形參的不改變。
?簡單理解就是,拷貝引用,為了避免引用值發生改變,例如被外部類的方法修改等,而導致內部類得到的值不一致,于是用final來讓該引用不可改變。
故如果定義了一個匿名內部類,并且希望它使用一個其外部定義的參數,那么編譯器會要求該參數引用是final的。
3、匿名內部類的初始化:
我們一般都是利用構造器來完成某個實例的初始化工作的,但是匿名內部類是沒有構造器的!那怎么來初始化匿名內部類呢?使用構造代碼塊!利用構造代碼塊能夠達到為匿名內部類創建一個構造器的效果。
public class OutClass {public InnerClass getInnerClass(final int age,final String name){return new InnerClass() {int age_ ;String name_;//構造代碼塊完成初始化工作{if(0 > age && age < 200){age_ = age;name_ = name;}}public String getName() {return name_;}public int getAge() {return age_;}};}public static void main(String[] args) {OutClass out = new OutClass();InnerClass inner_1 = out.getInnerClass(201, "chenssy");System.out.println(inner_1.getName());InnerClass inner_2 = out.getInnerClass(23, "chenssy");System.out.println(inner_2.getName());} }?
六、總結:
1、首先,把內部類作為外部類的一個特殊的成員來看待,因此它有類成員的封閉等級:private,protected,default,public,它有類成員的修飾符: static,final,abstract;
2、非靜態內部類nested inner class:隱含有一個外部類的指針this,因此,它可以訪問外部類的一切資源,包括private。
外部類訪問內部類的成員,先要取得內部類的對象,并且取決于內部類成員的封裝等級。
非靜態內部類不能包含任何static成員。
3、靜態內部類:static inner class,不再包含外部類的this指針,并且在外部類裝載時初始化。
靜態內部類能包含static或非static成員;
靜態內部類只能訪問外部類static成員;
外部類訪問靜態內部類的成員,循一般類法規。對于static成員,用類名.靜態成員即可訪問,對于非static成員,只能用 對象.非靜態成員 進行訪問。
4、對于方法中的內部類或塊中內部類(即局部內部類)只能訪問塊中或方法中的final變量。
5、局部內部類只允許訪問方法中的final局部變量和方法的final參數列表,所以說局部內部類和內部類沒什麼區別。但局部內部類不能在方法以外訪問,方法中不可以有static內部類。
6、匿名內部類如果繼承自接口,必須實現指定接口的方法,且無參數?;匿名內部類如果繼承自類,參數必須按父類的構造函數的參數傳遞。
7、外部類與內部類、局部內部類 的?訪問修飾符:
(1)對于外部類,只有兩種訪問修飾符,public與默認(default),因為外部類放在包中,只有兩種可能,包可見與包不可見。
(2)對于內部類(局部內部類、匿名內部類除外),可以使用所有的訪問修飾符,因為內部類放在外部類中,與成員變量的地位一致,所以有四種可能。
(3)局部內部類與局部變量一樣不能用訪問權限修飾符。
?
參考文章:https://blog.csdn.net/chenssy/article/details/13024951
總結
以上是生活随笔為你收集整理的Java基础篇:内部类详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java基础篇:四种代码块详解
- 下一篇: Java基础篇:强制类型转换