JavaSE實戰——面向對象(中) 抽象類,接口,多態,內部類,匿名內部類

    轉載請聲明出處:http://blog.csdn.net/zhongkelee/article/details/44831675

    我們接着上一篇博客“面向對象(上)”的內容,繼續往下說。

抽象類

    描述一個事物,卻沒有足夠信息,這時就將這個事物稱爲抽象事物。面對抽象的事物,雖然不具體,但是可以簡單化,不用面對具體的事物。(這樣其實更方便於面對同一類對象)

    抽象類:籠統,模糊,看不懂,不具體!
    抽象類也是不斷地向上抽取而來的。但是隻抽取了方法聲明,並不抽取具體的方法實現。只在乎有哪些功能,具體功能實現內容,由不同子類完成。抽象類中,可以定義抽象內容讓子類實現,也可以定義非抽象內容讓子類直接使用。它裏面定義的都是一些體系中的基本內容。
    抽象類的特點:
    1.抽象方法只能定義在抽象類中,抽象類和抽象方法都需要用abstract來修飾。(abstract可以描述類和方法,不可以描述變量)
    2.抽象類中,也可以定義非抽象方法。
    3.抽象方法只定義方法聲明,並不定義方法實現。
    4.抽象類不能實例化。不能用new關鍵字創建對象。
    5.只有子類繼承抽象類並覆蓋了抽象類中的所有抽象方法後,子類才能具體化(實例化),子類纔可以創建對象。否則,該子類還是一個抽象類。(好處:強制抽象類的子類具備並實現所有的抽象方法,才能實例化。)

abstract class 犬科{
	abstract void 吼叫();
}
class 狗 extends 犬科{
	void 吼叫(){
		System.out.println("汪汪");
	}
}
class 狼 extends 犬科{
	void 吼叫(){
		System.out.println("嗷嗷");
	}
}
class AbstractDemo{
	public static void main(String[] args){
		
	}
}
    關於抽象類,這裏註明幾個細節問題:
    1.抽象類中有構造函數嗎?
       有。抽象類的構造函數雖然不能給抽象類對象實例化,因爲抽象類不能創建對象,
       但是抽象類有子類,它的構造函數可以給子類對象實例化。

    2.抽象類和一般類的異同
       相同點:
             抽象類和一般類都是用來描述事物的,都在內部定義了成員,進行屬性和行爲的描述。
       不同點;
             a.抽象類描述事物的信息不具體。
                一般類描述事物的信息具體。
             b.一般類中不能定義抽象方法,只能定義非抽象方法。
                抽象類中可定義抽象方法,同時也可以定義非抽象方法。
             c.一般類可以被實例化。
                抽象類不可以被實例化。
    3.抽象類一定是個父類嗎?
       是的。因爲需要子類覆蓋所有抽象方法後,纔可以對子類實例化,使用這些方法。
       abstract class Demo extends Fu{}
       class Sub extends Demo{}
    4.抽象類中可以不定義抽象方法嗎?
       可以。但是很少見,目的就是不讓該類創建對象。AWT的適配器對象就是這種類。
       通常這個類中的方法有方法體,但是卻沒有內容。

abstract class Demo{
		void show1()
		{}
		void show2()
		{}
	}
    5.抽象關鍵字abstract和哪些關鍵字不能共存呢?
        final: final修飾類或者方法不可以被繼承或覆蓋,abstract修飾的類或方法必須有子類或被覆蓋。
        private:私有的方法,子類無法訪問到,不叫覆蓋。(public不能覆蓋private。final不能被覆蓋。)
        static:靜態區中的內容不需要對象,直接類名調用,抽象類名調用抽象方法沒有意義。

    需求:公司中程序員有姓名,工號,薪水,工作內容。
              項目經理除了有姓名,工號,薪水,還有獎金,工作內容。
    對給出需求進行數據建模。
    分析:
           程序猿:
                  屬性:姓名,工號,薪水
                  行爲:工作內容
          項目經理:
                  屬性:姓名,工號,薪水,還有獎金
                  行爲:工作內容
    兩者不存在所屬關係,但是有共性內容,可以向上抽取。
    兩者的共性類型是什麼?僱員。
    僱員:
           屬性:姓名,工號,薪水
           行爲:工作內容

