從新開始學Java——抽象類、接口、內部類

抽象類

爲何要定義抽象方法:若是定義某個方法時不能肯定該方法的具體實現細節; 好比定義 Person 類的 eat 方法時, 不能肯定其具體實現細節,由於中國人、 西方國家的人、 南亞國家的人吃飯方式不同。 能夠把該方法定義成一個抽象方法,具體 的實現細節,交給其後代(子類)來實現。java

抽象方法的定義

使用 abstract 關鍵字修飾方法的定義,方法體必須爲空(不然就不是抽象方法),抽象方法必須是非靜態的(抽象方法不能被 static 修飾), 抽象方法不能被 final 修飾、 不能被 private 修飾.ide

[ 修飾符 ] abstract 返回值類型 methodName() ; //注意這裏沒有 { }

抽象類的定義

爲何要定義抽象類:定義抽象方法的類, 必須被定義成抽象類

抽象類的定義方法設計

[ 修飾符 ] abstract class className {
	//使用 abstract 修飾類定義
}

抽象類的特徵

  • 抽象類不能被實例化 (有構造但僅供子類調用)
  • 抽象類中可包含屬性、 方法、 構造、 內部類、 枚舉、 代碼塊等
  • 其中的方法能夠是抽象方法, 也能夠是已實現方法
  • 含有抽象方法的類, 必須被定義成抽象類
  • 這個抽象方法多是自定義、 繼承來的、 或實現自接口的,不然就要所有實現其中的抽象方法
  • 抽象類中, 能夠沒有抽象方法

DEMO

/**
* 爲何要有抽象類
* 一、含有抽象方法的類,必須被定義成抽象類;
* 可是,抽象類未必非要有抽象方法
* 二、若是指望當前類不能被實例化,
* 而是交給子類完成實例化操做,能夠定義抽象類
* (抽象類有構造方法,抽象類不能被實例化(直接建立該類的對象))
* Person p = new Person(); // 錯誤的
*/
public abstract class Person {
	// 只要有花括號,就能夠執行,這樣的方法已經實現過了
	/**當一個方法在定義時沒法肯定其實現細節(具體處理什麼、怎麼處理)
	* 若是一個方法不是native 修飾的,
	* 當它沒有方法體時,這個方法是不能被執行的,此時這個方法就是一個抽象的方法,
	* 須要使用抽象關鍵字來修飾(abstract)
	*/
	public abstract void eat(String food);
	/**
	* abstract 修飾方法時,不能與 static 、 final連用
	*/
}
/**
* 子類繼承某個抽象類
* 一、若是子類依然不肯定某個方法的實現細節(不實現繼承自父類的抽象方法),
* 則能夠將該類繼續聲明爲抽象類
* 二、若是子類不但願是抽象類,必須實現繼承自父類的抽象方法
*/
public class Sinaean extends Person{
	public Sinaean(){
		super();
	}
	// 這個方法再也不是抽象方法了,並且能夠有方法體
	@Override
	public void eat(String food) {
		System.out.println("中國人大部分都用筷子吃"+ food);
	}
}
public class Thai extends Person{
	@Override
	public void eat(String food) {
		System.out.println("泰國人有時候用手抓着吃: "+ food);
	}
}

/**
* 建立抽象類的實例:
* 一、建立其子類類型的實例(這是本質)
* 二、能夠經過靜態方法來得到其實例(本質仍是建立子類類型對象 )
*/
public class Main {
	public static void main(String[] args) {
		// 不能實例化Person 類型:抽象類不能被實例化
		// Person p = new Person();//Cannot instantiate the type Person
		// 聲明一個Person 類型的變量p(p的編譯時類型是 Person)
		// 建立抽象類的子類類型的對象,並將其堆內存中首地址賦值給棧空間中的p變量
		Person p =new Sinaean();
		p.eat("火鍋");
		System.out.println("運行時類型: " + p.getClass());System.out.println("內存中的首地址是: "+ System.identityHashCode(p));
		// 建立抽象類的子類類型的對象,並將其堆內存中首地址賦值給棧空間中的p變量
		// p 變量中原來存儲的地址將被覆蓋
		p = new Thai();
		p.eat("米飯");
		System.out.println("運行時類型: " + p.getClass());
		System.out.println("內存中的首地址是: "+ System.identityHashCode(p));
		Calendar c = Calendar.getInstance(); // 經過靜態方法來得到一個實例
		Class<?> clazz = c.getClass();// 得到c 所引用的對象的真實類型(運行時類型)
		System.out.println(clazz);
		Class<?> superClass = clazz.getSuperclass();// 得到clazz的父類
		System.out.println(superClass);
	}
}

總結

抽象類的特色
一、抽象類【有構造】,可是不能被實例化
	抽象類的構造方法專供子類調用(構造方法也是能夠執行的)
