Java繼承後訪問成員的特點
繼承後的特點——成員變數
物件訪問成員變數時,會先在子類中查詢有沒有定義對應的變數,若子類中存在就會就近使用子類中的變數,若子類中沒有定義就會沿著繼承關係往上找有沒有定義相應的變數,若父類中也沒有則編譯不通過。程式碼示例:
class Fu { // Fu類中的成員變數。 int num = 5; int num2 = 7; } class Zi extends Fu { // Zi類中的成員變數 int num = 6; public void show() { // 訪問父類中的num2 System.out.println("num2=" + num2); // 訪問子類中的num System.out.println("num=" + num); } } public class ExtendsDemo0 { public static void main(String[] args) { // 建立子類物件 Zi z = new Zi(); // 呼叫子類中的show方法 z.show(); } } 演示結果: num2=7 num=6
子父類中出現了同名的成員變數時,在子類中訪問父類中非私有成員變數,需要使用 super
關鍵字,用法類似於 this
。
使用格式:
super.父類成員變數名
程式碼如下:
class Zi extends Fu { // Zi類中的成員變數 int num = 6; public void show() { //訪問父類中的num System.out.println("Fu num2=" + num2); //訪問父類中的num System.out.println("Fu num=" + super.num); } } 演示結果: Fu num = 7 Zi num = 5
Fu 類中的私有成員變數,子類是不能直接訪問的。編碼時,遵循封裝的原則,使用private修飾成員變數,可以在父類中提供公共的getXxx方法和setXxx方法來訪問。
super和this
父類空間優先於子類物件產生
在每次建立子類物件時,先初始化父類空間,再建立其子類物件本身。目的在於子類物件中包含了其對應的父類空間,便可以包含其父類的成員。程式碼體現在子類的構造方法呼叫時,一定先呼叫父類的構造方法。理解圖解如下:
super和this的含義
-
super :代表父類的儲存空間標識(可以理解為父類的引用)。
-
this :代表當前物件的引用(誰呼叫就代表誰)。
super和this的用法
this.成員變數 -- 本類的
super.成員變數 -- 父類的
this.成員方法名() -- 本類的
super.成員方法名() -- 父類的
程式碼示例:
class Animal {
public void eat() {
System.out.println("animal : eat");
}
}
class Cat extends Animal {
public void eat() {
System.out.println("cat : eat");
}
public void eatTest() {
this.eat(); // this 呼叫本類的方法
super.eat(); // super 呼叫父類的方法
}
}
public class ExtendsDemo08 {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Cat c = new Cat();
c.eatTest();
}
}
輸出結果為:
animal : eat
cat : eat
animal : eat
注意:
-
通過super引用屬性、方法、構造器時,都要求該成員是可見的,即該成員的修飾符不能是private的,跨包的話還不能是預設的。
-
super的追溯不僅限於直接父類
-
如果某個屬性或方法前面使用“this.”,那麼先從本類中查詢,如果未找到,會沿著繼承關係往上找
如果某個屬性或方法前面使用“super.”,那麼先從直接父類中查詢,如果未找到,會沿著繼承關係往上找
如果某個屬性或方法前面既沒有“this.”,也沒有“super.”,遵循就近原則,先從本類中查詢,如果未找到,會沿著繼承關係往上找
繼承後的特點——成員方法
成員方法不重名時
如果子、父類中的方法不重名,這時的呼叫是沒有影響的。物件呼叫方法時,會先在子類中查詢有沒有定義對應的方法,若子類中存在就執行子類中定義的方法,若子類中沒有定義就會沿著繼承關係往上找有沒有定義相應的方法,若父類中也沒有則編譯不通過。程式碼示例:
class Fu{
public void show(){
System.out.println("Fu類中的show方法執行");
}
}
class Zi extends Fu{
public void show2(){
System.out.println("Zi類中的show2方法執行");
}
}
public class ExtendsDemo04{
public static void main(String[] args) {
Zi z = new Zi();
//子類中沒有定義show方法,但是可以找從父類繼承的show方法去執行
z.show();
z.show2();
}
}
成員方法重名——重寫(Override)
如果子、父類中出現重名的成員方法,這時的訪問是一種特殊情況,叫做方法重寫 (Override)。
- 方法重寫 :子類繼承了父類,可以獲得父類的成員變數和成員方法;可是當父類的某個方法不適合於子類本身的特徵時,在子類中可以根據需要對從父類中繼承來的方法進行改造,也稱為方法的重寫、覆寫。在程式執行時,子類的方法將覆蓋父類的方法。
程式碼如下:
class Fu {
public void show() {
System.out.println("Fu show");
}
}
class Zi extends Fu {
//子類重寫了父類的show方法
public void show() {
System.out.println("Zi show");
}
}
public class ExtendsDemo05{
public static void main(String[] args) {
Zi z = new Zi();
// 子類中有show方法,只執行重寫後的show方法
z.show(); // Zi show
}
}
重寫的應用
子類可以根據需要,定義特定於自己的行為。既沿襲了父類的功能名稱,又根據子類的需要重新實現父類方法,從而進行擴充套件增強。比如新的手機增加來電顯示頭像的功能,程式碼如下:
class Phone {
public void sendMessage(){
System.out.println("發簡訊");
}
public void call(){
System.out.println("打電話");
}
public void showNum(){
System.out.println("來電顯示號碼");
}
}
//智慧手機類
class NewPhone extends Phone {
//重寫父類的來電顯示號碼功能,並增加自己的顯示姓名和圖片功能
public void showNum(){
//呼叫父類已經存在的功能使用super
super.showNum();
//增加自己特有顯示姓名和圖片功能
System.out.println("顯示來電姓名");
System.out.println("顯示頭像");
}
}
public class ExtendsDemo06 {
public static void main(String[] args) {
// 建立子類物件
NewPhone np = new NewPhone();
// 呼叫父類繼承而來的方法
np.call();
// 呼叫子類重寫的方法
np.showNum();
}
}
重寫時,用到super.父類成員方法,表示呼叫父類的成員方法。
方法重寫注意事項
-
方法名:必須完全一致
-
形參列表:必須完全一致
-
返回值型別:
如果是基本資料型別和void,必須一致
如果是引用資料型別,重寫的方法的返回值型別 <= 被重寫方法的返回值型別,Student<Person
-
修飾符:重寫的方法的修飾符範圍 >= 被重寫方法的修飾符範圍(public > protected > 預設 > private)
-
被重寫方法不能被
private
、static
、final
修飾,如果跨包的話,修飾符預設的也不能被重寫,因為預設的跨包不可見
繼承後的特點——構造方法
首先我們要回憶兩個事情,構造方法的定義格式和作用:
- 構造方法的名字是與類名一致的。所以子類是無法繼承父類構造方法的。
- 構造方法的作用是初始化成員變數的。所以子類的初始化過程中,必須先執行父類的初始化動作。
- 子類不會繼承父類的構造器,但是一定會呼叫父類的構造器
- 預設情況下,呼叫的是父類的無參構造,super()寫或不寫都會呼叫父類的無參構造,如果要寫,必須在子類構造器首行
- 如果父類沒有無參構造,必須在子類的構造器首行使用super(實參列表)顯式呼叫父類的有參構造,否則編譯報錯
- super([形參列表]) 和 this([形參列表]) 都必須是在構造方法的第一行,所以不能同時出現。
程式碼示例:
class Father{
public Father(){
System.out.println("父類的無參構造");
}
}
class Son extends Father{
private String str;
private int num;
public Son(){
//隱含呼叫了super(); 子類的構造器中一定會呼叫父類的構造器,預設呼叫父類的無參構造
System.out.println("子類的無參構造");
}
public Son(String str){
//隱含呼叫了super()
this.str = str;
System.out.println("子類的有參構造1");
}
public Son(String str,int num){
//呼叫過載的構造器,隱含呼叫了super()
this(str);
this.num = num;
System.out.println("子類的有參構造2");
}
}
public class ConstructorTest {
public static void main(String[] args) {
Son s1 = new Son();
/*
父類的無參構造
子類的無參構造
*/
//Son s2 = new Son("java");
/*
父類的無參構造
子類的有參構造1
*/
Son s3 = new Son("java", 10);
/*
父類的無參構造
子類的有參構造1
子類的有參構造2
*/
}
}