abstract class Employee{//對於老闆而言,面對僱員來指揮做事情,比面對程序猿和經理做事情,簡單得多。
	private String name;
	private String id;
	private double pay;
	Employee(String name, String id, double pay){
		this.name = name;
		this.id = id;
		this.pay = pay;
	}
	public abstract void work();
}
class Programmer extends Employee{
	Programmer(String name, String id, double pay){
		super(name,id,pay);
	}
	public void work(){
		System.out.println("code");
	}
}
class Manager extends Employee{
	private double bonus;
	Manager(String name, String id, double pay, double bonus){
		super(name,id,pay);
		this.bonus = bonus;
	}
	public void work(){
		System.out.println("manage");
	}
}
class AbstractDemo{
	public static void main(String[] args){
		new Manager("lichunchun","SA14010001",200000,5000).work();
	}
}
模板方法設計模式

    解決的問題:當功能內部一部分實現時確定,一部分實現是不確定的。這時可以把不確定的部分暴露出去,讓子類去實現。

abstract class GetTime{
	public final void getTime(){//此功能如果不需要複寫,可加final限定
		long start = System.currentTimeMillis();
		code();//不確定的功能部分,提取出來,通過抽象方法實現
		long end = System.currentTimeMillis();
		System.out.println("runtime : "+(end - start)+" ms");
	}
	public abstract void code();//抽象不確定的功能,讓子類複寫實現
}
class SubDemo extends GetTime{
	public void code(){//子類複寫功能方法
		for(int x = 0; x < 1000; x++){
			System.out.println("x");
		}
	}
}
class TemplateDemo{
	public static void main(String[] args){
		new SubDemo().getTime();
	}
}
接口

abstract class AbsDemo{
		public abstract void show1();
		public abstract void show2();
}
     抽象類中所有的方法都是抽象的。這時,可以把抽象類用另一種形式來表示:接口。初期可以理解爲接口是特殊的抽象類。
    定義接口使用的關鍵字不是class,是interface。
    接口中包含的成員,最常見的有全局常量、抽象方法。
    注意:接口中的成員都有固定的修飾符,即使不寫,編譯器也會自動補全。

               成員變量:public static final 
               成員方法:public abstract

               共性:成員都是public修飾的。

interface Inter{
	public static final int NUM = 4;
	public abstract void show1();
	public abstract void show2();
}
class Demo implements Inter{
	public void show1(){}
	public void show2(){}
}
class InterfaceDemo{
	public static void main(String[] args){
		new Demo();
	}
}
    接口的特點:
    1.接口不可以實例化。因爲接口中的方法都是抽象方法。
    2.接口的子類必須實現了接口中所有的抽象方法後,該子類纔可以實例化。否則,該子類還是一個抽象類。
    3.接口是用來被實現的。
    類與類之間存在着繼承關係,類與接口中間存在的是實現關係。繼承用extends,實現用implements。

class Fu1{
<span style="white-space:pre">	</span>void show(){
		sop("Fu1 show");
	}
}
class Fu2{
	void show(){
		sop("Fu2 show");
	}
}
class Zi extends Fu1,Fu2{
	
}
new Zi().show();//調用的不確定性。原因在於方法主體內容不同。
    Java中不直接支持多繼承,而是對該機制進行改良。通過接口來解決問題。將多繼承轉換成了多實現,在子類具體實現時,子類中才有方法主體,不會出現調用的不確定性。
interface InterA{
	public abstract void show();
}
interface InterB{
	public abstract void show();
}
class SubInter implements InterA,InterB{//多實現
	public void show(){
		System.out.println("inter show");
	}
}
class InterfaceDemo2{
	public static void main(String[] args){
		SubInter in = new SubInter();
		in.show();
	}
}
    其實,我們可以形象地將繼承和實現比喻爲只認一個爹,但可以有很多個叔叔,只不過這裏的叔叔沒有方法主體。

class Fu{
	void show(){
		System.out.println("fu show");
	}
}
interface Inter{
	public abstract void show1();
}
class Zi extends Fu implements Inter{
	public void method(){
		System.out.println("zi method");
	}
	public void show1(){
		System.out.println("zi show1");
	}
}
class InterfaceDemo3{
	public static void main(String[] args){
		Zi z = new Zi();
		z.show();
		z.show1();
		z.method();
	}
}
     類與類之間:單繼承關係。is a,是什麼。
    類與接口之間:實現關係。like a,像什麼。
    接口與接口之間:多繼承關係。
