Java繼承extends詳解
一、繼承問題的引出
繼承性是面向物件的第二大主要特徵。 下面首先編寫兩個程式:Person類、Student類。
Person類:
class Person {
private String name ;
private int age ;
public void setName(String name) {
this.name = name ;
}
public void setAge(int age) {
this.age = age ;
}
public String getName() {
return this.name ;
}
public int getAge(){
return this.age ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
Student類:
class Student {
private String name ;
private int age ;
private String school ;
public void setName(String name) {
this.name = name ;
}
public void setAge(int age) {
this.age = age ;
}
public void setSchool(String school) {
this.school = school ;
}
public String getName() {
return this.name ;
}
public int getAge(){
return this.age ;
}
public String getSchool() {
return this.school ;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
以上兩個程式通過程式碼的比較可以清楚的發現:程式碼之中存在了重複,而按照之前所學,這種重複是不可能消除的。
在所有的程式碼之中,最具有重複意義的就是連結串列類,針對於每一個簡單Java類或者是說其他的任何類,都編寫一個連結串列程式,程式碼量龐大,而且不方便維護。
二、繼承的概念
繼承是面向物件最顯著的一個特性。繼承是從已有的類中派生出新的類,新的類能吸收已有類的資料屬性和行為,並能擴充套件新的能力。
在Java之中,如果要實現繼承的關係,可以使用如下的語法:
class 子類 extends 父類 {}
- 1
子類又被稱為派生類; 父類又被稱為超類(Super Class)。
觀察繼承的基本實現:
package com.wz.extendsdemo;
class Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
class Student extends Person { // Student類繼承了Person類
}
public class TestDemo {
public static void main(String args[]) {
Student stu = new Student(); // 例項化的是子類
stu.setName("張三"); // Person類定義
stu.setAge(20); // Person類定義
System.out.println("姓名:" + stu.getName() + ",年齡:" + stu.getAge());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
執行結果:
姓名:張三,年齡:20
- 1
通過程式碼可以發現,子類(Student)並沒有定義任何的操作,而在主類中所使用的全部操作都是由Person類定義的,這證明:子類即使不擴充父類,也能維持父類的操作。
在子類之中擴充父類的功能:
package com.wz.extendsdemo;
class Person {
private String name;
private int age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
class Student extends Person { // Student類繼承了Person類
private String school; // 子類的屬性
public void setSchool(String school) {
this.school = school;
}
public String getSchool() {
return this.school;
}
}
public class TestDemo {
public static void main(String args[]) {
Student stu = new Student(); // 例項化的是子類
stu.setName("張三"); // Person類定義
stu.setAge(20); // Person類定義
stu.setSchool("清華大學"); // Student類擴充方法
System.out.println("姓名:" + stu.getName() + ",年齡:" + stu.getAge() + ",學校:" + stu.getSchool());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
執行結果:
姓名:張三,年齡:20,學校:清華大學
- 1
以上的程式碼,子類對於父類的功能進行了擴充(擴充了一個屬性和兩個方法)。但是思考一下:子類從外表看是擴充了父類的功能,但是對於以上的程式碼,子類還有一個特點:子類實際上是將父類定義的更加的具體化的一種手段。父類表示的範圍大,而子類表示的範圍小。
三、繼承的限制
雖然繼承可以進行類功能的擴充,但是其在定義的時候也是會存在若干種限制的。
限制一:一個子類只能夠繼承一個父類,存在單繼承侷限。 錯誤的寫法:
class A {}
class B {}
class C extends A,B {} // 一個子類繼承了兩個父類
- 1
- 2
- 3
以上操作稱為多重繼承,實際上以上的做法就是希望一個子類,可以同時繼承多個類的功能,但是以上的語法不支援而已,但是可以換種方式完成同樣的操作。
正確的寫法:
class A {}
class B extends A {}
class C extends B {}
- 1
- 2
- 3
C實際上是屬於(孫)子類,這樣一來就相當於B類繼承了A類的全部方法,而C類又繼承了A和B類的方法,這種操作稱為多層繼承。
結論:Java之中只允許多層繼承,不允許多重繼承,Java存在單繼承侷限。
限制二:在一個子類繼承的時候,實際上會繼承父類之中的所有操作(屬性、方法),但是需要注意的是,對於所有的非私有(no private)操作屬於顯式繼承(可以直接利用物件操作),而所有的私有操作屬於隱式繼承(間接完成)。
package com.wz.extendsdemo;
class A {
private String msg;
public void setMsg(String msg) {
this.msg = msg;
}
public String getMsg() {
return this.msg;
}
}
class B extends A {
public void print() {
//System.out.println(msg); // 錯誤: msg定義為private,不可見
}
}
public class TestDemo {
public static void main(String args[]) {
B b = new B();
b.setMsg("張三");
System.out.println(b.getMsg());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
此時對於A類之中的msg這個私有屬性發現無法直接進行訪問,但是卻發現可以通過setter、getter方法間接的進行操作。
限制三:在繼承關係之中,如果要例項化子類物件,會預設先呼叫父類構造,為父類之中的屬性初始化,之後再呼叫子類構造,為子類之中的屬性初始化,即:預設情況下,子類會找到父類之中的無參構造方法。
package com.wz.extendsdemo;
class A {
public A() { // 父類無參構造
System.out.println("*************************") ;
}
}
class B extends A {
public B() { // 子類構造
System.out.println("#########################");
}
}
public class TestDemo {
public static void main(String args[]) {
B b = new B() ; // 例項化子類物件
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
執行結果:
*************************
#########################
- 1
- 2
這個時候雖然例項化的是子類物件,但是發現它會預設先執行父類構造,呼叫父類構造的方法體執行,而後再例項化子類物件,呼叫子類的構造方法。而這個時候,對於子類的構造而言,就相當於隱含了一個super()的形式:
class B extends A {
public B() { // 子類構造
super(); // 呼叫父類構造
System.out.println("#########################");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
現在預設呼叫的是無參構造,而如果這個時候父類沒有無參構造,則子類必須通過super()呼叫指定引數的構造方法:
package com.wz.extendsdemo;
class A {
public A(String msg) { // 父類構造
System.out.println("*************************");
}
}
class B extends A {
public B() { // 子類構造
super("Hello"); // 呼叫父類構造
System.out.println("#########################");
}
}
public class TestDemo {
public static void main(String args[]) {
B b = new B(); // 例項化子類物件
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
執行結果:
*************************
#########################
- 1
- 2
在任何的情況下,子類都逃不出父類構造的呼叫,很明顯,super呼叫父類構造,這個語法和this()很相似:super呼叫父類構造時,一定要放在構造方法的首行上。