新手初學(xué)Java繼承、封裝與多態(tài)
面向?qū)ο蟮娜蠛诵奶匦?/h2>
面向?qū)ο箝_(kāi)發(fā)模式更有利于人們開(kāi)拓思維,在具體的開(kāi)發(fā)過(guò)程中便于程序的劃分,方便程序員分工合作,提高開(kāi)發(fā)效率。面向?qū)ο蟪绦蛟O(shè)計(jì)有以下優(yōu)點(diǎn)。
- 可重用性:代碼重復(fù)使用,減少代碼量,提高開(kāi)發(fā)效率。下面介紹的面向?qū)ο蟮娜蠛诵奶匦裕ɡ^承、封裝和多態(tài))都圍繞這個(gè)核心。
- 可擴(kuò)展性:指新的功能可以很容易地加入到系統(tǒng)中來(lái),便于軟件的修改。
- 可管理性:能夠?qū)⒐δ芘c數(shù)據(jù)結(jié)合,方便管理。
該開(kāi)發(fā)模式之所以使程序設(shè)計(jì)更加完善和強(qiáng)大,主要是因?yàn)槊嫦驅(qū)ο缶哂欣^承、封裝和多態(tài) 3 個(gè)核心特性。
封裝
封裝將類的某些信息隱藏在類內(nèi)部,不允許外部程序直接訪問(wèn),只能通過(guò)該類提供的方法來(lái)實(shí)現(xiàn)對(duì)隱藏信息的操作和訪問(wèn)。例如:一臺(tái)計(jì)算機(jī)內(nèi)部極其復(fù)雜,有主板、CPU、硬盤和內(nèi)存, 而一般用戶不需要了解它的內(nèi)部細(xì)節(jié),不需要知道主板的型號(hào)、CPU 主頻、硬盤和內(nèi)存的大小,于是計(jì)算機(jī)制造商將用機(jī)箱把計(jì)算機(jī)封裝起來(lái),對(duì)外提供了一些接口,如鼠標(biāo)、鍵盤和顯示器等,這樣當(dāng)用戶使用計(jì)算機(jī)就非常方便。
封裝的特點(diǎn):
- 只能通過(guò)規(guī)定的方法訪問(wèn)數(shù)據(jù)。
- 隱藏類的實(shí)例細(xì)節(jié),方便修改和實(shí)現(xiàn)。
實(shí)現(xiàn)封裝的具體步驟如下:
- 修改屬性的可見(jiàn)性來(lái)限制對(duì)屬性的訪問(wèn),一般設(shè)為
private
。 - 為每個(gè)屬性創(chuàng)建一對(duì)賦值(
setter
)方法和取值(getter
)方法,一般設(shè)為 public,用于屬性的讀寫。 - 在賦值和取值方法中,加入屬性控制語(yǔ)句(對(duì)屬性值的合法性進(jìn)行判斷)。
下面以一個(gè)員工類的封裝為例介紹封裝過(guò)程。一個(gè)員工的主要屬性有姓名、年齡、聯(lián)系電話和家庭住址。假設(shè)員工類為 Employee
,示例如下:
public class Employee { private String name; // 姓名 private int age; // 年齡 private String phone; // 聯(lián)系電話 private String address; // 家庭住址 public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { // 對(duì)年齡進(jìn)行限制 if (age < 18 || age > 40) { System.out.println("年齡必須在18到40之間!"); this.age = 20; // 默認(rèn)年齡 } else { this.age = age; } } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
繼承
繼承是面向?qū)ο蟮娜筇卣髦?。繼承和現(xiàn)實(shí)生活中的“繼承”的相似之處是保留一些父輩的特性,從而減少代碼冗余,提高程序運(yùn)行效率。
Java 中的繼承就是在已經(jīng)存在類的基礎(chǔ)上進(jìn)行擴(kuò)展,從而產(chǎn)生新的類。已經(jīng)存在的類稱為父類、基類或超類,而新產(chǎn)生的類稱為子類或派生類。在子類中,不僅包含父類的屬性和方法,還可以增加新的屬性和方法。
創(chuàng)建人類 People,并定義 name、age、sex、sn 屬性,代碼如下:
public class People { public String name; // 姓名 public int age; // 年齡 public String sex; // 性別 public String sn; // 身份證號(hào) public People(String name, int age, String sex, String sn) { this.name = name; this.age = age; this.sex = sex; this.sn = sn; } public String toString() { return "姓名:" + name + "\n年齡:" + age + "\n性別:" + sex + "\n身份證號(hào):" + sn; } }
創(chuàng)建 People 類的子類 Student 類,并定義 stuNo 和 department 屬性,代碼如下:
public class Student extends People { private String stuNo; // 學(xué)號(hào) private String department; // 所學(xué)專業(yè) public Student(String name, int age, String sex, String sn, String stuno, String department) { super(name, age, sex, sn); // 調(diào)用父類中的構(gòu)造方法 this.stuNo = stuno; this.department = department; } public String toString() { return "姓名:" + name + "\n年齡:" + age + "\n性別:" + sex + "\n身份證號(hào):" + sn + "\n學(xué)號(hào):" + stuNo + "\n所學(xué)專業(yè):" + department; } }
由于 Student 類繼承自 People 類,因此,在 Student 類中同樣具有 People 類的屬性和方法,這里重寫了父類中的 toString() 方法。
注意:如果在父類中存在有參的構(gòu)造方法而并沒(méi)有重載無(wú)參的構(gòu)造方法,那么在子類中必須含有有參的構(gòu)造方法,因?yàn)槿绻谧宇愔胁缓袠?gòu)造方法,默認(rèn)會(huì)調(diào)用父類中無(wú)參的構(gòu)造方法,而在父類中并沒(méi)有無(wú)參的構(gòu)造方法,因此會(huì)出錯(cuò)。
單繼承
Java 語(yǔ)言摒棄了 C++ 中難以理解的多繼承特征,即 Java 不支持多繼承,只允許一個(gè)類直接繼承另一個(gè)類,即子類只能有一個(gè)直接父類,extends 關(guān)鍵字后面只能有一個(gè)類名。
很多地方在介紹 Java 的單繼承時(shí),可能會(huì)說(shuō) Java 類只能有一個(gè)父類,嚴(yán)格來(lái)講,這種說(shuō)法是錯(cuò)誤的,應(yīng)該是一個(gè)類只能有一個(gè)直接父類,但是它可以有多個(gè)間接的父類。
繼承的優(yōu)缺點(diǎn)
在面向?qū)ο笳Z(yǔ)言中,繼承是必不可少的、非常優(yōu)秀的語(yǔ)言機(jī)制,它有如下優(yōu)點(diǎn):
- 實(shí)現(xiàn)代碼共享,減少創(chuàng)建類的工作量,使子類可以擁有父類的方法和屬性。
- 提高代碼維護(hù)性和可重用性。
- 提高代碼的可擴(kuò)展性,更好的實(shí)現(xiàn)父類的方法。
自然界的所有事物都是優(yōu)點(diǎn)和缺點(diǎn)并存的,繼承的缺點(diǎn)如下:
- 繼承是侵入性的。只要繼承,就必須擁有父類的屬性和方法。
- 降低代碼靈活性。子類擁有父類的屬性和方法后多了些約束。
- 增強(qiáng)代碼耦合性(開(kāi)發(fā)項(xiàng)目的原則為高內(nèi)聚低耦合)。當(dāng)父類的常量、變量和方法被修改時(shí),需要考慮子類的修改,有可能會(huì)導(dǎo)致大段的代碼需要重構(gòu)。
super關(guān)鍵字
由于子類不能繼承父類的構(gòu)造方法,因此,如果要調(diào)用父類的構(gòu)造方法,可以使用 super 關(guān)鍵字。super 可以用來(lái)訪問(wèn)父類的構(gòu)造方法、普通方法和屬性。
super 關(guān)鍵字的功能:
- 在子類的構(gòu)造方法中顯式的調(diào)用父類構(gòu)造方法
- 訪問(wèn)父類的成員方法和變量。
super調(diào)用父類構(gòu)造方法
super 關(guān)鍵字可以在子類的構(gòu)造方法中顯式地調(diào)用父類的構(gòu)造方法,基本格式如下:
super(parameter-list);
其中,parameter-list 指定了父類構(gòu)造方法中的所有參數(shù)。super( ) 必須是在子類構(gòu)造方法的方法體的第一行。
聲明父類 Person,類中定義兩個(gè)構(gòu)造方法。示例代碼如下:
public class Person { public Person(String name, int age) { } public Person(String name, int age, String sex) { } }
子類 Student 繼承了 Person 類,使用 super 語(yǔ)句來(lái)定義 Student 類的構(gòu)造方法。示例代碼如下:
public class Student extends Person { public Student(String name, int age, String birth) { super(name, age); // 調(diào)用父類中含有2個(gè)參數(shù)的構(gòu)造方法 } public Student(String name, int age, String sex, String birth) { super(name, age, sex); // 調(diào)用父類中含有3個(gè)參數(shù)的構(gòu)造方法 } }
從上述 Student 類構(gòu)造方法代碼可以看出,super 可以用來(lái)直接調(diào)用父類中的構(gòu)造方法,使編寫代碼也更加簡(jiǎn)潔方便。
編譯器會(huì)自動(dòng)在子類構(gòu)造方法的第一句加上super();
來(lái)調(diào)用父類的無(wú)參構(gòu)造方法,必須寫在子類構(gòu)造方法的第一句,也可以省略不寫。通過(guò) super 來(lái)調(diào)用父類其它構(gòu)造方法時(shí),只需要把相應(yīng)的參數(shù)傳過(guò)去。
super訪問(wèn)父類成員
當(dāng)子類的成員變量或方法與父類同名時(shí),可以使用 super 關(guān)鍵字來(lái)訪問(wèn)。如果子類重寫了父類的某一個(gè)方法,即子類和父類有相同的方法定義,但是有不同的方法體,此時(shí),我們可以通過(guò) super 來(lái)調(diào)用父類里面的這個(gè)方法。
使用 super 訪問(wèn)父類中的成員與 this 關(guān)鍵字的使用相似,只不過(guò)它引用的是子類的父類,語(yǔ)法格式如下:
super.member
其中,member 是父類中的屬性或方法。使用 super 訪問(wèn)父類的屬性和方法時(shí)不用位于第一行。
- super調(diào)用成員屬性
當(dāng)父類和子類具有相同的數(shù)據(jù)成員時(shí),JVM 可能會(huì)模糊不清。我們可以使用以下代碼片段更清楚地理解它。
class Person { int age = 12; } class Student extends Person { int age = 18; void display() { System.out.println("學(xué)生年齡:" + super.age); } } class Test { public static void main(String[] args) { Student stu = new Student(); stu.display(); } }
輸出結(jié)果為:
學(xué)生年齡:12
在上面的例子中,父類和子類都有一個(gè)成員變量 age。我們可以使用 super 關(guān)鍵字訪問(wèn) Person 類中的 age 變量。
- super調(diào)用成員方法
當(dāng)父類和子類都具有相同的方法名時(shí),可以使用 super 關(guān)鍵字訪問(wèn)父類的方法。具體如下代碼所示。
class Person { void message() { System.out.println("This is person class"); } } class Student extends Person { void message() { System.out.println("This is student class"); } void display() { message(); super.message(); } } class Test { public static void main(String args[]) { Student s = new Student(); s.display(); } }
輸出結(jié)果為:
This is student class
This is person class
在上面的例子中,可以看到如果只調(diào)用方法 message( ),是當(dāng)前的類 message( ) 被調(diào)用,使用 super 關(guān)鍵字時(shí),是父類的 message( ) 被調(diào)用
super和this的區(qū)別
this 指的是當(dāng)前對(duì)象的引用,super 是當(dāng)前對(duì)象的父對(duì)象的引用。下面先簡(jiǎn)單介紹一下 super 和 this 關(guān)鍵字的用法。
super 關(guān)鍵字的用法:
- super.父類屬性名:調(diào)用父類中的屬性
- super.父類方法名:調(diào)用父類中的方法
- super():調(diào)用父類的無(wú)參構(gòu)造方法
- super(參數(shù)):調(diào)用父類的有參構(gòu)造方法
如果構(gòu)造方法的第一行代碼不是 this() 和 super(),則系統(tǒng)會(huì)默認(rèn)添加 super()。
this 關(guān)鍵字的用法:
- this.屬性名:表示當(dāng)前對(duì)象的屬性
- this.方法名(參數(shù)):表示調(diào)用當(dāng)前對(duì)象的方法
當(dāng)局部變量和成員變量發(fā)生沖突時(shí),使用this.
進(jìn)行區(qū)分。
關(guān)于 Java super 和 this 關(guān)鍵字的異同,可簡(jiǎn)單總結(jié)為以下幾條。
- 子類和父類中變量或方法名稱相同時(shí),用 super 關(guān)鍵字來(lái)訪問(wèn)??梢岳斫鉃?super 是指向自己父類對(duì)象的一個(gè)指針。在子類中調(diào)用父類的構(gòu)造方法。
- this 是自身的一個(gè)對(duì)象,代表對(duì)象本身,可以理解為 this 是指向?qū)ο蟊旧淼囊粋€(gè)指針。在同一個(gè)類中調(diào)用其它方法。
- this 和 super 不能同時(shí)出現(xiàn)在一個(gè)構(gòu)造方法里面,因?yàn)?this 必然會(huì)調(diào)用其它的構(gòu)造方法,其它的構(gòu)造方法中肯定會(huì)有 super 語(yǔ)句的存在,所以在同一個(gè)構(gòu)造方法里面有相同的語(yǔ)句,就失去了語(yǔ)句的意義,編譯器也不會(huì)通過(guò)。
- this( ) 和 super( ) 都指的是對(duì)象,所以,均不可以在 static 環(huán)境中使用,包括 static 變量、static 方法和 static 語(yǔ)句塊。
- 從本質(zhì)上講,this 是一個(gè)指向?qū)ο蟊旧淼闹羔? 然而 super 是一個(gè) Java 關(guān)鍵字。
多態(tài)
多態(tài)性是面向?qū)ο缶幊痰挠忠粋€(gè)重要特征,它是指在父類中定義的屬性和方法被子類繼承之后,可以具有不同的數(shù)據(jù)類型或表現(xiàn)出不同的行為,這使得同一個(gè)屬性或方法在父類及其各個(gè)子類中具有不同的含義。
對(duì)面向?qū)ο髞?lái)說(shuō),多態(tài)分為編譯時(shí)多態(tài)和運(yùn)行時(shí)多態(tài)。其中編譯時(shí)多態(tài)是靜態(tài)的,主要是指方法的重載,它是根據(jù)參數(shù)列表的不同來(lái)區(qū)分不同的方法。通過(guò)編譯之后會(huì)變成兩個(gè)不同的方法,在運(yùn)行時(shí)談不上多態(tài)。而運(yùn)行時(shí)多態(tài)是動(dòng)態(tài)的,它是通過(guò)動(dòng)態(tài)綁定來(lái)實(shí)現(xiàn)的,也就是大家通常所說(shuō)的多態(tài)性。
Java 實(shí)現(xiàn)多態(tài)有 3 個(gè)必要條件:繼承、重寫和向上轉(zhuǎn)型。只有滿足這 3 個(gè)條件,開(kāi)發(fā)人員才能夠在同一個(gè)繼承結(jié)構(gòu)中使用統(tǒng)一的邏輯實(shí)現(xiàn)代碼處理不同的對(duì)象,從而執(zhí)行不同的行為。
- 繼承:在多態(tài)中必須存在有繼承關(guān)系的子類和父類。
- 重寫:子類對(duì)父類中某些方法進(jìn)行重新定義,在調(diào)用這些方法時(shí)就會(huì)調(diào)用子類的方法。
- 向上轉(zhuǎn)型:在多態(tài)中需要將子類的引用賦給父類對(duì)象,只有這樣該引用才既能可以調(diào)用父類的方法,又能調(diào)用子類的方法。
創(chuàng)建 Figure 類,在該類中首先定義存儲(chǔ)二維對(duì)象的尺寸,然后定義有兩個(gè)參數(shù)的構(gòu)造方法,最后添加 area() 方法,該方法計(jì)算對(duì)象的面積。代碼如下:
public class Figure { double dim1; double dim2; Figure(double d1, double d2) { // 有參的構(gòu)造方法 this.dim1 = d1; this.dim2 = d2; } double area() { // 用于計(jì)算對(duì)象的面積 System.out.println("父類中計(jì)算對(duì)象面積的方法,沒(méi)有實(shí)際意義,需要在子類中重寫。"); return 0; } }
創(chuàng)建繼承自 Figure 類的 Rectangle 子類,該類調(diào)用父類的構(gòu)造方法,并且重寫父類中的 area() 方法。代碼如下:
public class Rectangle extends Figure { Rectangle(double d1, double d2) { super(d1, d2); } double area() { System.out.println("長(zhǎng)方形的面積:"); return super.dim1 * super.dim2; } }
創(chuàng)建繼承自 Figure 類的 Triangle 子類,該類與 Rectangle 相似。代碼如下:
public class Triangle extends Figure { Triangle(double d1, double d2) { super(d1, d2); } double area() { System.out.println("三角形的面積:"); return super.dim1 * super.dim2 / 2; } }
創(chuàng)建 Test 測(cè)試類,在該類的 main() 方法中首先聲明 Figure 類的變量 figure,然后分別為 figure 變量指定不同的對(duì)象,并調(diào)用這些對(duì)象的 area() 方法。代碼如下:
public class Test { public static void main(String[] args) { Figure figure; // 聲明Figure類的變量 figure = new Rectangle(9, 9); System.out.println(figure.area()); System.out.println("==============================="); figure = new Triangle(6, 8); System.out.println(figure.area()); System.out.println("==============================="); figure = new Figure(10, 10); System.out.println(figure.area()); } }
從上述代碼可以發(fā)現(xiàn),無(wú)論 figure 變量的對(duì)象是 Rectangle 還是 Triangle,它們都是 Figure 類的子類,因此可以向上轉(zhuǎn)型為該類,從而實(shí)現(xiàn)多態(tài)。
執(zhí)行上述代碼,輸出結(jié)果如下:
長(zhǎng)方形的面積:
81.0 ===============================
三角形的面積:
24.0 ===============================
父類中計(jì)算對(duì)象面積的方法,沒(méi)有實(shí)際意義,需要在子類中重寫。
0.0
instanceof關(guān)鍵字
嚴(yán)格來(lái)說(shuō) instanceof 是 Java 中的一個(gè)雙目運(yùn)算符,由于它是由字母組成的,所以也是 Java 的保留關(guān)鍵字。在 Java 中可以使用 instanceof 關(guān)鍵字判斷一個(gè)對(duì)象是否為一個(gè)類(或接口、抽象類、父類)的實(shí)例,語(yǔ)法格式如下所示。
boolean result = obj instanceof Class
其中,obj 是一個(gè)對(duì)象,Class 表示一個(gè)類或接口。obj 是 class 類(或接口)的實(shí)例或者子類實(shí)例時(shí),結(jié)果 result 返回 true,否則返回 false。
下面介紹 Java instanceof 關(guān)鍵字的幾種用法。
1)聲明一個(gè) class 類的對(duì)象,判斷 obj 是否為 class 類的實(shí)例對(duì)象(很普遍的一種用法),如以下代碼:
Integer integer = new Integer(1); System.out.println(integer instanceof Integer); // true
2)聲明一個(gè) class 接口實(shí)現(xiàn)類的對(duì)象 obj,判斷 obj 是否為 class 接口實(shí)現(xiàn)類的實(shí)例對(duì)象,如以下代碼:
Java 集合中的 List 接口有個(gè)典型實(shí)現(xiàn)類 ArrayList。
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
所以我們可以用 instanceof 運(yùn)算符判斷 ArrayList 類的對(duì)象是否屬于 List 接口的實(shí)例,如果是返回 true,否則返回 false。
ArrayList arrayList = new ArrayList(); System.out.println(arrayList instanceof List); // true
或者反過(guò)來(lái)也是返回 true
List list = new ArrayList(); System.out.println(list instanceof ArrayList); // true
3)obj 是 class 類的直接或間接子類
我們新建一個(gè)父類 Person.class,代碼如下:
public class Person { }
創(chuàng)建 Person 的子類 Man,代碼如下:
public class Man extends Person { }
測(cè)試代碼如下:
Person p1 = new Person(); Person p2 = new Man(); Man m1 = new Man(); System.out.println(p1 instanceof Man); // false System.out.println(p2 instanceof Man); // true System.out.println(m1 instanceof Man); // true
第 4 行代碼中,Man 是 Person 的子類,Person 不是 Man 的子類,所以返回結(jié)果為 false。
值得注意的是 obj 必須為引用類型,不能是基本類型。例如以下代碼:
int i = 0; System.out.println(i instanceof Integer); // 編譯不通過(guò) System.out.println(i instanceof Object); // 編譯不通過(guò)
所以,instanceof 運(yùn)算符只能用作對(duì)象的判斷。
當(dāng) obj 為 null 時(shí),直接返回 false,因?yàn)?null 沒(méi)有引用任何對(duì)象。
Integer i = 1; System.out.println(i instanceof null); // false
所以,obj 的類型必須是引用類型或空類型,否則會(huì)編譯錯(cuò)誤。
當(dāng) class 為 null 時(shí),會(huì)發(fā)生編譯錯(cuò)誤,錯(cuò)誤信息如下:
Syntax error on token "null", invalid ReferenceType
所以 class 只能是類或者接口。
方法重載
Java 允許同一個(gè)類中定義多個(gè)同名方法,只要它們的形參列表不同即可。如果同一個(gè)類中包含了兩個(gè)或兩個(gè)以上方法名相同的方法,但形參列表不同,這種情況被稱為方法重載(overload)。
例如,在 JDK 的 java.io.PrintStream 中定義了十多個(gè)同名的 println() 方法。
public void println(int i){…} public void println(double d){…} public void println(String s){…}
這些方法完成的功能類似,都是格式化輸出。根據(jù)參數(shù)的不同來(lái)區(qū)分它們,以進(jìn)行不同的格式化處理和輸出。它們之間就構(gòu)成了方法的重載。實(shí)際調(diào)用時(shí),根據(jù)實(shí)參的類型來(lái)決定調(diào)用哪一個(gè)方法。例如:
System.out.println(102); // 調(diào)用println(int i)方法 System.out.println(102.25); // 調(diào)用println(double d)方法 System.out.println("價(jià)格為 102.25"); // 調(diào)用println(String s)方法
方法重載的要求是兩同一不同:同一個(gè)類中方法名相同,參數(shù)列表不同。至于方法的其他部分,如方法返回值類型、修飾符等,與方法重載沒(méi)有任何關(guān)系。
使用方法重載其實(shí)就是避免出現(xiàn)繁多的方法名,有些方法的功能是相似的,如果重新建立一個(gè)方法,重新取個(gè)方法名稱,會(huì)降低程序可讀性。
為什么方法重載不能用方法的返回值類型區(qū)分呢?
對(duì)于int f() { }
和void f() { }
兩個(gè)方法,如果這樣調(diào)用int result = f();,
系統(tǒng)可以識(shí)別是調(diào)用返回值類型為 int 的方法,但 Java 調(diào)用方法時(shí)可以忽略方法返回值,如果采用如下方法來(lái)調(diào)用f();
,你能判斷是調(diào)用哪個(gè)方法嗎?如果你尚且不能判斷,那么 Java 系統(tǒng)也會(huì)糊涂。在編程過(guò)程中有一條重要規(guī)則就是不要讓系統(tǒng)糊涂,系統(tǒng)一糊涂,肯定就是你錯(cuò)了。因此,Java 里不能用方法返回值類型作為區(qū)分方法重載的依據(jù)。
方法重寫
在子類中如果創(chuàng)建了一個(gè)與父類中相同名稱、相同返回值類型、相同參數(shù)列表的方法,只是方法體中的實(shí)現(xiàn)不同,以實(shí)現(xiàn)不同于父類的功能,這種方式被稱為方法重寫(override),又稱為方法覆蓋。當(dāng)父類中的方法無(wú)法滿足子類需求或子類具有特有功能的時(shí)候,需要方法重寫。
子類可以根據(jù)需要,定義特定于自己的行為。既沿襲了父類的功能名稱,又根據(jù)子類的需要重新實(shí)現(xiàn)父類方法,從而進(jìn)行擴(kuò)展增強(qiáng)。
在重寫方法時(shí),需要遵循下面的規(guī)則:
- 參數(shù)列表必須完全與被重寫的方法參數(shù)列表相同。
- 返回的類型必須與被重寫的方法的返回類型相同(Java1.5 版本之前返回值類型必須一樣,之后的 Java 版本放寬了限制,返回值類型必須小于或者等于父類方法的返回值類型)。
- 訪問(wèn)權(quán)限不能比父類中被重寫方法的訪問(wèn)權(quán)限更低(public>protected>default>private)。
- 重寫方法一定不能拋出新的檢査異?;蛘弑缺恢貙懛椒暶鞲訉挿旱臋z査型異常。例如,父類的一個(gè)方法聲明了一個(gè)檢査異常 IOException,在重寫這個(gè)方法時(shí)就不能拋出 Exception,只能拋出 IOException 的子類異常,可以拋出非檢査異常。
另外還要注意以下幾條:
- 重寫的方法可以使用 @Override 注解來(lái)標(biāo)識(shí)。
- 父類的成員方法只能被它的子類重寫。
- 聲明為 final 的方法不能被重寫。
- 聲明為 static 的方法不能被重寫,但是能夠再次聲明。
- 構(gòu)造方法不能被重寫。
- 子類和父類在同一個(gè)包中時(shí),子類可以重寫父類的所有方法,除了聲明為 private 和 final 的方法。
- 子類和父類不在同一個(gè)包中時(shí),子類只能重寫父類的聲明為 public 和 protected 的非 final 方法。
- 如果不能繼承一個(gè)方法,則不能重寫這個(gè)方法。
抽象類
Java 語(yǔ)言提供了兩種類,分別為具體類和抽象類。
在面向?qū)ο蟮母拍钪?,所有的?duì)象都是通過(guò)類來(lái)描繪的,但是反過(guò)來(lái),并不是所有的類都是用來(lái)描繪對(duì)象的,如果一個(gè)類中沒(méi)有包含足夠的信息來(lái)描繪一個(gè)具體的對(duì)象,那么這樣的類稱為抽象類。
在 Java 中抽象類的語(yǔ)法格式如下:
<abstract>class<class_name> { <abstract><type><method_name>(parameter-iist); }
其中,abstract 表示該類或該方法是抽象的;class_name 表示抽象類的名稱;method_name 表示抽象方法名稱,parameter-list 表示方法參數(shù)列表。
如果一個(gè)方法使用 abstract 來(lái)修飾,則說(shuō)明該方法是抽象方法,抽象方法只有聲明沒(méi)有實(shí)現(xiàn)。需要注意的是 abstract 關(guān)鍵字只能用于普通方法,不能用于 static 方法或者構(gòu)造方法中。
抽象方法的 3 個(gè)特征如下:
- 抽象方法沒(méi)有方法體
- 抽象方法必須存在于抽象類中
- 子類重寫父類時(shí),必須重寫父類所有的抽象方法
注意:在使用 abstract 關(guān)鍵字修飾抽象方法時(shí)不能使用 private 修飾,因?yàn)槌橄蠓椒ū仨毐蛔宇愔貙?,而如果使用?private 聲明,則子類是無(wú)法重寫的。
抽象類的定義和使用規(guī)則如下:
- 抽象類和抽象方法都要使用 abstract 關(guān)鍵字聲明。
- 如果一個(gè)方法被聲明為抽象的,那么這個(gè)類也必須聲明為抽象的。而一個(gè)抽象類中,可以有 0~n 個(gè)抽象方法,以及 0~n 個(gè)具體方法。
- 抽象類不能實(shí)例化,也就是不能使用 new 關(guān)鍵字創(chuàng)建對(duì)象。
接口
抽象類是從多個(gè)類中抽象出來(lái)的模板,如果將這種抽象進(jìn)行的更徹底,則可以提煉出一種更加特殊的“抽象類”——接口(Interface)。接口是 Java 中最重要的概念之一,它可以被理解為一種特殊的類,不同的是接口的成員沒(méi)有執(zhí)行體,是由全局常量和公共的抽象方法所組成。
注意:一個(gè)接口可以有多個(gè)直接父接口,但接口只能繼承接口,不能繼承類。
定義接口
接口對(duì)于其聲明、變量和方法都做了許多限制,這些限制作為接口的特征歸納如下:
- 具有 public 訪問(wèn)控制符的接口,允許任何類使用;沒(méi)有指定 public 的接口,其訪問(wèn)將局限于所屬的包。
- 方法的聲明不需要其他修飾符,在接口中聲明的方法,將隱式地聲明為公有的(public)和抽象的(abstract)。
- 在 Java 接口中聲明的變量其實(shí)都是常量,接口中的變量聲明,將隱式地聲明為 public、static 和 final,即常量,所以接口中定義的變量必須初始化。
- 接口沒(méi)有構(gòu)造方法,不能被實(shí)例化。
例如:
public interface A { publicA(){…} // 編譯出錯(cuò),接口不允許定義構(gòu)造方法 }
一個(gè)接口不能夠?qū)崿F(xiàn)另一個(gè)接口,但它可以繼承多個(gè)其他接口。子接口可以對(duì)父接口的方法和常量進(jìn)行重寫。例如:
public interface StudentInterface extends PeopleInterface { // 接口 StudentInterface 繼承 PeopleInterface int age = 25; // 常量age重寫父接口中的age常量 void getInfo(); // 方法getInfo()重寫父接口中的getInfo()方法 }
例如,定義一個(gè)接口 MyInterface,并在該接口中聲明常量和方法,如下:
public interface MyInterface { // 接口myInterface String name; // 不合法,變量name必須初始化 int age = 20; // 合法,等同于 public static final int age = 20; void getInfo(); // 方法聲明,等同于 public abstract void getInfo(); }
實(shí)現(xiàn)接口
接口的主要用途就是被實(shí)現(xiàn)類實(shí)現(xiàn),一個(gè)類可以實(shí)現(xiàn)一個(gè)或多個(gè)接口,繼承使用 extends 關(guān)鍵字,實(shí)現(xiàn)則使用 implements 關(guān)鍵字。因?yàn)橐粋€(gè)類可以實(shí)現(xiàn)多個(gè)接口,這也是 Java 為單繼承靈活性不足所作的補(bǔ)充。類實(shí)現(xiàn)接口的語(yǔ)法格式如下:
<public> class <class_name> [extends superclass_name] [implements interface1_name[, interface2_name…]] { // 主體 }
對(duì)以上語(yǔ)法的說(shuō)明如下:
public
:類的修飾符;superclass_name
:需要繼承的父類名稱;interface1_name
:要實(shí)現(xiàn)的接口名稱。
實(shí)現(xiàn)接口需要注意以下幾點(diǎn):
- 實(shí)現(xiàn)接口與繼承父類相似,一樣可以獲得所實(shí)現(xiàn)接口里定義的常量和方法。如果一個(gè)類需要實(shí)現(xiàn)多個(gè)接口,則多個(gè)接口之間以逗號(hào)分隔。
- 一個(gè)類可以繼承一個(gè)父類,并同時(shí)實(shí)現(xiàn)多個(gè)接口,implements 部分必須放在 extends 部分之后。
- 一個(gè)類實(shí)現(xiàn)了一個(gè)或多個(gè)接口之后,這個(gè)類必須完全實(shí)現(xiàn)這些接口里所定義的全部抽象方法(也就是重寫這些抽象方法);否則,該類將保留從父接口那里繼承到的抽象方法,該類也必須定義成抽象類。
總結(jié)
本篇文章就到這里了,希望可以幫助到你,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Springboot整合Mybatispuls的實(shí)例詳解
這篇文章主要介紹了Springboot整合Mybatispuls的相關(guān)資料,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11SpringBoot + FFmpeg實(shí)現(xiàn)一個(gè)簡(jiǎn)單的M3U8切片轉(zhuǎn)碼系統(tǒng)
使用大名鼎鼎的ffmpeg,把視頻文件切片成m3u8,并且通過(guò)springboot,可以實(shí)現(xiàn)在線的點(diǎn)播。2021-05-05SpringBoot如何進(jìn)行參數(shù)校驗(yàn)實(shí)例詳解
開(kāi)發(fā)過(guò)程中,后臺(tái)的參數(shù)校驗(yàn)是必不可少的,下面這篇文章主要給大家介紹了關(guān)于SpringBoot如何進(jìn)行參數(shù)校驗(yàn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-01-01Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(41)
下面小編就為大家?guī)?lái)一篇Java基礎(chǔ)的幾道練習(xí)題(分享)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧,希望可以幫到你2021-07-07詳解Java數(shù)組擴(kuò)容縮容與拷貝的實(shí)現(xiàn)和原理
這篇文章主要帶大家學(xué)習(xí)數(shù)組的擴(kuò)容、縮容及拷貝,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-05-05Java動(dòng)態(tài)代理四種實(shí)現(xiàn)方式詳解
這篇文章主要介紹了Java四種動(dòng)態(tài)代理實(shí)現(xiàn)方式,對(duì)于開(kāi)始學(xué)習(xí)java動(dòng)態(tài)代理或者要復(fù)習(xí)java動(dòng)態(tài)代理的朋友來(lái)講很有參考價(jià)值,有感興趣的朋友可以參考一下2021-04-04Java實(shí)現(xiàn)常見(jiàn)排序算法的優(yōu)化
今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Java實(shí)現(xiàn)常見(jiàn)排序算法的優(yōu)化展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-01-01詳解Spring Cloud負(fù)載均衡重要組件Ribbon中重要類的用法
本篇文章主要介紹了詳解Spring Cloud負(fù)載均衡重要組件Ribbon中重要類的用法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-03-03