interface Inter1{
	public abstract void show1();
}
interface Inter2{
	public abstract void show2();
}
interface Inter3 extends Inter1,Inter2{
	public abstract void show3();
}
class Inter implements Inter3{
	public void show1(){}
	public void show2(){}
	public void show3(){}
}
    爲了方便創建Inter接口的子類對象,可以用一個類先把接口中的所有方法都空實現。該類創建對象沒有意義,所以可以將該類抽象。這就是傳說中的沒有抽象方法的抽象類。
interface Inter{
	public abstract void show1();
	public abstract void show2();
	public abstract void show3();
	public abstract void show4();
}
abstract class Demo implements Inter{//沒有抽象方法的抽象類,方便創建接口的子類對象。
	//private Demo(){} 不抽象,光私有化構造函數是不可以的。
	//因爲Demo類是需要被子類繼承的,子類對象初始化是要訪問父類構造函數的,不可以私有化。
	public void show1(){}
	public void show2(){}//空實現
	public void show3(){}
	public void show4(){}
}
class DemoA extends Demo /*implements Inter*/{
	public void show1(){
		System.out.println("show1");
	}
}
class DemoB extends Demo /*implements Inter*/{
	public void show3(){
		System.out.println("show3");
	}
}
class InterfaceDemo4{
	public static void main(String[] args){
		
	}
}
   接口的思想
        筆記本電腦的USB接口。
        1.接口的出現擴展了功能。
        2.接口其實就是暴露出來的規則。
        3.接口的出現降低了耦合性。解耦~
    接口的出現,產生了兩方:一方在使用接口(筆記本),一方在實現接口(USB鼠標)。

    開發順序:接口->使用接口的類->實現接口的類,這其中,使用接口的類面向的是接口。

class Mouse{}
interface USB{}
class NewMouse extends Mouse implements USB{}
    抽象類:用於描述事物的共性基本功能,是一個體系單元的共性內容的向上抽取。
    接口:用於定義的都是事物的額外擴展功能。
    共性:都是不斷向上抽取的結果。
    抽象類和接口的區別:
    1.類與類之間是繼承關係,而且只能單繼承
       類與接口之間是實現關係,而且可以多實現
    2.抽象類中可以定義抽象和非抽象方法,子類可以直接使用,或者覆蓋使用。
       接口中定義的都是抽象方法,子類必須全部實現纔可以使用。
    3.抽象類使用的是 is a 關係
       接口使用的是 like a 關係
    4.抽象類的成員修飾符可以自定義(除了abstract部分前面說的那些限制條件)
       接口中的成員修飾符都是固定的。(public static final 和 public abstract)
    5.抽象類有構造函數
       接口沒有構造函數

abstract class 犬{
	public abstract void 吃();
	public abstract void 叫();	
}
interface 緝毒
{
	public abstract void 緝毒();
}
class 緝毒犬 extends 犬 implements 緝毒{
	public void 吃(){}
	public void 叫(){}
	public void 緝毒(){}
}
多態

    下面我們再來說面向對象核心思想的最後一個特徵:多態。

    多態在程序中的體現:父類的引用或者接口的引用指向了子類的對象。
    多態的好處:提高了代碼的擴展性。
    多態的弊端:不能使用子類的特有方法。(即訪問的侷限性)
    多態的前提:
        1.必須有關係,繼承,實現。
        2.通常有覆蓋。
    多態的出現思想上也做着變化:以前是創建對象並指揮對象做事情。有了多態以後,我們可以找到對象的共性類型,直接操作共性類型做事情即可,這樣可以指揮一批對象做事情,即通過操作父類或接口實現。