二、抽象類中能夠有抽象方法,也能夠沒有
	含有抽象方法的類必須是抽象類(參看Person中的第一點)
	抽象類中能夠沒有抽象方法(參看Person中的第二點)
三、怎麼建立抽象類的實例:建立其子類類型的實例(這是本質)
	待建立對象的子類類型必須是非抽象類
	不必定非要是直接子類,間接子類也能夠
	能夠經過靜態方法來得到其實例(Calendar.getInstance() )
	Calendar c = Calendar.getInstance();
四、應該選擇哪一種方式來建立抽象類的實例:
a>、若是當前抽象類中有靜態方法,則優先使用靜態方法
b>、若是子類中有靜態方法返回相應實例,用子類的靜態方法
c>、尋找非抽象的子類,建立子類類型的對象便可
d>、本身繼承這個類並實現其中的抽象方法,而後建立實例
注意:有時爲了實現咱們的需求,可能會不調用靜態方法來得到實例,而是選擇建立子類對象

接口

接口是一種比抽象類更抽象的類型;接口是從多個類似的類中抽象出來的規範: 它定 義了某一批類(接口的實現類或實現類的子類)所要必須遵循的規範。 接口只定義常量 或方法, 而不關注方法的實現細節,接口體現了規範和實現相分離的設計哲學。code

定義接口

[ 修飾符 ] interface InterfaceName {
	定義在接口中的常量 ( 0 到 n 個)
	定義在接口中的抽象方法 ( 0 到 n 個)
	static 修飾的方法 ( 0 到 n 個)
	default 修飾的方法( 0 到 n 個)
}

接口中的成員

  • 能夠包含屬性(只能是常量)
  • 系統會對沒有顯式使用 public static final 修飾的變量追加這些修飾
  • 能夠包含方法(必須是抽象的實例方法)
  • 接口中不容許有非抽象的方法
  • 接口中能夠存在靜態方法( 用 static 修飾的靜態方法)
  • 接口中能夠存在默認方法( 用 default 修飾的方法 )
  • 能夠包含內部類或內部接口
  • 能夠包含枚舉類
  • 不能包含構造方法
  • 不能包含代碼塊

接口的繼承和實現

接口的繼承使用 extends 關鍵字實現:對象

public interface Usb1 extends Usb {};

Java 語言中的接口能夠繼承多個接口: 多個接口之間使用 , 隔開 ( 英文狀態的逗 號 );子接口能夠繼承父接口中的: 抽象方法、常量屬性、內部類、枚舉類繼承

類能夠實現接口:使用 implements 關鍵字來實現接口

接口的特徵

接口中的屬性默認都是 public 、 static 、 final 類型:這些成員必須被顯式初始化;接口中的方法默認都是 public 、 abstract 類型的。內存

接口中根本就沒有構造方法, 也就可能經過構造來實例化,但容許定義接口類型的引用變量,該引用變量引用實現了這個接口的類的實例。get

接口不能實現另外一個接口, 但能夠繼承多個接口。it

接口必須經過實現類來實現它的抽象方法,當某個類實現了某個接口時, 必須實現其中全部的抽象方法,或者是不實現其中的抽象方法, 而把該類定義成抽象類。

類只能繼承一個類, 但能夠實現多個接口,多個接口之間用逗號分開。

接口和抽象類的異同

共同點:
	接口和抽象類都不能被實例化
	接口和抽象類都處於繼承樹的頂端
	接口和抽象類均可以包含抽象方法
	實現接口或繼承抽象類的普通類必須實現其中的抽象方法
區別
	抽象類中能夠有非抽象方法, 接口中只能有抽象方法或static修飾的方法或default修飾的方法
	一個類只能繼承一個直接父類, 而接口能夠實現多繼承
	抽象類可定義靜態屬性和普通屬性, 而接口只能定義靜態屬性
	抽象類有本身的構造, 接口徹底沒有
	抽象類中能夠有代碼塊, 接口中不能夠有

DEMO

/**
* 聲明接口,並肯定接口中能夠有什麼
* 一、常量
* 二、抽象方法
* 三、 default 修飾的非抽象方法(JDK1.8開始)* 四、接口沒有構造方法
*/
public interface Usb {
	// 接口沒有構造方法
	// public Usb(){}
	/**
	* 接口中只能定義常量(沒有不是否是常量的屬性)
	* 一、接口中全部的屬性默認都是 public static final 修飾的
	* 二、常量的命名:全部字母都是大寫,若是有多個單詞,中間用下劃線隔開
	* */
	int POWER_UNIT = 100 ;// 充當供電單位
	/**
	* JDK1.8 以前 僅容許在接口中聲明抽象方法
	* 全部的方法都是 public abstrct 修飾的
	*/
	void power();
	/**
	* JDK1.8 開始,容許定義被default修飾的非抽象方法
	* 這個方法是個public 修飾的非靜態方法(子類或子接口能夠重寫)
	*/
	default void show(){
		System.out.println("每次供電單位是: "+POWER_UNIT) ;
	}
}

