1. 程式人生 > >多型、構造方法(建構函式、構造器)

多型、構造方法(建構函式、構造器)

1、多型

(1)多型概述定義及使用格式:

多型是繼封裝、繼承之後,面向物件的特性。

父類引用變數可以指向子類物件。

注意:

多型的前提是必須有子父類關係或者類實現介面關係,否則無法完成多型。

在使用多型後的父類引用變數呼叫方法時,會呼叫子類重寫後的方法。

多型的定義格式:就是父類的引用變數指向子類物件

使用格式:

父類型別  變數名 = new 子類型別();

變數名.方法名();

普通類多型定義的格式

父類 變數名 = new 子類();

如: class Fu {}

class Zi extends Fu {}

類的多型使用

Fu f = new Zi();

l 抽象類多型定義的格式:

抽象類 變數名 = new
抽象類子類(); 如: public abstract class Fu {----------------定義一個抽象父類 public abstract void method();----------抽象方法method } public class Zi extends Fu {   public void method(){----------------重寫方法 System.out.println(“重寫父類抽象方法”); } } //類的多型使用 Fu fu= new Zi();--------------類的多型使用

l 介面多型定義的格式

介面 變數名 = new 介面實現類();
如:public interface Fu {
             public abstract void method();
}
public class Zi implements Fu {
             public void method(){
              System.out.println(“重寫介面抽象方法”);
}
}
//介面的多型使用
Fu fu = new Zi();

l 注意事項

同一個父類的方法會被不同的子類重寫。在呼叫方法時,呼叫的為各個子類重寫後的方法。

Person p1 = new Student();

   Person p2 = new Teacher();

   p1.work(); //p1會呼叫Student類中重寫的work方法

   p2.work(); //p2會呼叫Teacher類中重寫的work方法

當變數名指向不同的子類物件時,由於每個子類重寫父類方法的內容不同,所以會呼叫不同的方法。

(2)多型成員的方法

l 多型成員變數

當子父類中出現同名的成員變數時,多型呼叫該變數時:

編譯時期:參考的是引用型變數所屬的類中是否有被呼叫的成員變數。沒有,編譯失敗。

執行時期:也是呼叫引用型變數所屬的類中的成員變數。

簡單記:編譯和執行都參考等號的左邊。編譯執行看左邊。

Public class Fu {-----------------------父類
    int num = 4;------------------------定義常量
}
Public class Zi extends Fu {---------------繼承
    int num = 5;------------------定義常量,長量名與父類相同
}
Public class Demo {
    public static void main(String[] args)     {
        Fu f = new Zi();
        System.out.println(f.num);
        Zi z = new Zi();
        System.out.println(z.num);
    }
}

l 多型成員方法

編譯時期:參考引用變數所屬的類,如果類中沒有呼叫的方法,編譯失敗。

執行時期:參考引用變數所指的物件所屬的類,並執行物件所屬類中的成員方法。

簡而言之:編譯看左邊,執行看右邊。

Public class Fu {
    int num = 4;
    void show()    {
        System.out.println("Fu show num");
    }
}
Public class Zi extends Fu {
    int num = 5;
   public void show()    {
        System.out.println("Zi show num");
    }
}
Public class Demo {
    public static void main(String[] args)     {
        Fu f = new Zi();
        f.show();
    }
}

 

(3)instanceof關鍵字

通過instanceof關鍵字來判斷某個物件是否屬於某種資料型別。如學生的物件屬於學生類,學生的物件也屬於人類。

使用格式:

boolean  b  = 物件  instanceof  資料型別;

Person p1 = new Student(); // 前提條件,學生類已經繼承了人類

boolean flag = p1 instanceof Student; //flag結果為true

boolean flag2 = p1 instanceof Teacher; //flag結果為false

(4)多型轉型:分為向上轉型與向下轉型兩種:

向上轉型:當有子類物件賦值給一個父類引用時,便是向上轉型,多型本身就是向上轉型的過程。

使用格式:

父類型別  變數名 = new 子類型別();

如:Person p = new Student();

l 向下轉型:一個已經向上轉型的子類物件可以使用強制型別轉換的格式,將父類引用轉為子類引用,這個過程是向下轉型。如果是直接建立父類物件,是無法向下轉型的!

使用格式:

子類型別 變數名 = (子類型別) 父類型別的變數;

:Student stu = (Student) p;  //變數p 實際上指向Student物件

 (5)多型的好處與弊端

當父類的引用指向子類物件時,就發生了向上轉型,即把子類型別物件轉成了父類型別。向上轉型的好處是隱藏了子類型別,提高了程式碼的擴充套件性。

但向上轉型也有弊端,只能使用父類共性的內容,而無法使用子類特有功能,功能有限制。看如下程式碼

 舉例說明:

//描述動物類,並抽取共性eat方法
public abstract class Animal {
    abstract void eat();
}
// 描述狗類,繼承動物類,重寫eat方法,增加lookHome方法
public class Dog extends Animal {
    public void eat() {
        System.out.println("啃骨頭");
    }