abstract class Animal{
	public abstract void eat();
}
class Dog extends Animal{
	public void eat(){
		System.out.println("bones");
	}
	public void lookHome(){
		System.out.println("look home");
	}
}
class Cat extends Animal{
	public void eat(){
		System.out.println("fish");
	}
	public void catchMouse(){
		System.out.println("catch mouse");
	}
}
class Pig extends Animal{
	public void eat(){
		System.out.println("si liao");
	}
	public void gongDi(){
		System.out.println("gong di");
	}
}
class DuotaiDemo{
	public static void main(String[] args){
		/*
		Animal a = new Dog();
		a.eat();//子類方法覆蓋父類,運行的是子類方法
		*/
		method(new Cat());
	}
	//當面對共性類型時,所有的子類對象都可以接收,說明提高了代碼的擴展性。
	public static void method(Animal a){//a = new Dog(); a = new Cat(); a = new Pig();
		a.eat();
	}
}
向上向下轉型

    向上轉型好處:隱藏了子類型,提高了代碼的擴展性。

    向上轉型弊端:只能使用父類中的功能,不能使用子類特有功能。功能被限定了。

    如果不需要面對子類型,通過提高擴展性,或者使用父類的功能即可完成操作,就使用向上轉型。

    向下轉型好處:可以使用子類型的特有功能。

    向下轉型弊端:面對具體的子類型。向下轉型有風險。容易發生ClassCastException。只要轉換類型和對象類型不匹配就會發生。想要安全,必須要進行判斷。

    判斷一個對象是否匹配某一個類型,需要使用一個關鍵字 instanceof ,用法 對象 instanceof 類型。

    什麼時候用向下轉型:需要使用子類型的特有方法時。但一定要用 instanceof 判斷。

abstract class Animal{
	public abstract void eat();
}
class Dog extends Animal{
	public void eat(){
		System.out.println("bones");
	}
	public void lookHome(){
		System.out.println("look home");
	}
}
class Cat extends Animal{
	public void eat(){
		System.out.println("fish");
	}
	public void catchMouse(){
		System.out.println("catch mouse");
	}
}
class DuotaiDemo2{
	public static void main(String[] args){
		Animal a = new Cat();
		//a.eat();
		if (!(a instanceof Dog)){//判斷對象是否實現了指定的接口或繼承了指定的類
			System.out.println("類型不匹配");
			return;
		}
		Dog d = (Dog)a;
		d.eat();
		d.lookHome();
	}
}
    轉型過程中,至始至終,只有子類對象這一個實例在做着類型的變化。

    練習:

    畢姥爺:講課。釣魚。
    畢老師 extends 畢姥爺:講課。看電影。
    要求體現多態,要看到向上轉型,向下轉型。

class 畢姥爺{
	public void 講課(){
		System.out.println("管理");
	}
	public void 釣魚(){
		System.out.println("釣魚");
	}
}
class 畢老師 extends 畢姥爺{
	public void 講課(){
		System.out.println("java");
	}
	public void 看電影(){
		System.out.println("看電影");
	}
}
class DuoTaiTest{
	public static void main(String[] args){
		//多態形式
		畢姥爺 x = new 畢老師();//向上轉型
		x.講課();
		x.釣魚();
		//要想使用畢老師的特有方法 看電影
		畢老師 y = (畢老師)x;//向下轉型
		y.講課();
		y.釣魚();
		y.看電影();
	}
	//轉型過程中,至始至終,只有子類對象這一個實例在做着類型的變化。
}
多態在子父類成員上的特點

    多態中對成員的調用。
    1.成員變量
       當子父類中出現同名成員變量時,
       多態調用時,只看調用該成員變量的引用所屬類中的成員變量。
       簡單說:無論編譯或者運行,都看等號左邊的就哦了。
      (編譯時不產生對象,只檢查語法錯誤)

    2.成員函數
       當子父類中出現一模一樣的函數時,多態調用,
       編譯時,看的是引用變量所屬的類中的方法。(編譯時,還沒有創建對象)
       運行時,看的是對象所屬的類中的方法。(注意,此時父類方法,子類都已繼承,若一模一樣,則子類的覆蓋)
       簡單說:編譯看左邊,運行看右邊
       成員方法動態綁定到當前對象上。

    3.靜態方法
       當子父類中出現一模一樣的函數時,多態調用,
       編譯和運行是看引用變量所屬的類中的方法。
       簡單說:編譯運行都是看左邊
       其實,真正調用靜態方法是不需要對象的,直接類名調用。
       因爲靜態方法綁定到該方法所屬的類上,不所屬於對象。

    注:

       

class Fu{
	int num = 4;
	void show(){
		System.out.println("fu show");
	}
	static void staticMethod(){
		System.out.println("fu static method");
	}
}
class Zi extends Fu{
	int num = 6;
	void show(){
		System.out.println("zi show");
	}
	static void staticMethod(){
		System.out.println("zi static method");
	}
}
class DuoTaiDemo3{
	public static void main(String[] args){
		Fu f = new Zi();
		System.out.println(f.num);
		
		f.show();
		
		f.staticMethod();
	}
}
    4.this.成員變量

       始終代表的是本類引用
       執行哪個類的成員函數,那個成員函數中的this就代表那個類的引用。

