簡單的說就是在一個(gè)類、接口或者方法的內(nèi)部創(chuàng)建另一個(gè)類。這樣理解的話創(chuàng)建內(nèi)部類的方法就很明確了。
package com.gissky.innerClass;public class Path { public class Start{ public Start(String start){ this.start=start; } PRivate String start; public String getStart() { return start; } } public class End{ public End(String end){ this.end=end; } private String end; public String getEnd() { return end; } }}2.內(nèi)部類的實(shí)例化2.1 在外部類的非靜態(tài)方法內(nèi)部實(shí)例化,這個(gè)很簡單,和我們平時(shí)做的一樣。public Start start(String start){ return new Start(start);}public End end(String end){ return new End(end);}2.2 在外部類的非靜態(tài)方法外部的任意位置創(chuàng)建內(nèi)部類對象的時(shí)候,必須具體指明這個(gè)對象的類型,即:OuterClassName.InnerClassName,并且必須在有外部類對象的前提下進(jìn)行,使用類似:外圍類對象.new 內(nèi)部類();這樣的語法。public static void main(String[] args){ Path path=new Path(); Path.Start start=path.new Start("my home"); Path.End end=path.new End("fu zhou"); path.Go(start, end);}3. 內(nèi)部類有一個(gè)指向外部類的對象的引用內(nèi)部類擁有其外部類的所有元素的訪問權(quán)。這是如何做到的呢?修改一下上面的程序:
public class Path { private double startTime; private double endTime; public class Start{ public Start(String start){ startTime=new Date().getTime(); this.start=start; } private String start; public String getStart() { return start; } } public class End{ public End(String end){ this.end=end; endTime =new Date().getTime(); } private String end; public String getEnd() { return end; } } public void Go(Start start,End end){ System.out.println("start to end"); System.out.println(endTime-startTime); } public Start start(String start){ return new Start(start); } public End end(String end){ return new End(end); }}這是編譯器已經(jīng)幫我們生成了3個(gè).class文件:

前兩個(gè)便是編譯器生成的內(nèi)部類的.class文件,奇怪的是它們是以其外部類開頭再加上$和自己類的名字。我們用javap反編譯工具查看一下:

