[toc]
內(nèi)部類
- 內(nèi)部類作為外部類的成員,可以使用人已訪問控制符修飾。
- 外部類的上一級程序單元是包栽惶,所以只有兩個作用域:同一個包內(nèi)、任意位置汁蝶。因此需要兩種權(quán)限:包訪問權(quán)限幔亥、公開訪問權(quán)限针肥,對應(yīng)省略訪問修飾控制符和public訪問控制符握恳。因此如果一個外部類不使用任何訪問控制符修飾,則只能被同一個包中其他類訪問拔稳。
- 內(nèi)部類的上一程序單元是外部類礁遵,所以具有4個作用域:同一個類政勃、同一個包懒叛、父子類、和任意位置拆檬,可以有4種訪問權(quán)限控制逝钥。
- 成員內(nèi)部類的class文件格式為:OuterClass$InnerClass.class
- 外部類成員變量持际、內(nèi)部類成員變量與內(nèi)部類里方法的局部變量同名,則可以通過使用this、外部類類名.this作為限定區(qū)分展鸡。
非靜態(tài)內(nèi)部類
定義:一個類放在另一個類的內(nèi)部定義涡尘。
大部分時候都被作為成員內(nèi)部類定義座泳,而不是局部內(nèi)部類。
可以直接訪問外部類的private成員,反過來不成立
因為在非靜態(tài)內(nèi)部類對象里啦扬,保存了一個它寄存的外部類對象的引用瞄摊。
非靜態(tài)內(nèi)部類的方法內(nèi)訪問某個變量時:
1.系統(tǒng)優(yōu)先在方法內(nèi)查找是否存在該名字的局部變量,如果存在即使用蹲嚣;
2.到該方法所在的內(nèi)部類中查找是否存在改名字的成員變量,如果存在即使用狂丝;
3.到內(nèi)部類所在的外部類中查找,如不存在出現(xiàn)編譯錯誤:提示找不到該變量。非靜態(tài)內(nèi)部類成員只有在非靜態(tài)內(nèi)部類范圍內(nèi)是可知的叛本,并不能被外部類直接使用营搅。如果外部類需要訪問非靜態(tài)內(nèi)部類成員峭拘,必須顯示創(chuàng)建非靜態(tài)內(nèi)部類對象來進行訪問。
非靜態(tài)內(nèi)部類里不能有靜態(tài)方法姓惑、靜態(tài)Field唯沮、靜態(tài)初始化塊践险。
靜態(tài)內(nèi)部類
包括靜態(tài)成員、非靜態(tài)成員筷频。
- 靜態(tài)成員不能訪問非靜態(tài)成員。
- 靜態(tài)內(nèi)部類不能訪問外部類的實例成員惩猫,只能訪問外部類的類成員。
- 靜態(tài)內(nèi)部類的實例方法也不能訪問外部類的實例成員左刽,只能訪問外部類的靜態(tài)成員。(靜態(tài)內(nèi)部類是外部類的類相關(guān)舅世,不是外部類的對象相關(guān)查辩。靜態(tài)內(nèi)部類對象不是寄存在外部類對象里的,而是寄存在外部類的類本身中。當靜態(tài)內(nèi)部類對象存在時,并不存在一個被他寄存的外部類壮莹,靜態(tài)內(nèi)部類對象里只有對外部類的類引用杂抽,沒有對外部類的對象應(yīng)用。)
- 外部類不能直接訪問靜態(tài)內(nèi)部類成員刃唐,可以使用靜態(tài)內(nèi)部類的類名作為調(diào)用者來訪問靜態(tài)內(nèi)部類的類成員。
public class A{
static class B{
private static int pro1 = 5;
private int pro2 = 9;
}
public void accessInnerB(){
System.out.println(B.pro1);
System.out.println(new B().pro2);
}
}
內(nèi)部類的使用
(1)外部類內(nèi)部使用內(nèi)部類
不要再外部類的靜態(tài)成員中使用非靜態(tài)內(nèi)部類爽篷,靜態(tài)成員不能訪問非靜態(tài)成員袒啼。
(2)外部類以外使用非靜態(tài)內(nèi)部類
- 內(nèi)部類不能使用private訪問控制權(quán)限,private修飾的內(nèi)部類只能在外部類的內(nèi)部使用按声。
- 省略訪問控制符的內(nèi)部類钠惩,只能被與外部類處于同一個包中的其他類所訪問鲤遥。
- 使用protected修飾的內(nèi)部類爹凹,可悲與外部類處于同一個包中的其他類和外部類的子類所訪問指郁。
- 使用public修飾的內(nèi)部類拷呆,可以在任何地方被訪問绣溜。
(3)在外部類以外使用靜態(tài)內(nèi)部類
語法:new OuterClass.InnerConstructor()
局部內(nèi)部類
定義:把一個內(nèi)部類放在方法中定義
- 上一級程序單元都為方法岁诉,不是類候生,所以目溉,所有的局部成員都不能使用static修飾。
- 局部成員的作用域?qū)嵲谒诘姆椒ǎ渌绦騿卧肋h也不可能訪問另一個方法中的局部成員宽涌,所以所有局部成員都不能使用訪問控制符修飾平夜。
生成的class文件命名格式為:
OuterClass$NInnerClass.class.
public class LocalInnerClass
{
public static void main(String[] args)
{
// 定義局部內(nèi)部類
class InnerBase
{
int a;
}
// 定義局部內(nèi)部類的子類
class InnerSub extends InnerBase
{
int b;
}
// 創(chuàng)建局部內(nèi)部類的對象
InnerSub is = new InnerSub();
is.a = 5;
is.b = 8;
System.out.println("InnerSub對象的a和b實例變量是:"
+ is.a + "," + is.b);
}
}
匿名內(nèi)部類
定義:只需要使用一次的類,必須繼承一個父類
內(nèi)部不能是抽象類
系統(tǒng)在創(chuàng)建匿名內(nèi)部類時卸亮,會創(chuàng)建匿名內(nèi)部類的對象褥芒,因此,不允許將匿名內(nèi)部類定義成抽象類嫡良。
不能定義構(gòu)造器
匿名內(nèi)部類沒有類名,無法定義構(gòu)造器献酗,但匿名內(nèi)部類可以定義實例初始化塊寝受,利用實例初始化塊來完成構(gòu)造器需要完成的事情。
通過接口創(chuàng)建匿名內(nèi)部類時罕偎,也不能顯示創(chuàng)建構(gòu)造函數(shù)很澄,因此只有一個隱式的無參數(shù)構(gòu)造器,所以new接口名后的括號不能傳入?yún)?shù)值。
通過繼承父類創(chuàng)建匿名內(nèi)部類時甩苛,將擁有和父類相似的構(gòu)造器蹂楣,既擁有相同形參列表。
如果匿名內(nèi)部類需要訪問外部類的局部變量讯蒲,必須用final修飾符來修飾外部類的局部變量痊土。
- 通過接口創(chuàng)建匿名內(nèi)部類
interface Product
{
public double getPrice();
public String getName();
}
public class AnonymousTest
{
public void test(Product p)
{
System.out.println("購買了一個" + p.getName()
+ ",花掉了" + p.getPrice());
}
public static void main(String[] args)
{
AnonymousTest ta = new AnonymousTest();
// 調(diào)用test()方法時墨林,需要傳入一個Product參數(shù)赁酝,
// 此處傳入其匿名實現(xiàn)類的實例
ta.test(new Product()
{
public double getPrice()
{
return 567.8;
}
public String getName()
{
return "AGP顯卡";
}
});
}
}
- 繼承父類創(chuàng)建匿名內(nèi)部類
abstract class Device
{
private String name;
public abstract double getPrice();
public Device(){}
public Device(String name)
{
this.name = name;
}
// 此處省略了name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
}
public class AnonymousInner
{
public void test(Device d)
{
System.out.println("購買了一個" + d.getName()
+ ",花掉了" + d.getPrice());
}
public static void main(String[] args)
{
AnonymousInner ai = new AnonymousInner();
// 調(diào)用有參數(shù)的構(gòu)造器創(chuàng)建Device匿名實現(xiàn)類的對象
ai.test(new Device("電子示波器")
{
public double getPrice()
{
return 67.8;
}
});
// 調(diào)用無參數(shù)的構(gòu)造器創(chuàng)建Device匿名實現(xiàn)類的對象
Device d = new Device()
{
// 初始化塊
{
System.out.println("匿名內(nèi)部類的初始化塊...");
}
// 實現(xiàn)抽象方法
public double getPrice()
{
return 56.2;
}
// 重寫父類的實例方法
public String getName()
{
return "鍵盤";
}
};
ai.test(d);
}
}
閉包 & 回調(diào)
閉包:能被調(diào)用的對象旭等,保存了它的作用域信息酌呆。
回調(diào):某個方法一旦獲得了內(nèi)部類對象的引用后,就可以再合適的時候反過來去調(diào)用外部類實例的方法搔耕。(允許客戶類通過內(nèi)部類引用來調(diào)用其外部類的方法)
- 接口
interface Teachable
{
void work();
}
- 程序員
public class Programmer
{
private String name;
//Programmer類的兩個構(gòu)造器
public Programmer(){}
public Programmer(String name)
{
this.name = name;
}
//此處省略了name的setter和getter方法
public void setName(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public void work()
{
System.out.println(name + "在燈下認真敲鍵盤...");
}
}
public class TeachableProgrammer extends Programmer
{
public TeachableProgrammer(){}
public TeachableProgrammer(String name)
{
super(name);
}
//教學(xué)工作依然由TeachableProgrammer類定義
private void teach()
{
System.out.println(getName() + "教師在講臺上講解...");
}
private class Closure implements Teachable
{
/*
非靜態(tài)內(nèi)部類回調(diào)外部類實現(xiàn)work方法隙袁,非靜態(tài)內(nèi)部類引用的作用僅僅是
向客戶類提供一個回調(diào)外部類的途徑
*/
public void work()
{
teach();
}
}
//返回一個非靜態(tài)內(nèi)部類引用,允許外部類通過該非靜態(tài)內(nèi)部類引用來回調(diào)外部類的方法
public Teachable getCallbackReference()
{
return new Closure();
}
}
public class TeachableProgrammerTest
{
public static void main(String[] args)
{
TeachableProgrammer tp = new TeachableProgrammer("李剛");
//直接調(diào)用TeachableProgrammer類從Programmer類繼承到的work方法
tp.work();
//表面上調(diào)用的是Closure的work方法弃榨,
//實際上是回調(diào)TeachableProgrammer的teach方法
tp.getCallbackReference().work();
}
}
——整理自瘋狂Java