class Fu{
	int num = 5;
	void show(){
		System.out.println("num = "+this.num);//this代表的是本類引用。//成員變量:編譯運行看左邊。
	}
}
class Zi extends Fu{
	int num = 6;
}
class DuoTaiTest2{
	public static void main(String[] args){
		Fu f = new Zi();
		f.show();//運行看右邊。右邊是Zi類對象,其實Zi類也是有show()函數的,是繼承的Fu類的。所以運行的是父類的show。
	}
}
class Fu{
	int num = 5;
	void show(){}
}
class Zi extends Fu{
	int num = 6;
	void show(){
		System.out.println("num = "+this.num);//此時,這個this所在的show函數在Zi類中,所以,這個this代表Zi類對象,只要是執行Zi的show函數,都是打印Zi的num值。
	}
}
class DuoTaiTest2{
	public static void main(String[] args){
		Fu f = new Zi();
		f.show();//成員函數,編譯看左邊,運行看右邊。運行的是子類的show。
	}
}
    多態練習:USB接口。

//先定義一個規則。java中可以通過接口的形式來完成規則的定義,進行解耦。
//讓設備和筆記本的耦合性降低,易於擴展和維護。
interface USB{
	public abstract void open();
	public abstract void close();
}
class NoteBook{
	public void run(){
		System.out.println("NoteBook run");
	}
	/**
	使用符合規則的外圍設備。這樣就不用每次面對具體的類型,
	後期加進來的設備都符合這個規則,只要面對規則就可以了。
	*/
	public void useUSB(USB usb){//定義了一個接口類型的引用//USB usb = new MouseByUSB();//多態提高了筆記本的擴展性
		if (usb != null){
			usb.open();
			usb.close();
		}
	}
}
class MouseByUSB /*extends Mouse*/ implements USB{
	public void open(){
		System.out.println("mouse open");
	}
	public void close(){
		System.out.println("mouse close");
	}
}
class KeyBoardByUSB/*extends KeyBoard*/ implements USB{
	public void open(){
		System.out.println("keyboard open");
	}
	public void close(){
		System.out.println("keyboard close");
	}
}
class USBTest{
	public static void main(String[] args){
		NoteBook book = new NoteBook();
		book.run();
		book.useUSB(null);
		book.useUSB(new MouseByUSB());
		book.useUSB(new KeyBoardByUSB());
	}
}
Object類

    Object:所有類的直接或者間接父類,Java認爲所有的對象都具備一些基本的共性內容,這些內容可以不斷的向上抽取,最終就抽取到了一個最頂層的類中的,該類中定義的就是所有對象都具備的功能。
    具體方法:
    1,boolean equals(Object obj):用於比較兩個對象是否相等,其實內部比較的就是兩個對象地址。

          而根據對象的屬性不同,判斷對象是否相同的具體內容也不一樣。

          所以在定義類時,一般都會複寫equals方法,建立本類特有的判斷對象是否相同的依據。

    2,String toString():將對象變成字符串。

          默認返回的格式:類名@哈希值 = getClass().getName() + '@' + Integer.toHexString(hashCode())

          爲了對象對應的字符串內容有意義,可以通過複寫,建立該類對象自己特有的字符串表現形式。 
    3,Class getClass():獲取任意對象運行時的所屬字節碼文件對象
    4,int hashCode():返回該對象的哈希碼值。支持此方法是爲了提高哈希表的性能。
    通常equals,toString,hashCode,在應用中都會被複寫,建立具體對象的特有的內容。

