多型、構造方法(建構函式、構造器)
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()); } }