    public void lookHome() {
        System.out.println("看家");
    }
}
// 描述貓類,繼承動物類,重寫eat方法,增加catchMouse方法
public class Cat extends Animal {
    public void eat() {
        System.out.println("吃魚");
    }

    public void catchMouse() {
        System.out.println("抓老鼠");
    }
}

測試:

public class Test {
    public static void main(String[] args) {
        Animal a = new Dog(); //多型形式,建立一個狗物件
        a.eat(); // 呼叫物件中的方法,會執行狗類中的eat方法
        // a.lookHome();//使用Dog類特有的方法,需要向下轉型,不能直接使用
        
        // 為了使用狗類的lookHome方法,需要向下轉型
// 向下轉型過程中,可能會發生型別轉換的錯誤,即ClassCastException異常
        // 那麼,在轉之前需要做健壯性判斷 
        if( !a instanceof Dog){ // 判斷當前物件是否是Dog型別
                 System.out.println("型別不匹配,不能轉換"); 
                 return; 
        } 
        Dog d = (Dog) a; //向下轉型
        d.lookHome();//呼叫狗類的lookHome方法
    }
}

l 什麼時候使用向上轉型:

當不需要面對子類型別時,通過提高擴充套件性,或者使用父類的功能就能完成相應的操作,這時就可以使用向上轉型。

如:Animal a = new Dog();

    a.eat();

l 什麼時候使用向下轉型

當要使用子類特有功能時,就需要使用向下轉型。

如:Dog d = (Dog) a; //向下轉型

    d.lookHome();//呼叫狗類的lookHome方法

l 向下轉型的好處:可以使用子類特有功能。

l 弊端是:需要面對具體的子類物件;在向下轉型時容易發生ClassCastException型別轉換異常。在轉換之前必須做型別判斷。

如:if( !a instanceof Dog){…}

 2、構造方法(建構函式、構造器)

(1)構造方法介紹

從字面上理解即為構建創造時用的方法,即就是物件建立時要執行的方法。既然是物件建立時要執行的方法,那麼只要在new物件時,知道其執行的構造方法是什麼,

就可以在執行這個方法的時候給物件進行屬性賦值。

格式:

修飾符 構造方法名(引數列表)

{

}

構造方法的體現:

構造方法沒有返回值型別。也不需要寫返回值。因為它是為構建物件的,物件建立完,方法就執行結束。

 構造方法名稱必須和類名保持一致。

 構造方法沒有具體的返回值。

public class Person {-------------------定義person類
    // Person的成員屬性age和name
    private int age;
    private String name;
}
    // Person的構造方法,擁有引數列表
  public Person(int a, String nm) {------------------構造方法,傳參
        // 接受到建立物件時傳遞進來的值,將值賦給成員屬性
        age = a;
        name = nm;
    }
}

構造方法是專門用來建立物件的,也就是在new物件時要呼叫構造方法。

構造方法舉例:

public class Person {
    // Person的成員屬性age和name
    private int age;
    private String name;

    // Person的構造方法,擁有引數列表
    public Person(int a, String nm) {
        // 接受到建立物件時傳遞進來的值,將值賦給成員屬性
        age = a;
        name = nm;
    }
    public void speak() {//---------------------定義方法
        System.out.println("name=" + name + ",age=" + age);
    }
}
public class PersonDemo {
    public static void main(String[] args) {
        // 建立Person物件,並明確物件的年齡和姓名
        Person p2 = new Person(23, "張三");//---------------new物件時直接賦值
        p2.speak();
    }
}

構造方法的注意事項:

---------------描述事物時,並沒有顯示指定構造方法,當在編譯Java檔案時,編譯器會自動給class檔案中新增預設

的構造方法。如果在描述類時,我們顯示指定了構造方法,那麼,當在編譯Java原始檔時,編譯器就不會再給class檔案

中新增預設構造方法。

-----------------------一個類中可以有多個構造方法,多個構造方法是以過載的形式存在的

-----------------------構造方法是可以被private修飾的,作用:其他程式無法建立該類的物件。

(2)構造方法和一般方法的區別

目前學習了兩種方法:一般方法與構造方法

區別:

-------------------構造方法在物件建立時就執行了,而且只執行一次。

-------------------一般方法是在物件建立後,需要使用時才被物件呼叫,並可以被多次呼叫

 構造方法舉例:

//構造方法
//許可權、類名(引數列表)
public class Person {
    private String name;
    private int age;
    public Person(String name,int age){//有參構造方法
        this.name=name;
        this.age=age;
    }
    public Person(){
        System.out.println("這是空參的構造方法");
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
}
package demo04;

public class Test {
//構造方法在建立物件時被呼叫,並且一個物件只能呼叫一次構造方法
    //構造方法只能對屬性賦值一次
    //2、構造方法,如果該類沒有構造方法,預設有一個空參構造方法
    //如果這個類有構造方法,就不會預設新增一個空參構造
    public static void main(String[] args) {
        
        Person p = new Person();
        Person b= new Person("lisi",19);
        System.out.println(p.getName()+".."+p.getAge());
        System.out.println(b.getName()+".."+b.getAge());
    }

}