//java.lang.Object
class Person extends Object{
	private int age;
	private String name;
	Person(String name, int age){
		this.age = age;
		this.name = name;
	}
	public boolean equals(Object obj){//覆蓋父類方法 //Object obj = new Person();
		//提高效率。如果兩個引用指向的是同一個對象,不用再轉換並比較內容了。直接判斷地址就可以。
		if (this == obj)
			return true;
		//Object.age是錯誤的,因爲Object中沒有age屬性。
		//要想使用子類對象的特有屬性或者行爲,必須對其進行向下轉型。並且需要進行類型判斷。
		if (!(obj instanceof Person))
			throw new ClassCastException("類型錯誤");
		Person p = (Person)obj;
		//判斷兩個人的名字是否相同,不要用==,字符串本身就是一個對象。使用String類的equals方法判斷。
		return this.name.equals(p.name) && this.age == p.age;
	}
	/**
	建立Person對象特有的字符串表現形式。只要覆蓋toString方法即可。
	*/
	public String toString(){
		return "Person [ name = "+this.name+", age = "+this.age+" ]";
	}
}
class ObjectDemo{
	public static void main(String[] args){
		Person p1 = new Person("lisi",22);
		Person p2 = new Person("wanger",23);
		System.out.println(p1);//Person@1db9742
		System.out.println(p1.toString());
		System.out.println(p2.toString());
		System.out.println(p1.equals(p2));//判斷的是對象的內容,用equals。
		System.out.println(p1 == p2);//判斷的是對象的地址。
		
	}
}
內部類

    內部類其實就是將類定義到了另一個類內部。A類要直接訪問B類中的成員時,可以將A類定義到B類中。作爲B類的內部類存在。

    訪問規則:內部類可以直接訪問外部類中的成員。外部類要想訪問內部類,只能創建內部類的對象來訪問。

    當內部類定義在外部類中的成員位置上,可以使用一些成員修飾符修飾 private、static。

   1:默認修飾符。

       直接訪問內部類格式:外部類名.內部類名 變量名 =  外部類對象.內部類對象;

       Outer.Inner in = new Outer.new Inner();//這種形式很少用。

       但是這種應用不多見,因爲內部類之所以定義在內部就是爲了封裝。

       想要獲取內部類對象通常都通過外部類的方法來獲取。這樣可以對內部類對象進行控制。

   2:私有修飾符。

       通常內部類被封裝,都會被私有化,因爲封裝性不讓其他程序直接訪問。 

   3:靜態修飾符。

       如果內部類被靜態修飾,相當於外部類,會出現訪問侷限性,只能訪問外部類中的靜態成員。

       注意;如果內部類中定義了靜態成員,那麼該內部類必須是靜態的。

   內部類編譯後的文件名爲:“外部類名$內部類名.java”;

class Outer{
	private static int num = 4;
	class Inner{//內部類,相當於外部類中的一個成員。可以被成員修飾符所修飾。//外部類先加載,如果沒有用到非靜態內部類,非靜態內部類的class文件一直不加載。
		static final int count = 5;//在非靜態的內部類中只允許定義靜態的常量。不能定義其它靜態成員。//這是因爲,非靜態內部類如果被調用,需要new Inner()對象,而靜態成員不需要對象即可調用,這是矛盾的。
		void show(){
			System.out.println(num);//Outer.this.num
		}
	}
	static class Inner2{//靜態內部類。相當於一個外部類。隨着Outer的加載而加載。
		void show2(){
			System.out.println("show2..."+num);
		}
		static void show3(){
			System.out.println("show3..."+num);
		}
	}
	public void method(){
		Inner in = new Inner();
		in.show();
	}
}
class InnerClassDemo{
	public static void main(String[] args){
		Outer out = new Outer();
		out.method();
		
		//=========非靜態,非私有的內部類訪問方式=========
		Outer.Inner in = new Outer().new Inner();//需要外部類對象
		in.show();
		
		//=========靜態,非私有的內部類訪問方式1,訪問非靜態成員=========
		Outer.Inner2 in = new Outer.Inner2();//不需要外部類對象
		in.show2();
		
		//=========靜態,非私有的內部類訪問方式2,訪問靜態成員=========
		Outer.Inner2.show3();
	}
}
     爲什麼內部類就可以直接訪問外部類中的成員?

    那是因爲內部類持有了外部的引用:外部類名.this

class Outer{
	int num = 2;
	class Inner{
		int num = 3;
		void show(){
			int num = 4;
			System.out.println("show..."+Outer.this.num);//Inner.this.num
		}
	}
	public void method(){
		new Inner().show();
	}
}
class InnerClassDemo2{
	public static void main(String[] args){
		new Outer().method();
	}
}
    內部類可以定義在外部類中的成員位置上,也可以定義在外部類中的局部位置上。

   注意:局部內部類,只能訪問被final修飾的局部變量

   因爲非final修飾的,生命週期不夠,因爲局部變量消失,建立的對象還沒有消失。

