一、抽象類
1、抽象類含義的概括:
當多個類出現相同功能時,但功能主體不同,這樣可以向上抽取,抽取時只抽取功能定義,而不抽取功能主體。也就是說,我們在從下往上看繼承這個體系結構時,位于上層的類要比下層更具有通用性,也就是說更加抽象,即最上層的祖先(即超類)最具通用性。這時只講上層的類作為遺傳(或者說派生)下層類的基本的類,而不考慮特定的實例對象。
2、抽象類的特點:
1、抽象方法一定在抽象類中,就是說,一個類中含有抽象方法,這個類也必須是抽象的。
2、抽象方法和抽象類必須被abstract修飾,這是作為抽象(類或方法)的一個標志。
3、抽象類不可用new創建對象,因為調用抽象方法沒有意義(抽象方法為類中的成員)。
4、抽象類中的方法要被使用,必須由子類復寫所有的抽象方法后,建立子類對象調用,也就是說,如果子類中只覆蓋了部分抽象方法,那么這個子類仍為抽象的,是個抽象類。
總結來說,抽象類,是提供功能的,具體實現形式還是需要由子類來實現的,這就強迫了子類復寫抽象類中的抽象方法。需要注意的是,抽象類中是可以有非抽象方法(子類可不必對其復寫)的。
在此,我個人補充一點關于抽象類可以創建數組的東西。
因為數組也是一種特殊的對象,但是像下面這樣就可以用new。
代碼如下:
//抽象類Person
abstract class Person
{
private String name;
public Person(String name)
{
this.name = name;
}
//抽象方法:對人的描述,但對每種人的描述是不清楚的,所以用抽象方法
public abstract String getDescription();
public String getName()
{
return name;
}
}
//Student繼承父類Person
class Student extends Person
{
public Student(String name)
{
super(name);
}
//復寫超類的抽象方法,對學生進行具體描述
public String getDescription()
{
return "I'm a student.";
}
}
////Worker繼承父類Person
class Worker extends Person
{
public Worker(String name)
{
super(name);
}
//復寫超類的抽象方法,對工人進行具體描述
public String getDescription()
{
return "I'm a worker.";
}
}
//測試
class PersonText
{
public static void main(String[] args)
{
Person[] p = new Person[2];//用抽象類創建數組
//再創建兩個對象
p[0] = new Student("Sun");
p[1] = new Worker("Wu");
System.out.println(p[0].getName() + ":" + p[0].getDescription());
System.out.println(p[1].getName() + ":" + p[1].getDescription());
}
}
至于Person[] p = new Person[2];
是如何在內存中進行分配的呢?在這里,Person[] p = new Person[2]
只是創建了兩個數組類型的元素,并不是Person類對象,在這里只是一種引用。而下面的new Student和new Worker才真正創建了兩個對象,分別賦值給數組中的兩個元素,或者說是兩個變量。
3、抽象類與一般類無多大區別:
1、描述事物還依然照常描述,只是抽象類中出現了一些看不懂的東西,即功能定義,這些不確定的部分也為事物的功能,需要明確出來,但無法定義主體。通過抽象方法來表示。
2、抽象類比一般類多了抽象函數,類中可以定義抽象方法,但是不必創建主體內容。
3、抽象類不可以實例化,因為抽象方法沒意義,無法創建對象
4、特殊之處:抽象類中可以不定義抽象方法,抽象類僅僅是為了不讓這個類建立對象。
舉例:
/*
假如在開發一個系統時需要對員工進行建模,員工有3個屬性:
姓名、工號以及工資,經理也是員工,除了含有員工的屬性外。
還有一個獎金屬性,請使用繼承的思想設計出員工類和經理類,
要求類中提供必要的方法進行屬性訪問。
分析:
員工類:name、id、salary
經理類:繼承了員工類,并與自己的bonus。
*/
abstract class Employee
{
//本應將變量定義為private,為了測試不加private
String name;
double salary;
public Employee(String name,double salary)
{
this.name = name;
this.salary = salary;
}
//抽象方法,不可定義功能主體
public abstract void work();
}
//普通員工繼承抽象類雇員
class Pro extends Employee
{
public Pro(String name,double salary)
{
super(name,salary);
}
//復寫父類中的抽象方法
public void work()
{
System.out.println("Pro Work");
}
}
//經理繼承抽象類雇員
class Manager extends Employee
{
private double bonus;
public Manager(String name,double salary,double bonus)
{
//調用超類Employee中的構造器
super(name,salary);
this.bonus = bonus;
this.salary += bonus;//經理獲得的最終工資
}
//復寫父類中的抽象方法
public void work()
{
System.out.println("Manager Work");
}
}
class ManagerText
{
public static void main(String [] args)
{
Manager boss = new Manager("Anna",80000,5000);
Pro staff = new Pro("Ben",50000);
boss.work();
System.out.println("name = " + boss.name + ",salary = " + boss.salary);
staff.work();
System.out.println("name = " + staff.name + ",salary = " + staff.salary);
}
}
二、接口
接口:是一種實現關系
1、接口:
接口可理解為一種特殊的抽象類(但不是),當抽象類中的方法全為抽象的(即不包含任何非抽象方法),可通過接口表示。---- class用來定義類;而interface定義接口
2、定義接口的格式特點:
接口:interface ???實現:implements
1、接口中常見定義:常量、抽象方法
2、接口中成員的固定修飾符:
??常量:public static final
??方法:public abstract
注意:
A、接口中的成員全為public,當然那些修飾符是可以省略的,因為接口會自動設為相應的權限,但是還是最好加上。
B、當接口中的常量賦值后,不可再進行第二次賦值操作。
C、接口不可創建對象,因為其中全為抽象方法,需要被子類實現后,對接口中抽象方法全覆蓋后,子類才可以實現實例化。
interface Inter
{
public static final int NUM = 3;
public abstract void show();
}
class Test implements Inter
{
public void shoe(){}
}
class InterfaceDemo
{
public static void main(String [] args)
{
Test t = new Test();
System.out.println(t.NUM);
System.out.println(Test.NUM);
System.out.println(Inter.NUM);
int NUM = 5;//Error
}
}
3、接口可被類多實現,即將java中的多繼承改良成多實現。
1、多繼承不可以:
是因為父類中的方法有方法體,若多個父類存在相同方法(而方法體不同),子類如果多繼承這些父類的話,那么在運行子類的時候,并不能判斷出要運行這些父類中的哪個方法,因此程序會出現異常。所以多繼承不可以。
2、多實現可以:
是因為接口中的方法是抽象的,并無方法體,無論這些接口中存在多少個同名的方法,由于無方法體,子類只需要覆蓋一次即可,這個方法的具體實現只是通過子類這一個方法實現的。
3、接口之間是可以多繼承的:
因為接口中存在的是抽象的方法,接口與接口之間無論是否存在同名函數,這些都是需要子類覆蓋的,這樣就不會出現無法判斷覆蓋哪一個的問題了。
4、接口的特點:
1、接口是對外暴露的規則
2、接口可以用來多實現
3、接口是程序的擴展功能
4、接口與接口之間可有繼承關系
5、接口降低了耦合性
6、類與接口之間是實現關系,且類可以繼承一個類的同時實現多個接口
接口關系:like-a;???類關系:has-a
需要注意的是:兩個或多個接口中不可有不同返回類型的同名抽象函數。
如下列的A和B中的show是不允許的
interface A
{
public abstract int show();
}
interface B
{
public abstract boolean show();
}
interface C extends A,B
{
public abstract void Cx();
}
因為在類實現C的時候,是無法復寫抽象方法show()的,因為不知道要返回哪種類型。
舉例:
//抽象類
abstract class Sporter
{
abstract void play();
}
//三個接口
interface Learn extends Study //學習
{
public void learn();
}
interface Study//研究
{
public void study();
}
interface Smoking
{
public abstract void smoke();
}
//類繼承類,類實現接口,接口繼承接口
class WuDong extends Sporter implements Smoking,Learn
{
//分別復寫每個抽象方法
public void play()
{
System.out.println("Wu Playing");
}
public void Smoking()
{
System.out.println("Wu Smoking");
}
public void learn()
{
System.out.println("Wu Learning");
}
public void study()
{
System.out.println("Wu Studying");
}
}
class IntfaceDemo
{
public static void main(String[] args)
{
//......
}
}
三、抽象類和接口的異同
1、概述:
1、抽象類(abstract class):一般僅用于被子類繼承。
當多個類出現相同功能時,但功能主體不同,這樣可以向上抽取,抽取時只抽取功能定義,而不抽取功能主體。也就是說,我們在從下往上看繼承這個體系結構時,位于上層的類要比下層更具有通用性,也就是說更加抽象,即最上層的祖先(即超類)最具通用性。這時只講上層的類作為遺傳(或者說派生)下層類的基本的類,而不考慮特定的實例對象。
2、接口(interface):用來建立類與類之間關聯的標準
接口可理解為一種特殊的抽象類(但不是),當抽象類中的方法全為抽象的(即不包含任何非抽象方法),通過接口表示。
簡單代碼示例:
//抽象類
abstract class A{
//可用各種修飾符聲明
int x;
public int a;
private int b;
static int c;
final int d;
public static final int e;
//抽象方法必須有
abstract void function();
//還可以有非抽象方法
void fun(){
//.......
}
}
//接口
interface B{
//聲明必須加默認修飾符
public static final int a;
//方法必須是抽象的,不可有非抽象方法
abstract void function();
}
interface X{}
interface Y{}
//繼承關系:抽象類和類間
class C extends A{
void function(){
//......
}
}
//實現關系:類與接口間,可多實現
class D implements B,X,Y{
//可直接訪問B中的a
void function(){
//......
}
}
//多繼承關系:接口與接口間
interface E extends B,X implements Y
{
//......
}
2、區別和聯系
一)區別:
1、與類間關系不同:
抽象類是一種被子類繼承(extends)的關系,
而接口和類是一種實現(implements)關系,
接口和接口是繼承(extends)關系。
注:
a.子類只能繼承一個抽象類。
b.一個類卻可以實現多個接口。
c.接口可以繼承多個接口。
2、定義特點不同:
a.抽象類可以定義變量、非抽象方法以及抽象方法(必須有一個抽象方法)
變量:private、public、final、static等等修飾符
抽象方法:abstract(必須有)、public、static等等修飾符
b.接口可以定義常量、抽象方法
常量:public static final(都是存在的,如果沒有會默認加上),賦值后,不可再次賦值
方法:public abstract
3、權限不同:
a.抽象類可以有私有變量或方法,子類繼承抽象父類必須復寫全部的抽象方法
b.接口是公開(public)的,里面不能有私有變量或方法,因為接口是對外暴露的,是提供給外界使用的;
實現接口必須重寫接口中的全部抽象方法
4、成員不同:
a.抽象類中可以有自己的成員,也可以由非抽象的成員方法。
b.接口中只能有靜態的不能被修改的成員變量(即常量),而且所有成員方法皆為抽象的。
5、變量類型不同:
a.抽象類中的變量默認是friendly型的,即包內的任何類都可以訪問它,而包外的任何類
都不能訪問它(包括包外繼承了此類的子類),其值可以在子類中重新定義,也可重新賦值。
b.接口中定義的變量是默認的public static final,且必須進行初始化即賦初值,并不可改變。
6、設計理念不同:
a.抽象類表示的是:"si-a"的關系
b.接口表示的是:"like-a"的關系
(組合是"has-a"的關系)
二)聯系:
1.其實接口是抽象類的延伸,可以將它看做是純粹的抽象類,就是說接口比抽象類還抽象。
2、抽象類和接口都必須被一個類(子類)復寫里面的全部抽象方法。
3、接口和抽象類都不可創建對象,因為其中含有抽象方法,需要被子類實現后,
對接口中抽象方法全覆蓋后,子類才可以實現實例化。
補充:
Java接口和Java抽象類有太多相似的地方,又有太多特別的地方,究竟在什么地方,才是它們的最佳位置呢?把它們比較一下,你就可以發現了。
1、Java接口和Java抽象類最大的一個區別,就在于Java抽象類可以提供某些方法的部分實現,而Java接口不可以,這大概就是Java抽象類唯一的優點吧,但這個優點非常有用。如果向一個抽象類里加入一個新的具體方法時,那么它所有的子類都一下子都得到了這個新方法,而Java接口做不到這一點,如果向一個Java接口里加入一個新方法,所有實現這個接口的類就無法成功通過編譯了,因為你必須讓每一個類都再實現這個方法才行,這顯然是Java接口的缺點。
2、一個抽象類的實現只能由這個抽象類的子類給出,也就是說,這個實現處在抽象類所定義出的繼承的等級結構中,而由于Java語言的單繼承性,所以抽象類作為類型定義工具的效能大打折扣。在這一點上,Java接口的優勢就出來了,任何一個實現了一個Java接口所規定的方法的類都可以具有這個接口的類型,而一個類可以實現任意多個Java接口,從而這個類就有了多種類型。
3、從第2點不難看出,Java接口是定義混合類型的理想工具,混合類表明一個類不僅僅具有某個主類型的行為,而且具有其他的次要行為。
4、結合1、2點中抽象類和Java接口的各自優勢,具精典的設計模式就出來了:聲明類型的工作仍然由Java接口承擔,但是同時給出一個Java抽象類,且實現了這個接口,而其他同屬于這個抽象類型的具體類可以選擇實現這個Java接口,也可以選擇繼承這個抽象類,也就是說在層次結構中,Java接口在最上面,然后緊跟著抽象類,哈,這下兩個的最大優點都能發揮到極至了。這個模式就是“缺省適配模式”。
在Java語言API中用了這種模式,而且全都遵循一定的命名規范:Abstract +接口名。
Java接口和Java抽象類的存在就是為了用于具體類的實現和繼承的,如果你準備寫一個具體類去繼承另一個具體類的話,那你的設計就有很大問題了。Java抽象類就是為了繼承而存在的,它的抽象方法就是為了強制子類必須去實現的。
使用Java接口和抽象Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。而不要用具體Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。
我想,如果你編的代碼里面連一個接口和抽象類都沒有的話,也許我可以說你根本沒有用到任何設計模式,任何一個設計模式都是和抽象分不開的,而抽象與Java接口和抽象Java類又是分不開的。理解抽象,理解Java接口和抽象Java類,我想就應該是真正開始用面向對象的思想去分析問題,解決問題了吧。
注:
1、設計接口的目的就是為了實現C++中的多重繼承,不過java團隊設計的一樣更有趣的東西
來實現這個功能,那就是內部類(inner class)
2、一般的應用里,最頂級的是接口,然后是抽象類實現接口,最后才到具體類實現。
不是很建議具體類直接實現接口的。還有一種設計模式是面向接口編程,而非面向實現編程。