/**
* 一、類 能夠實現接口,用關鍵字implements來完成實現
* 二、若是原本不但願是抽象類,則須要實現從接口"繼承"的全部抽象方法
*/
public class MiUsb extends Object implements Usb {
	/**
	* MiUsb中都有什麼
	* 從Object中繼承的全部方法
	* 從Usb中繼承的常量
	* 從Usb中繼承的default的方法(JDK1.8開始)
	* 實現了全部的抽象方法
	*/
	@Override
	public void power() {
		System.out.println("小米Usb充電器,供電單位: "+POWER_UNIT);
	}
	@Override
	public void show() {
		Usb.super.show();
	}
}

public class Test {
	public static void main(String[] args) {
		// 聲明一個接口類型的引用變量Usb u = null;
		// 建立實現類的實例 並將其堆內存首地址賦值給u變量
		u = new MiUsb();
		u.power();
	}
}

一個類實現多個接口 

public interface Transfer {
	void transmission();
}

/**
* 一、用接口繼承接口
* 二、接口能夠繼承父接口中的常量、抽象方法、 default方法
* 三、一個接口能夠繼承多個接口,中間用逗號隔開就行
*/
public interface UsbTypeC extends Usb , Transfer{}

/**
* 一個類能夠實現多個接口,中間用逗號分隔開就能夠
*/
public class OppoUsb implements UsbTypeC,Usb{
	@Override
	public void transmission() {
		System.out.println("Oppo手機");
	}
	@Override
	public void power() {
		System.out.println("Oppo 手機,供電單位"+ POWER_UNIT);
	}
}

public class Test2 {
	public static void main(String[] args) {
		// 聲明一個接口類型的引用變量
		OppoUsb u = null;
		u = new OppoUsb();
		u.power();// 實現了Usb接口中的方法
		u.transmission();// 實現了Transfer接口中的方法
	}
}

內部類

內部類的分類以下:

成員內部類:
	實例內部類
	靜態內部類
局部內部類:
	匿名內部類

DEMO

public class Human {/* 類體括號 */
	public static void main(String[] args){ // main 方法的方法體開始
		int a = 250;
		System.out.println(a);
		class ON{ // 局部內部類(Local Inner Class)
		}
		ON oo = new ON();
		System.out.println(oo);
		class OFF{ // 局部內部類(Local Inner Class)
		}
	}// main 方法的方法體結束static String earth; // 屬性:靜態屬性( 類屬性 )
	
	String name ; // 屬性 :實例屬性(實例變量)
	static class Country{ // 靜態內部類[ static Inner Class]
	}
	class Head{// 實例內部類 (成員內部類) [ Member Inner Class ]
	}
	class Hand{// 實例內部類
	}
}

得到到本身的內部類

/**
* 得到某個類內部的全部的靜態內部類和全部的成員內部類
* 注意:不能得到到局部內部類
*/
public class GetInnerClass {
	public static void main(String[] args) {
		Class<?> c = Human.class;
		// 得到 c 內部的內部類(靜態內部類、成員內部類)
		Class<?>[] classes = c.getDeclaredClasses();// 得到本類內聲明的非 局部內部類
		for (int i = 0; i < classes.length; i++) {
			Class<?> cc = classes[i];
			System.out.println(cc);
		}
	}
}

也能夠經過一個內部類獲取本身聲明在哪一個類內部

public class GetOutterClass {
	public static void main(String[] args) {
		Class<?> c = Human.Country.class;
		// 得到某個內部類聲明在那個外部類中
		Class<?> oc = c.getDeclaringClass(); // 得到聲明本身的 那個類
		System.out.println(oc);
	}
}

建立靜態內部類的實例

public class GetInstance1 {
	public static void main(String[] args) {
		/** 靜態內部類的實例 */
		Human.Country c = new Human.Country();
		System.out.println(c);
		/** 實例內部類的實例 */
		Human h = new Human();// 建立外部類的實例
		Human.Hand hand = h.new Hand();// 之外部類的實例 h 爲基礎,建立內部類的實例
		System.out.println(hand);
		// 或者:
		Human.Head head= new Human().new Head();
		System.out.println(head);
	}
}

匿名內部類

有一個局部內部類,它連名字都沒有,則它就是匿名內部類,可是它有對應的.class文件。

