1. 程式人生 > 其它 >封裝繼承多型的複習

封裝繼承多型的複習

來自韓順平,點選此處開始學習!

super關鍵字

定義:super代表父類引用,用於引用父類的屬性和方法。

note:

  1. 訪問時不能訪問private屬性和方法,super.屬性名;super.方法名(引數);

     2. super用於子類構造器的注意點:必須在構造器第一行,不能與this()共存在同一構造器,如果子類構造器不寫super或this則預設存在一個super方法呼叫父類的無參構造器。
      3. super();只能用於子類的構造器第一行。

對於子類中與父類同名的屬性或方法,使用this訪問子類,用super訪問父類。

若不使用關鍵字使用子類中某一方法或屬性,遵循jvm繼承的記憶體模型規則:即1.先查早本類,有則直接呼叫。2.沒有則查其父類,有則呼叫,若有但是私有方法,則報錯。3.父類沒有則繼續往上查詢,直到object以此類推。

this與super的區別

重寫/覆蓋(override)

定義: 子類有一個方法和其父類的某個方法,名稱,引數列表都相同,但是方法體不同,則稱該子類重寫了父類。

note:

  1. 子類方法的名稱和引數列表要完全相同。

  2. 子類與父類的返回值不需一致,子類的返回值可以是父類返回值的子類(但不能是父類返回值的父類或不相關類,否則編譯報錯),也將構成重寫。如:父類返回 Object類,子類返回String類

  3. 子類中不能縮小父類方法的訪問許可權。public > protected > default > private

練習

overload與override的區別

 public class Person {
     private String name;
     private int age;
 
     public Person() {
 
    }
 
     public Person(String name, int age) {
         this.name = name;
         this.age = age;
    }
 
     public String say() {
         return " 我的名字是:" + this.name + " 我的年齡是:" + this.age;
    }
 }
 
 public class Student extends Person{
     private int id;
     private int score;
 
     public Student(String name, int age, int id, int score) {
         super(name, age);
         this.id = id;
         this.score = score;
    }
 
     public String say(){
         return super.say() +" 我的id是:" + this.id + " 我的分數是:" + this.score;
    }
 }
 
 public class Test {
     public static void main(String[] args) {
         Student kuang = new Student("kuang", 18, 111333, 99);
         Person lxly = new Person("lxly", 26);
         System.out.println(lxly.say());
         System.out.println(kuang.say());
 
    }
 }
 

多型(polymorphic)

簡單來說,多型就是指方法或物件有多種形態,多型是建立在封裝和繼承的基礎上的。

  1. 方法的多型,如過載和重寫。

  2. 物件的多型!(核心)

    1. 一個物件的編譯型別和執行型別可以不一致

    2. 編譯型別在定義物件時,就確定了不能改變

    3. 執行型別可以改變

    4. 編譯型別看定義是 = 左邊,執行型別看 = 右邊

例子:

 public class Animal {
 
     public void shout(){
         System.out.println("Animal shout() wow!");
    }
 }
 
 public class Dog extends Animal {
 
     public void shout() {
         System.out.println("Dog shout wow!");
    }
 }
 
 public class Cat extends Animal {
 
     public void shout() {
         System.out.println("Cat shout wow!");
    }
 }
 
 public class Application {
     public static void main(String[] args) {
         //animal編譯型別是Animal 執行型別是Cat
         Animal animal = new Cat();
         animal.shout();
         System.out.println("=============");
         //animal編譯型別是Animal 執行型別是Dog
         animal = new Dog();
         animal.shout();
    }
 }
 

還有一種用法:在方法的引數列表中寫父類,但傳入子類作為引數,達到多型目的。

向上轉型

定義:父類的引用指向了子類的物件。

