Java繼承和多態(tài)
繼承
定義
繼承就是利用現(xiàn)有類創(chuàng)建新類的過程惋鹅,現(xiàn)有的類稱為 父類(基類)持灰,新類稱為 子類(派生類)。比如現(xiàn)實(shí)中:兒子繼承了父親遺產(chǎn)一樣负饲。面向?qū)ο蟪绦蛟O(shè)計(jì)中的繼承堤魁,就是代碼重用。
Java中所有的類都繼承自 Object 這個(gè)父類返十。
實(shí)現(xiàn)
在Java中實(shí)現(xiàn)繼承需要使用到extends關(guān)鍵字妥泉;
[訪問修飾符] class 派生類名 extends 基類名 {
成員列表
}
如:
class Student extends Person
{
……
}
訪問修飾符 請(qǐng)參看之前章節(jié)的內(nèi)容。
例子
例:
定義抽象類:Person洞坑。
public abstract class Person { // 抽象類
private String name; // 私有變量
public String getName() { // Getter方法
return name;
}
public void setName(String name) { //Setter方法
this.name = name;
}
public Person(String name) { // 構(gòu)造函數(shù)盲链,用于初始化name
super();
this.name = name;
}
public abstract String getDesc(); // 抽象類中的抽象方法。 只有聲明,沒有具體實(shí)現(xiàn)刽沾。
public String toString(){ // toString方法覆蓋了Object類的toString方法
return name + getDesc();
}
}
public class Student extends Person { // 繼承類
private String major; // 新增加的數(shù)據(jù)
public String getMajor() {
return major;
}
public void setMajor(String major) {
this.major = major;
}
public Student(String name,String major) { // 構(gòu)造函數(shù)用于初始化
super(name); // 調(diào)用超類構(gòu)造函數(shù)
this.major = major;
}
@Override
public String getDesc() { // 必須實(shí)現(xiàn)超類中的抽象方法
// TODO Auto-generated method stub
return " : a student, major is " + major;
}
繼承的作用
- 當(dāng)今軟件設(shè)計(jì)的特征
軟件規(guī)模越來越大
軟件設(shè)計(jì)者越來越多
軟件設(shè)計(jì)分工越來越細(xì) - 引入繼承本慕,實(shí)現(xiàn)了代碼重用;
- 引入繼承侧漓,實(shí)現(xiàn)了遞增式的程序設(shè)計(jì)锅尘。
- 繼承是能自動(dòng)傳播代碼和重用代碼的有力工具;
- 繼承能夠在某些比較一般的類的基礎(chǔ)上建造布蔗、建立和擴(kuò)充新類藤违;
- 能減少代碼和數(shù)據(jù)的重復(fù)冗余度,并通過增強(qiáng)一致性來減少模塊間的接口和界面纵揍,從而增強(qiáng)了程序的可維護(hù)性顿乒;
- 能清晰地體現(xiàn)出類與類之間的層次結(jié)構(gòu)關(guān)系。
注意事項(xiàng)
- 繼承是單方向的泽谨,即派生類可以繼承和訪問基類中的成員璧榄,但基類則無法訪問派生類中的成員; 父類不能訪問子類方法
- 在Java中只允許 單一繼承 方式吧雹,即一個(gè)派生類只能繼承于一個(gè)基類犹菱,而不能象C++中派生類繼承于多個(gè)基類的多重繼承方式。
繼承中的構(gòu)造方法
- 父類中的構(gòu)造方法可以被重載吮炕,但不能被子類繼承,即便它是public的访得;
- 父類的構(gòu)造方法負(fù)責(zé)初始化屬于它的成員變量龙亲,而子類的構(gòu)造方法則只需考慮屬于自己的成員變量,不必去關(guān)注父類的情況悍抑。
- 當(dāng)實(shí)例化子類的對(duì)象時(shí)鳄炉,必須先執(zhí)行父類的構(gòu)造方法,然后再執(zhí)行子類的構(gòu)造方法搜骡;
- 如果父類還有更上級(jí)的父類拂盯,就會(huì)先調(diào)用最高父類的構(gòu)造方法,再逐個(gè)依次地將所有繼承關(guān)系的父類構(gòu)造方法全部執(zhí)行记靡;
- 如果父類的構(gòu)造方法執(zhí)行失敗谈竿,那么子類的對(duì)象也將無法實(shí)例化。
如
class ParentClass { //定義父類
public ParentClass() { //構(gòu)造方法
System.out.println("這是父類的構(gòu)造方法摸吠。");
}
}
class ChildClass extends ParentClass { //子類繼承于父類
public ChildClass() { //構(gòu)造方法
System.out.println("這是子類的構(gòu)造方法空凸。");
}
}
public class ConstructorTest { //該類用于容納main方法
public static void main(String[] args) {
ChildClass cc = new ChildClass(); //實(shí)例化子類對(duì)象
}
}
super
如果子類需要調(diào)用父類的方法或者屬性怎么辦?
- 在子類的構(gòu)造方法中寸痢,super關(guān)鍵字可以顯式地調(diào)用父類的構(gòu)造方法呀洲,用于將參數(shù)傳遞給它;需要注意的是: 該語句必須是子類構(gòu)造方法的第一條語句
如:
class Point //定義"點(diǎn)"類
{
protected float mX, mY; //x軸坐標(biāo)和y軸坐標(biāo)
public Point(float x, float y) //構(gòu)造方法
{
mX = x;
mY = y;
}
……
}
class Circle extends Point //定義"圓"類繼承于"點(diǎn)"類
{
protected float mRadius; //半徑
public Circle(float x, float y, float r) //構(gòu)造方法
{
super(x, y); //顯式調(diào)用父類構(gòu)造方法,必須是第一條語句
mRadius = r;
}
……
}
- 如果父類和子類中有同名成員道逗,在子類中默認(rèn)訪問是屬于自己的那一個(gè)成員兵罢;super關(guān)鍵字可以明確地指定要訪問父類中的成員;但前提條件是:父類中的該成員不是private的
多態(tài)
方法覆蓋
定義
- 在類的繼承體系結(jié)構(gòu)中滓窍,如果子類中出現(xiàn)了與父類中有同原型的方法卖词,那么認(rèn)為子類中的方法覆蓋了父類中的方法(也稱為方法重寫);
- 通過子類的實(shí)例調(diào)用被覆蓋的方法時(shí)贰您,將總是調(diào)用子類中的方法坏平,而父類中的方法將被隱藏。
如:
/*如果不但名稱相同锦亦,而且連方法原型也完全相同的話舶替,則構(gòu)成方法覆蓋*/
class ParentClass { //定義父類
public void fun() {
System.out.println("這是父類中的方法。");
}
}
class ChildClass extends ParentClass {//子類繼承于父類
public void fun() { //子類覆蓋父類中的方法
System.out.println("這是子類中的方法杠园。");
}
}
class OverriddenTest { //用于容納main方法
public static void main(String[] args) {
ParentClass parObj = new ParentClass();
parObj.fun(); //父類的實(shí)例調(diào)用此方法
ChildClass chiObj = new ChildClass();
chiObj.fun(); //子類的實(shí)例調(diào)用此方法
}
}
方法覆蓋的注意事項(xiàng)
- 子類中重寫的方法顾瞪,其訪問權(quán)限不能比父類中被重寫方法的訪問權(quán)限更低
- 在子類中重寫方法時(shí)要保持方法的簽名與父類中方法的簽名一致
引用轉(zhuǎn)型
- 基類(父類)的引用可以指向派生類(子類)的對(duì)象。
如:
BaseClass obj = new DerivedClass();
- 如果存在方法覆蓋抛蚁,那么將會(huì)調(diào)用其派生類中的方法
如:
class Shapes { //基本形狀類
public void draw() { //繪圖的方法
System.out.println("繪制了一個(gè)基本形狀陈醒。");
}
}
class Circle extends Shapes { //圓形類繼承于基本形狀類
public void draw() { //覆蓋父類的繪圖方法
System.out.println("繪制了一個(gè)圓形。");
}
}
class Square extends Shapes { //正方形類繼承與基本形狀類
public void draw() { //覆蓋父類的繪圖方法
System.out.println("繪制了一個(gè)正方形瞧甩。");
}
}
public class polymorphismDemo {
public static void main(String[] args) {
Shapes obj = new Shapes(); //父類的引用指向父類的實(shí)例
obj.draw(); //調(diào)用繪圖方法
obj = new Circle(); //父類的引用指向子類的實(shí)例
obj.draw(); //調(diào)用繪圖方法
obj = new Square(); //父類的引用指向子類的實(shí)例
obj.draw(); //調(diào)用繪圖方法
}
}
多態(tài)
在Java中钉跷,使用父類的引用,調(diào)用同一個(gè)方法肚逸,卻可以得到不同的調(diào)用結(jié)果爷辙,這就是多態(tài)。即: 同一函數(shù)朦促,多種形態(tài)膝晾。
實(shí)際上多態(tài)包括 動(dòng)態(tài)多態(tài) 和 靜態(tài)多態(tài)。
靜態(tài)多態(tài)
- 靜態(tài)多態(tài)也稱為編譯時(shí)多態(tài)务冕,即在編譯時(shí)決定調(diào)用哪個(gè)方法血当;
- 靜態(tài)多態(tài)一般是指方法重載;
- 只要構(gòu)成了方法重載禀忆,就可以認(rèn)為形成了靜態(tài)多態(tài)的條件臊旭;
- 靜態(tài)多態(tài)與是否發(fā)生繼承沒有必然聯(lián)系。
動(dòng)態(tài)多態(tài)
動(dòng)態(tài)多態(tài)也稱為運(yùn)行時(shí)多態(tài)箩退,即在運(yùn)行時(shí)才能確定調(diào)用哪個(gè)方法巍扛。
形成動(dòng)態(tài)多態(tài)必須具體以下條件:
- 必須要有繼承的情況存在;
- 在繼承中必須要有方法覆蓋乏德;
- 必須由基類的引用指向派生類的實(shí)例撤奸,并且通過基類的引用調(diào)用被覆蓋的方法吠昭;
由上述條件可以看出,繼承是實(shí)現(xiàn)動(dòng)態(tài)多態(tài)的首要前提胧瓜。
抽象
抽象方法
基類無法(或者沒有必要)提供被覆蓋方法的具體實(shí)現(xiàn)矢棚,那么就可以將這個(gè)方法定義為 抽象方法。
[訪問權(quán)限] abstract 返回值類型 方法名(參數(shù)列表){
}
如:
public abstract void draw(){
}
抽象類
如果某個(gè)類包含抽象方法府喳,那么這個(gè)類就必須定義成 抽象類蒲肋。
[訪問權(quán)限] abstract class 類名{
成員列表
}
如:
public abstract class Shapes{
public abstract void draw();
}
抽象類的注意事項(xiàng)
- 抽象類不可以直接實(shí)例化,只可以用來繼承钝满;
- 抽象類的派生子類應(yīng)該提供對(duì)其所有抽象方法的具體實(shí)現(xiàn)兜粘;
- 如果抽象類的派生子類沒有實(shí)現(xiàn)其中的所有抽象方法,那么該派生子類仍然是抽象類弯蚜,只能用于繼承孔轴,而不能實(shí)例化;
- 抽象類中也可以包含有非抽象的方法碎捺;
- 構(gòu)造方法和靜態(tài)方法不可以修飾為abstract路鹰。
如:
abstract class Shapes { //基本形狀類,抽象類
public abstract void draw(); //繪圖方法收厨,抽象方法
}
class Circle extends Shapes { //圓形類繼承于基本形狀類
public void draw() { //實(shí)現(xiàn)抽象父類的抽象繪圖方法
System.out.println("繪制了一個(gè)圓形晋柱。");
}
}
class Square extends Shapes { //正方形類繼承與基本形狀類
public void draw() { //實(shí)現(xiàn)抽象父類的抽象繪圖方法
System.out.println("繪制了一個(gè)正方形。");
}
}
public class abstractDemo { //該類用于容納main方法
public static void main(String[] args) {
Shapes obj;
obj = new Circle(); //父類的引用指向子類的實(shí)例
obj.draw(); //調(diào)用繪圖方法
obj = new Square(); //父類的引用指向子類的實(shí)例
obj.draw(); //調(diào)用繪圖方法
}
}
接口
接口定義
如果某個(gè)類中所有的方法都是 抽象方法诵叁,那么可以考慮將該類定義為接口雁竞。
[訪問權(quán)限] interface 接口名{
成員列表
}
如:
public interface IMyInterface{
public void doIt();
}
實(shí)現(xiàn)接口
接口只能用于實(shí)現(xiàn),不能實(shí)例化(new)拧额。
[訪問權(quán)限] class 類名 implements 接口名 {
成員列表
}
如:
public class MyClass implements IMyInterface {
public void doIt(){
}
}
接口的注意事項(xiàng)
- 接口中不能定義非抽象方法碑诉,也就是說接口中不能包含有函數(shù)實(shí)體。
接口中的所有方法都默認(rèn)為抽象方法势腮,無需在每個(gè)方法前加abstract關(guān)鍵字。 - 接口的實(shí)現(xiàn)類應(yīng)該提供對(duì)接口中所有抽象方法的具體實(shí)現(xiàn)漫仆,否則將成為抽象類捎拯。
與抽象類和它的繼承類相似,也可以使用接口的引用指向其實(shí)現(xiàn)類的對(duì)象盲厌,從而達(dá)到動(dòng)態(tài)多態(tài)的效果署照。 - Java只支持單繼承,而不能象C++那樣可以多重繼承吗浩,接口正是為了彌補(bǔ)這一點(diǎn)建芙。
- Java中還允許一個(gè)接口繼承于另一個(gè)接口,即由父接口派生出子接口懂扼。
如:
public interface 子接口名 extends 父接口名 {
成員列表
}
final關(guān)鍵字
final修飾變量
如果將某個(gè)變量修飾為final禁荸,那么該變量就成為 常量
如:
final double PI = 3.14159;
☆ 常量在聲明時(shí)必須初始化右蒲。
final修飾方法
如果將某個(gè)成員方法修飾為final,則意味著該方法 不能被子類覆蓋
如:
public final void fun() {
……
}
//如果在派生類中出現(xiàn)同原型的方法赶熟,將會(huì)報(bào)錯(cuò)瑰妄。
final修飾類
如果將某個(gè)類修飾為final,則說明該類 無法被繼承
如:
public final class MyClass {
……
}
//任何類想繼承于MyClass類都將報(bào)錯(cuò)映砖。
類和類之間的關(guān)系
有——>has
汽車有輪子间坐。一般來說這種關(guān)系對(duì)應(yīng)的是 屬性。是——>is
圓形是個(gè)形狀邑退。一般來說這種關(guān)系對(duì)應(yīng)的是 方法竹宋。