class Outer{
	private int num = 4;
	public void method(){
		final int x = 5;
		class Inner{
			void show(){
				System.out.println("x = "+x);
				System.out.println("num = "+num);
			}
		}
	}
}
class InnerClassDemo2{
	public static void main(String[] args){
		new Outer().method();
	}
}
匿名內部類

    匿名內部類:簡化書寫的內部類。
    前提:內部類需要繼承或者實現外部的類或者接口。
    格式:new 父類or接口名(){定義子類成員或者覆蓋父類方法}.方法。
    匿名內部類其實就是一個子類對象。
    new Demo();---------後面加;號, 就是父類對象。
    new Demo(){}-------後面加{}號,就是子類對象。

abstract class Demo{
	abstract void show();
}
class Outer{
	private int num = 4;
	public void method(){
		new Demo(){
			public void show(){
				System.out.println("show..."+num);
			}
		}.show();
	}
}
class InnerClassDemo3{
	public static void main(String[] args){
		new Outer().method();
	}
}
    當出現多個方法時,可以如下使用:

interface Inter{
	public abstract void show1();
	public abstract void show2();
}
class Outer{
	private int num = 2;
	/*class Inner implements Inter{
		public void show1(){}
		public void show2(){}
	}*/
	public void method(){
		//Inner in = new Inner();
		//in.show1();
		//in.show2();
		Inter in = new Inter(){
			public void show1(){}
			public void show2(){}
		};
		in.show1();
		in.show2();
	}
}
class InnerClassDemo4{
	public static void main(String[] args){
		//new Outer().method();
		new Inner();//不可以。主函數是靜態的,無法訪問靜態成員。靜態方法中,不可以出現this。
		new InnerClassDemo4().new Inner();//可以。
	}
	class Inner{
	
	}
	public void show(){
		this.new Inner();//可以。
	}
}
    經典面試題:如下的1、2寫法正確嗎?有何區別?
class Outer{
	/*class Inner extends Object{
	
	}*/
	public void method(){
		//1
		new Object(){
			public void show(){
				System.out.println("show run");
			}
		}.show();//匿名內部類就是子類對象。子類對象.子類方法
		//2
		Object obj = new Object(){//object obj指向了自己的子類對象。對象向上提升爲了Object。就不能調用子類的特有方法。
			public void show(){
				System.out.println("show run");
			}
		};
		obj.show();//不可以。
	}
}
class InnerClassDemo5{
	public static void main(String[] args){
		
	}
}

    寫法是正確,1和2都是在通過匿名內部類建立一個Object類的子類對象。

    區別:

    第一個可是編譯通過,並運行。

    第二個編譯失敗,因爲匿名內部類是一個子類對象,當用Object的obj引用指向時,就被提升爲了Object類型,而編譯時檢查Object類中是否有show方法,所以編譯失敗。

class InnerClassDemo6 {
	+(static)class Inner{
	void show(){}
	}
	public void method(){
		this.new Inner().show();//可以
	}
	public static void main(String[] args) {//static不允許this
		this.new Inner().show();//錯誤,Inner類需要定義成static
	}
}
    經典筆試題:通過匿名內部類補足Outer類中的代碼。

interface Inter{
	void show();
]
class Outer{//通過匿名內部類補足Outer類中的代碼
	
}
class InnerClassDemo6{
	public static void main(String[] args){
		Outer.method().show();
	}
}
    分析:

    Outer.method():意思是Outer中有一個名稱爲method的方法,而且這個方法是靜態的。

    Outer.method().show():當Outer類調用靜態的method方法運算結束後的結果又調用了show方法,意味着:method()方法運算完一個是對象,而且這個對象是Inter類型的。

    答案:

interface Inter{
	void show();
]
class Outer{//通過匿名內部類補足Outer類中的代碼
	public static Inter method(){
		return new Inter(){
			public void show(){}
		};
	}
}
class InnerClassDemo6{
	public static void main(String[] args){
		Outer.method().show();
	}
}
    此外,當函數的參數是接口類型引用時,匿名內部類還可以作爲方法的參數進行傳遞。

interface Inter{
	public abstract void show();
}
class InnerClassDemo7{
	public static void main(String[] args){
		function(new Inter(){
			public void show(){}
		});//匿名內部類作爲方法的參數進行傳遞
	}
	public static void function(Inter in){//如果function不是static,主函數是無法調用的
		in.show();
	}
}

後面一片博客將會介紹,JavaSE面向對象中的異常、包。

有任何問題請和我聯繫,共同進步:[email protected]

轉載請聲明出處:http://blog.csdn.net/zhongkelee/article/details/44831675

發佈了50 篇原創文章 · 獲贊 137 · 訪問量 32萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章