新建一個新的 Class,叫作 TestAnonyous1。隨後建立一個接口,叫作 USB,並提供一個方法(void transfer) 。具體在 TestAnonyous1 中的例子:用匿 名內部類實現接口。

DEMO

/**
* 建立匿名內部類
*/
public class TestAnonymours1 {
	public static void main(String[] args) {
		// 編譯時類型:變量u 聲明的類型是USB
		// 用匿名內部類來實現接口
		USB u = new USB(){
		@Override
		public void transfer() {
			System.out.println("USB正在傳輸");
		}
		};// 把USB當成屍體,結果鬼{}上身了,就能實例化了
		u.transfer();
		System.out.println(System.identityHashCode(u));
		// 得到建立的實例的運行時類型
		Class<?> c = u.getClass();// 任何一個對象均可以經過getClass來得到其 運行時類型
		System.out.println(c);
		Class<?> oc = c.getDeclaringClass();// 嘗試得到聲明本身的那個外部類
		System.out.println(oc);// null 說明 匿名內部類不是直接聲明在類體內部的
		Class<?>[] inters = c.getInterfaces();
		for (int i = 0; i < inters.length; i++) {
			System.out.println(inters[i]);
		}
	}
}
public abstract class AbstractUSB implements USB{
// 從實現的接口中繼承了抽象方法 transfer
}
/**
* 建立匿名內部類
*/
public class TestAnonymours2 {
	public static void main(String[] args) {
	// 建立一個抽象類的實例(本質必定是建立其子類類型的實例)
	// 用匿名內部類來繼承抽象類,並實現其中的抽象方法
	AbstractUSB au = new AbstractUSB() {
		@Overridepublic void transfer() {
			System.out.println("AbstractUSB正在傳輸");
		}
	};
	au.transfer();
	Class<?> c = au.getClass();// 得到au 對象的運行時類型
	System.out.println("匿名內部類: "+ c.getName());
	Class<?> sc = c.getSuperclass();
	System.out.println("匿名內部類的父類: " + sc.getName() );
	}
}
/**
* 建立匿名內部類
*/
public class TestAnonymours3 {
	public static void main(String[] args) {
	// 用匿名內部類繼承一個普通的類
	// 並重寫其中的方法
	Object o = new Object(){
		@Override
		public String toString(){
		return "我是鬼。。 ";
	}};
	System.out.println(o);
	System.out.println(o.getClass());
	System.out.println(o.getClass().getSuperclass());
	}
}

總結

一、內部類
	嵌套在另外一個類內部的類
二、內部類的分類
	直接寫在類體括號內的:
	靜態內部類、非靜態內部類(實例內部類、成員內部類)
	不是直接寫在類體括號內,好比寫在方法中、寫在代碼塊中:局部內部類
	若是某個局部內部類連名字都沒有,那它就是匿名內部類
三、問題:
	對於 Human.java 來講有一個與它對應的 Human.class 文件,內部類是否有對應的 .class 文件?
	有.class 文件,對於靜態內部類、實例內部類來講,他們對應的 .class 的名稱是:
		外部類類名$內部類類名.class好比 Human 類中的 Country 類對應的 字節碼文件的名稱是:Human$Country.class
	對於局部內部類(有名稱的)來講:他們對應的 .class 文件名稱是:外部類類名$Number 內部類類名.class
		其中的 Number 是 使用 該名稱 的 內部類 在 外部類 出現的位置(第幾個)
		
四、一個類可否獲取到本身的內部類(靜態內部類、成員內部類)
	GetInnerClass.java
	
五、一個內部類可否獲取本身聲明在哪一個類內部: GetOutterClass.java

六、建立內部類的實例
	a>、局部內部類的實例,只能在當前的代碼塊內部使用,
	好比 Human 類內部的 main 方法的 ON 類,則這個類只能在 main 方法內部使用
		class ON{ // 局部內部類(Local Inner Class)
		}
		ON oo = new ON();// 建立局部內部類的實例
		System.out.println(oo);
	b>、 建立靜態內部類的實例:
		// 外部類.靜態內部類 變量名 = new 外部類.靜態內部類()Human.Country c = new Human.Country();
	c>、建立實例內部類的實例:
		Human h = new Human();// 建立外部類的實例
		外部類.實例內部類 變量名 = 外部類實例.new 實例內部類();
		Human.Hand hand = h .new Hand();
七、匿名內部類
	有一個局部內部類,它連名字都沒有,則它就是匿名內部類,可是它有對應的.class 文件
	匿名內部類對應的.class 文件名 是外部類類名$數字.class
	a>、用匿名內部類實現接口: TestAnonymous1.java
	b>、用匿名內部類繼承抽象類: TestAnonymous2.java
	c>、用匿名內部類繼承普通的類: TestAnonymous3.java
相關文章
相關標籤/搜索