由此我們便可以知道,內(nèi)部類中訪問外部類的成員變量是通過一個(gè)調(diào)用外部類的一個(gè)靜態(tài)方法進(jìn)行的,該靜態(tài)方法須傳入一個(gè)外圍類的引用。
到這里應(yīng)該明白2.2中提到的“在外部類的非靜態(tài)方法外部的任意位置創(chuàng)建內(nèi)部類對象的時(shí)候,必須具體指明這個(gè)對象的類型,即:OuterClassName.InnerClassName,并且必須在有外部類對象的前提下進(jìn)行”的原因了吧。那是因?yàn)椋瑑?nèi)部類要連接到創(chuàng)建它的外部類對象上。
這樣做不是存在安全風(fēng)險(xiǎn)嗎?這種擔(dān)心是很有道理的。任何人都可以通過調(diào)用access$0方法很容易地讀取到私有域beep。當(dāng)然,access$0不是Java的合法方法名。但熟悉類文件結(jié)構(gòu)的黑客可以使用十六進(jìn)制編輯器輕松地創(chuàng)建一個(gè)用虛擬機(jī)指令調(diào)用那個(gè)方法的類文件。由于隱秘地訪問方法需要擁有包可見性,所以攻擊代碼需要與被攻擊類放在同一個(gè)包中。
總而言之,如果內(nèi)部類訪問了私有數(shù)據(jù)域,就有可能通過附加在外圍類所在包中的其他類訪問它們,但做這些事情需要高超的技巧和極大的決心。程序員不可能無意之中就獲得對類的訪問權(quán)限,而必須刻意地構(gòu)建或修改類文件才有可能達(dá)到這個(gè)目的。
4. 靜態(tài)內(nèi)部類首先靜態(tài)內(nèi)部類沒有像普通內(nèi)部類那么多的限制了,它就不需要對外部類對象的引用。
有時(shí)候,使用內(nèi)部類只是為了把一個(gè)類隱藏在另外一個(gè)類的內(nèi)部,并不需要內(nèi)部類引用外圍類對象。為此,可以將內(nèi)部類聲明為static,以便取消產(chǎn)生的引用。
注釋:在內(nèi)部類不需要訪問外圍類對象的時(shí)候,應(yīng)該使用靜態(tài)內(nèi)部類。有些程序員用嵌套類(nested class)表示靜態(tài)內(nèi)部類。
注釋:聲明在接口中的內(nèi)部類自動(dòng)成為static和public。
5. 內(nèi)部類與向上轉(zhuǎn)型public interface Fly { public void fly();}public class Animal { private class Bird implements Fly{ @Override public void fly() { System.out.println("Bird.flu()"); } } public Fly Bfly(){ return new Bird(); } public static void main(String[] args) { Animal animal=new Animal(); Fly fly=animal.Bfly(); fly.fly(); }}由上述代碼可知,我們無法將Fly接口向下轉(zhuǎn)型,因?yàn)槲覀儫o法知道內(nèi)部類Bird的名字(它是private)。因此private內(nèi)部類可以完全阻止任何依賴于類型的編碼。客戶端不能訪問到任何新加在內(nèi)部類中的接口,所以擴(kuò)展接口是沒有價(jià)值的(阻止了is-like-a形式的出現(xiàn))。并且由于內(nèi)部類是private的,因此它對于客戶端來說是完全不可見的,并且不可用。所得到的知識指向基類或接口的引用,方便的隱藏了實(shí)現(xiàn)細(xì)節(jié)。
6. 局部內(nèi)部類即,定義在方法和作用域內(nèi)的內(nèi)部類。使用理由:在你的方法或作用域內(nèi)由于某種原因要使用一個(gè)類來輔助你的程序,但是你又不想這個(gè)類是公用的。
如,這樣一個(gè)需求:我進(jìn)行一個(gè)評價(jià)工作,我選取了指標(biāo),然后根據(jù)不同的指標(biāo)讓用戶選擇不同的模型來計(jì)算該指標(biāo)的評價(jià)值。這樣一來,我就需要記錄:指標(biāo)名稱、模型名稱、模型參數(shù)。然后通過反射來計(jì)算。實(shí)際工作中我這樣設(shè)計(jì):
public InfModel Execute(InfModel infModel, String measure, Users user) { //region 內(nèi)部類--用于構(gòu)造各個(gè)指標(biāo)所使用的模型及參數(shù) class Model{ public Method execute; public Object intance; public String modelParam; /**構(gòu)造器 * @param execute 模型執(zhí)行的方法接口 * @param intance 模型實(shí)例 * @param modelParam 構(gòu)造模型所需參數(shù) */ public Model(Method execute,Object intance, String modelParam){ this.modelParam=modelParam; this.execute=execute; this.intance=intance; } } //endregion //后續(xù)工作}局部類不能用public或private訪問說明符進(jìn)行聲明。它的作用域被限定在聲明這個(gè)局部類的塊中。
局部類有一個(gè)優(yōu)勢,即對外部世界可以完全地隱藏起來。除Execute方法之外,沒有任何方法知道Model類的存在。
7.匿名內(nèi)部類public class AnonymousInnerClass { public Destination destination(final String dest){ return new Destination() { @Override public String readLabel() { return dest; } }; } public static void main(String[] args){ AnonymousInnerClass aic=new AnonymousInnerClass(); System.out.println(aic.destination("天涯海角").readLabel()); }}由上面的代碼可以看出,匿名內(nèi)部類也就是沒有類名的一個(gè)內(nèi)部類。因此不要向匿名內(nèi)部類中添加其基類中沒有的接口(因?yàn)榧由弦彩前准?,外面是訪問不到的)。但是為了實(shí)現(xiàn)的需要可以添加一些私有的成員。同時(shí)我們也注意到,內(nèi)部類內(nèi)部希望使用一個(gè)在其外部定的對象時(shí),編譯器則要求其參數(shù)應(yīng)用必須是final的。
為什么這個(gè)參數(shù)必須為final的呢?為此我們仔細(xì)地考查一下控制流程。
這時(shí)在內(nèi)部類中產(chǎn)生了“閉包”,閉包將使得dest脫離了它所在的方法繼續(xù)存在,這樣在readLabel方法中就可以任意的修改dest了,但是外部類對此卻全然不知。因此,dest必須是final的。
其實(shí)為了能夠讓readLabel方法工作,Destination類(這里是一個(gè)匿名內(nèi)部類,編譯器給它取的名字為AnonymousInnerClass$1.class)在dest域釋放之前將dest域在自己的構(gòu)造器中進(jìn)行備份。

上面的例子對于接口或者一個(gè)還有無參構(gòu)造器的類來說是可行的。但是當(dāng)一個(gè)類的構(gòu)造器有參數(shù)的時(shí)候該怎么辦呢?很簡單,就像我們new一個(gè)有參構(gòu)造器的類一樣來構(gòu)造即可:
public abstract class Base { { System.out.println("Base instane initializer"); } public Base(int i){ System.out.println("Base constructor, i="+i); } public abstract void f();}public class AnonymousInnerClass { public static Base getBase(int i){ return new Base(i) { { System.out.println("Inside instane initializer"); } @Override public void f() { System.out.println("In AnonymousInnerClass f()"); } }; } public static void main(String[] args){ Base base=getBase(5); base.f(); }}我們發(fā)現(xiàn)這里的變量i沒有要求必須是final的,這是為什么呢?因?yàn)?,這是變量i不是在內(nèi)部類內(nèi)部使用,而是被傳遞給了其基類的構(gòu)造函數(shù),它并不會(huì)在匿名內(nèi)部類中被直接使用。(?'ω')?注:要不要加final就是看局部內(nèi)部類的外圍方法結(jié)束后,其參數(shù)是不是還要被局部內(nèi)部類使用,如果還要被使用,則要加final否則就不用加了。
新聞熱點(diǎn)
疑難解答
圖片精選