多型的前提是繼承關係。

  1. 本質:父類的引用指向子類

  2. 語法:父類型別 引用名 = new 子類型別();

  3. 特點:編譯型別看左邊,執行型別看右邊。

    可以呼叫父類中的成員方法(需遵守訪問許可權),不能呼叫子類的特有成員屬性/方法,最終執行效果看子類的具體實現。(動態繫結

    因為在編譯階段,能呼叫那些成員,是由編譯型別決定的,所以在父類引用指向子類的例子中,引用物件如果直接呼叫子類特有成員,將會報錯無法通過編譯。但是,一旦編譯通過以後,開始執行時,則會將引用物件當作子類,遵循子類繼承的記憶體模型原則。舉個例子,呼叫方法時,如果子類(執行型別)對父類的方法進行了重寫,那麼父類的引用會呼叫子類重寫的方法。

向下轉型

  1. 語法:子類型別 引用名 = (子類型別)父類引用

  2. 只能強轉父類的引用,不能強轉父類的物件

  3. 要求父類的引用必須指向的是當前目標型別的物件(原來A的引用指向B,A才可以向下轉型為B),

     A a = new B();//A的引用指向B
     B b = (B)a;//向下轉型
     //((B)a).show();   show()是B的獨有方法。
  4. 向下轉型後,可以呼叫子類型別中所有的成員

細節

  1. 屬性與重寫無關,屬性值看編譯型別(等號左邊)

  2. instanceof 比較操作符,用於判斷物件型別是否是xx型別或xx型別的子型別。(判斷的是執行型別)

練習

注意:b == s;//true,判斷的是實際指向的物件是否一致。

動態繫結

  1. 當呼叫物件方法時,方法會和該物件的執行型別繫結,先從執行物件的類裡找方法,沒有再去父類找,遵循繼承記憶體模型。

  2. 當某一方法呼叫屬性時,沒有動態繫結,即該方法在哪兒,呼叫的屬性就在哪兒。(哪裡宣告,哪裡使用)

應用

 public class Test {
     public static void main(String[] args) {
         Person[] people = new Person[5];
         people[0] = new Person(30, "jack1");
         people[1] = new Student(15, "luna", 90);
         people[2] = new Student(16, "Smith", 88);
         people[3] = new Teacher(36, "james", 5000);
         people[4] = new Teacher(39, "judy", 7000);
 
         for (int i = 0; i < people.length; i++) {
             System.out.println(people[i].say());
             if (people[i] instanceof Student)
                ((Student) people[i]).study();
             if (people[i] instanceof Teacher)
                ((Teacher) people[i]).teach();
        }
 
    }
 }
 



 public class Test {
     public void showEmpAnnual(Employee e){
             System.out.println(e.getAnnual());//動態繫結
    }
 
     public void testWork(Employee e){//向上轉型
         if (e instanceof Manager)
            ((Manager) e).manage();//向下轉型
         else if (e instanceof UsualEmp)
            ((UsualEmp) e).work();
    }
 
     public static void main(String[] args) {
         Manager manager = new Manager("li",8955,5000);
         UsualEmp usualEmp = new UsualEmp("jack",7000);
         Test test = new Test();
         test.testWork(manager);//向上轉型
         test.testWork(usualEmp);
         test.showEmpAnnual(manager);
         test.showEmpAnnual(usualEmp);
 
 
    }
 }

Object類詳解

==和equals的對比

== 是一個比較運算子

  1. 既可以判斷基本型別,又可以判斷引用型別

  2. 如果判斷基本型別,則判斷的是值是否相等

  3. 如果判斷的是引用型別,則判斷的是地址是否相等,即是否指向同一物件

equals方法是Object的方法

  1. 只能判斷引用型別

  2. 預設的判斷是地址是否相同。

    但JDK原始碼中有些類已經重寫了equals方法,如String Integer,它們的equals比較的是值是否相等。

 @Override
 public boolean equals(Object obj) {
     System.out.println("重寫的");
     if (this == obj){
         return true;
    }
     if (obj instanceof Person){
         Person p = (Person)obj;
         return this.name.equals(p.name) && this.age==p.age && this.gender == p.gender;
 
    }
     return false;
 }

hashCode()方法 初見

  1. 作用是提供具有雜湊結構容器的效率

  2. 兩個引用,如果指向的額是同一個物件,則雜湊值肯定是一樣的

  3. 兩個引用,如果指向的是不同的物件,則雜湊值不一樣。

  4. 雜湊值主要是根據地址號來的,但不能完全將雜湊值等同於地址。

toString

  1. 預設返回:全類名 + @ + 雜湊值的十六進位制 (即Object的toString方法)

  2. 重寫toString方法,列印物件或拼接物件時,都會自動呼叫該物件的toString

  3. 當直接輸出一個物件時,toString方法會被預設呼叫。

finalize (不常用)

  1. 當物件被回收時,系統自動呼叫該物件的finalize方法,子類也可以重寫該方法。

  2. 回收時機:當某個物件沒有任何引用時,系統會決定什麼時候(有一套自己的演算法)使用垃圾回收機制來銷燬物件,程式設計師也可以通過System.gc()主動呼叫

 public class Finalize {
     public static void main(String[] args) {
         Car bmw = new Car("325li");
         bmw = null;
         //這時 car物件就是一個垃圾 垃圾回收器就會回收(銷燬)物件(不是立即回收)
         //程式設計師就可以在finalize中,寫自己的業務邏輯(如釋放資源:資料庫連線,或開啟檔案
         //如果程式設計師不重寫,那麼就會預設呼叫Object的finalize
         System.gc();//主動呼叫垃圾回收器
         System.out.println("程式退出");
    }
 }
 
 class  Car{
     private String name;
 
     public Car(String name) {
         this.name = name;
    }
 
     @Override
     protected void finalize() throws Throwable {
         System.out.println("我們銷燬"+ this.name);
         System.out.println("釋放了資源....");
    }
 }

斷點除錯

  1. 斷點除錯用於檢視原始碼執行過程,從而發現錯誤所在。

  2. 在斷點除錯中,是以物件的執行型別來執行的。

  1. 可以在除錯的過程中動態加入斷電,快捷鍵f9可以直接跳入下一個斷點。

  2. 進入方法可以進入jdk原始碼中的方法。

  3.