欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入講解Java中的多態(tài)和抽象類

 更新時間:2023年08月07日 10:48:35   作者:兩個貓崽子和你  
這篇文章主要介紹了深入講解Java中的多態(tài)和抽象類,有時候,設計一個數(shù)組或方法的參數(shù),返回值類型時,無法確定具體的類型,只能確定是某個系列的類型,這時就引入了多態(tài),需要的朋友可以參考下

1、多態(tài)

  • 同一行為,通過不同事物,可以體現(xiàn)不同的形態(tài)。Java是強類型靜態(tài)語言。
    • 強類型:每個變量在聲明之前必須聲明它確切的類型
    • 靜態(tài):賦值和運算時都是嚴格按照聲明時的數(shù)據(jù)類型來處理的
  • 有時候,設計一個數(shù)組或方法的參數(shù),返回值類型時,無法確定具體的類型,只能確定是某個系列的類型,這時就引入了多態(tài)

1.1 多態(tài)的實現(xiàn)方式

父類的引用,創(chuàng)建子類對象。必須有繼承,父類定義方法,子類重寫方法。

  • 方法的形參用父類類型,傳入的實參用子類類型
  • 使用父類類型聲明對象,創(chuàng)建的是子類對象
class Pet{
    public String name="pet";
    public void speak(){
    }
}
class Dog extends Pet{
    public String name="dog";
    @Override
    public void speak(){
        System.out.println("汪汪汪");
    }
    public void work(){
    }
}
class Cat extends Pet{
    public String name="cat";
    @Override
    public void speak(){
        System.out.println("喵喵喵");
    }
}
class Master{
    public void speak(Pet pet){//形參是父類類型對象
        pet.speak();
    }
}

1.2 多態(tài)狀態(tài)下需要注意的問題

Pet pet = new Dog();//使用父類類型聲明對象,創(chuàng)建的是子類對象
//等號左邊是編譯時類型,等號右邊是運行時類型
pet.play();//編譯和運行都正常
//pet.work();//報錯
/*
	報錯原因
	1.調用時按照編譯時方法調用,運行方法時按照運行時類型運行
    2.能調用什么類型看等號左邊有定義什么方法,子類獨有的方法不能被調用
    3.調用了重寫方法時,調用的是父類的方法,執(zhí)行時入棧的是子類重寫過的方法
*/
Dog dog = (Dog)pet;
dog.work();//正確,先進行強轉,然后調用子類獨有的方法
//訪問屬性時,默認訪問的是引用類型的屬性
System.out.println(pet.name);//pet
System.out.println(pet1.name);//pet
@Test
public void test(){
    Master master = new Master();
    Pet pet = new Cat();//使用父類類型聲明對象,創(chuàng)建的是子類對象
    //等號左邊是編譯時類型,等號右邊是運行時類型
    //調用時按照編譯時方法調用,運行方法時按照運行時類型運行
    //能調用什么類型看等號左邊有定義什么方法,子類獨有的方法不能被調用
    //調用了重寫方法時,調用的是父類的方法,執(zhí)行時入棧的是子類重寫過的方法
    Pet pet1 = new Dog();
    master.speak(new Cat());//實參是子類類型對象
    master.speak(new Dog());
    //訪問屬性時,默認訪問的是引用類型的屬性
    System.out.println(pet.name);//pet
    System.out.println(pet1.name);//pet
}

1.3 多態(tài)狀態(tài)下的轉型

@Test
public void test(){
    //實際在堆中創(chuàng)建的對象類型是運行時類型,也就是Dog類對象
    Pet pet = new Dog();//將子類對象的引用類型從子類類型自動提升成父類類型,稱為向上轉型
    Cat cat = (Cat)pet;//編譯不報錯,運行時報錯ClassCastException
    Dog dog = (Dog)pet;//強制類型轉換,將父類引用類型轉成子類引用類型,向下轉型
}
  • 向上轉型:自動轉換,將子類對象的引用類型從子類類型自動提升成父類類型
  • 向下轉型:強制轉換,將父類引用類型轉成子類引用類型
    • 向下轉型要注意的問題:需要使用instanceof關鍵字判斷引用對象是否為某一類的對象
@Test
public void test(){
    //實際在堆中創(chuàng)建的對象類型是運行時類型,也就是Dog類對象
    Pet pet = new Dog();//將子類對象的引用類型從子類類型自動提升成父類類型,稱為向上轉型
    Cat cat = (Cat)pet;//編譯不報錯,運行時報錯ClassCastException
    Dog dog = (Dog)pet;//強制類型轉換,將父類引用類型轉成子類引用類型,向下轉型
    System.out.println(pet instanceof Dog);//true
    System.out.println(pet instanceof Cat);//false
    System.out.println(pet instanceof Pet);//true
}

1.4 多態(tài)的應用

  • 多態(tài)參數(shù):方法的形參用父類類型,傳入的實參用子類類型
  • 多態(tài)數(shù)組:同一父類的不同子類對象可以組成一個數(shù)組
@Test
public void test(){
    Pet[] pets = new Pet[4];
    pets[0] = new Cat();
    pets[1] = new Dog();
    pets[2] = new Dog();
    pets[3] = new Cat();
    for(int i=0;i<pets.length;i++){
        pets[i].play();
    }
}

1.5 虛方法和非虛方法

只有虛方法才能實現(xiàn)多態(tài),使用的比較多的虛方法

1.5.1 非虛方法

方法在編譯期,就確認了具體的調用版本,在運行時不可變,這種方法就稱為非虛方法

  • 靜態(tài)方法:與類型直接關聯(lián)
  • 私有方法:在外部不可訪問
  • final修飾的方法:不能被繼承
  • 實例構造器(構造方法),通過super調用的父類方法

 1.5.2 虛方法

  • 靜態(tài)分派:使用父類的引用調用方法
  • 動態(tài)綁定:根據(jù)具體的運行時類型決定運行哪個方法
    • 方法的參數(shù)在編譯期間確定,根據(jù)編譯器時類型,找最匹配的
    • 方法的所有者如果沒有重寫,就按照編譯時類型處理;如果有重寫,就按照運行時類型處理

1.5.3重寫和重載中的方法調用

  • 重寫示例1
   /*
   	1、編譯期間進行靜態(tài)分派:即確定是調用Animal類中的public void eat()方法,如果Animal類或它的父類中沒有這個方法,將會報錯。
   	2、運行期間進行動態(tài)綁定:即確定執(zhí)行的是Cat類中的public void eat()方法,因為子類重寫了eat()方法,如果沒有重寫,那么還是執(zhí)行Animal類在的eat()方法
   */
   abstract class Animal {  
       public abstract void eat();  
   }  
   class Cat extends Animal {  
       public void eat() {  
           System.out.println("吃魚");  
       }  
   }  
   class Dog extends Animal {  
       public void eat() {  
           System.out.println("吃骨頭");  
       }  
   }
   public class Test{
       public static void main(String[] args){
           Animal a = new Cat();
           a.eat();
       }
   }
  • 重載示例1
   class Father{
   }
   class Son extends Father{
   }
   class Daughter extends Father{
   }
   class MyClassOne{
   	public void method(Father f) {
   		System.out.println("father");
   	}
   	public void method(Son s) {
   		System.out.println("son");
   	}
       public void method(Daughter d) {
   		System.out.println("daughter");
   	}
   }
   class MyClassTwo{
   	public void method(Father f) {
   		System.out.println("father");
   	}
   	public void method(Son s) {
   		System.out.println("son");
   	}
   }
   public class TestOverload {
       /*
       	1、編譯期間進行靜態(tài)分派:即確定是調用MyClassOne類中的method(Father f)方法。
       	2、運行期間進行動態(tài)綁定:確定執(zhí)行的是MyClassOne類中method(Father f)方法
       	## 因為此時f,s,d編譯時類型都是Father類型,因此method(Father f)是最合適的
       */
       @Test
   	public void test() {
   		MyClassOne my = new MyClassOne();
   		Father f = new Father();
   		Father s = new Son();
   		Father d = new Daughter();
   		my.method(f);//father
   		my.method(s);//father
   		my.method(d);//father
   	}
       /*
       	1、編譯期間進行靜態(tài)分派:即確定是分別調用MyClassTwo類中的method(Father f),method(Son s),method(Father f)方法。
       	2、運行期間進行動態(tài)綁定:即確定執(zhí)行的是MyClass類中的method(Father f),method(Son s),method(Father f)方法
       	## 因為此時f,s,d編譯時類型分別是Father、Son、Daughter,而Daughter只能與Father參數(shù)類型匹配
       */
       @Test
   	public void test() {
   		MyClassTwo my = new MyClassTwo();
   		Father f = new Father();
   		Son s = new Son();
   		Daughter d = new Daughter();
   		my.method(f);//father
   		my.method(s);//Son
   		my.method(d);//father
   	}
   }

重載與重寫示例1

    class MyClass{
    	public void method(Father f) {
    		System.out.println("father");
    	}
    	public void method(Son s) {
    		System.out.println("son");
    	}
    }
    class MySub extends MyClass{
    	public void method(Daughter d) {
    		System.out.println("daughter");
    	}
    }
    class Father{
    }
    class Son extends Father{
    }
    class Daughter extends Father{
    }
    /*
    	1、編譯期間進行靜態(tài)分派:即確定是分別調用MyClass類中的method(Father f),method(Son s),method(Father f)方法。
    	2、運行期間進行動態(tài)綁定:即確定執(zhí)行的是MyClass類中的method(Father f),method(Son s),method(Father f)方法。
    	## my變量在編譯時類型是MyClass類型,那么在MyClass類中,只有method(Father f),method(Son s)方法。,s,d變量編譯時類型分別是Father、Son、Daughter,而Daughter只能與Father參數(shù)類型匹配。而在MySub類中并沒有重寫method(Father f)方法,所以仍然執(zhí)行MyClass類中的method(Father f)方法
    */
    public class TestOverload {
    	public static void main(String[] args) {
    		MyClass my = new MySub();
    		Father f = new Father();
    		Son s = new Son();
    		Daughter d = new Daughter();
    		my.method(f);//father
    		my.method(s);//son
    		my.method(d);//father
    	}
   }

2、抽象類

2.1 定義及特點

使用abstract關鍵字修飾類,定義的就是抽象方法

  • abstract關鍵字可以修飾類和方法
  • abstract關鍵字修飾的方法特點
    • 只能在抽象類中
    • 沒有方法體,不能執(zhí)行
  • 抽象類的特點
    • 抽象類中可以有抽象方法,也可以有普通方法
    • 抽象類不允許創(chuàng)建對象

抽象父類的子類一定要重寫抽象父類的所有抽象方法

2.2 抽象類需要注意的問題

public class TestOne {
    public static void main(String[] args) {
        //Person person = new Person();//報錯,不允許創(chuàng)建對象
        //Person p = new Woman();//報錯,不允許創(chuàng)建對象
        Person student = new Student();//正確
        Woman student1 = new Student();//正確
        Student student3 = new Student();//正確
        //創(chuàng)建的對象可以調用抽象類的普通方法
        //當繼承體系中抽象類存在相同方法簽名的重名方法時,采用就近原則
        //不管創(chuàng)建的是哪個子類的對象,都是從現(xiàn)在當前類尋找方法進行調用,然后尋找本類的子父類,然后尋找本類的父類的父類
        student.methodOne();
        student1.methodOne();
        student3.methodOne();
        //訪問成員變量,聲明的引用類型是什么類型,就訪問此類型中的成員變量
        System.out.println(student.country);
        System.out.println(student1.country);
        System.out.println(student3.country);
    }
}
abstract class Person{
    //抽象類可以有自己的屬性
    public String country="person";
    //可以有抽象方法
    abstract void sayHello();
    //可以有普通方法
    public void methodOne(){
        System.out.println("in abstract class Person methodOne");
    }
}
abstract class Woman extends Person{
    public String country="woman";
    abstract void sing();
    public void methodOne(){
        System.out.println("in abstract class Woman methodOne");
    }
}
class Student extends Woman{
    public String country="student";
    @Override
    void sayHello() {
    }
    @Override
    void sing() {
    }
    public void methodOne(){
        System.out.println("in abstract class Student methodOne");
    }
}

3、Object根父類

java.lang.Object是類層次結構的根類,即所有類的父類。

每個類都使用 Object 作為超類。

  • Object類型的變量與除Object以外的任意引用數(shù)據(jù)類型的對象都多態(tài)引用
  • 所有對象(包括數(shù)組)都實現(xiàn)這個類的方法。
  • 如果一個類沒有特別指定父類,那么默認則繼承自Object類

3.1 Object類中的API

API(Application Programming Interface),應用程序編程接口。

Java API是一本程序員的字典 ,是JDK中提供給我們使用的類的說明文檔。

所以我們可以通過查詢API的方式,來學習Java提供的類,并得知如何使用它們。

在API文檔中是無法得知這些類具體是如何實現(xiàn)的,如果要查看具體實現(xiàn)代碼,那么我們需要查看src源碼

根據(jù)JDK源代碼及Object類的API文檔,Object類當中包含的方法有11個。今天我們主要學習其中的5個:

3.1.1 toString()

public String toString()

①默認情況下,toString()返回的是“對象的運行時類型 @ 對象的hashCode值的十六進制形式"

②通常是建議重寫,如果在eclipse中,可以用Alt +Shift + S–>Generate toString()

③如果我們直接System.out.println(對象),默認會自動調用這個對象的toString()

因為Java的引用數(shù)據(jù)類型的變量中存儲的實際上時對象的內存地址,但是Java對程序員隱藏內存地址信息,所以不能直接將內存地址顯示出來,所以當你打印對象時,JVM幫你調用了對象的toString()。

例如自定義的Person類:

public class Person {  
    private String name;
    private int age;
    @Override
    public String toString() {
        return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
    }
    // 省略構造器與Getter Setter
}

3.1.2 getClass()

public final Class<?> getClass():獲取對象的運行時類型

因為Java有多態(tài)現(xiàn)象,所以一個引用數(shù)據(jù)類型的變量的編譯時類型與運行時類型可能不一致,因此如果需要查看這個變量實際指向的對象的類型,需要用getClass()方法

public static void main(String[] args) {
		Object obj = new String();
		System.out.println(obj.getClass());//運行時類型
	}

3.1.3 finalize()

protected void finalize():用于最終清理內存的方法

public class TestFinalize {
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			MyData my = new MyData();
		}
		System.gc();//通知垃圾回收器來回收垃圾
		try {
			Thread.sleep(2000);//等待2秒再結束main,為了看效果
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
class MyData{
	@Override
	protected void finalize() throws Throwable {
		System.out.println("輕輕的我走了...");
	}
}

面試題:對finalize()的理解?

  • 當對象被GC確定為要被回收的垃圾,在回收之前由GC幫你調用這個方法,不是由程序員手動調用。
  • 這個方法與C語言的析構函數(shù)不同,C語言的析構函數(shù)被調用,那么對象一定被銷毀,內存被回收,而finalize方法的調用不一定會銷毀當前對象,因為可能在finalize()中出現(xiàn)了讓當前對象“復活”的代碼
  • 每一個對象的finalize方法只會被調用一次。
  • 子類可以選擇重寫,一般用于徹底釋放一些資源對象,而且這些資源對象往往時通過C/C++等代碼申請的資源內存

3.1.4 hashCode()

public int hashCode():返回每個對象的hash值。

hashCode 的常規(guī)協(xié)定:

①如果兩個對象的hash值是不同的,那么這兩個對象一定不相等;

②如果兩個對象的hash值是相同的,那么這兩個對象不一定相等。

主要用于后面當對象存儲到哈希表等容器中時,為了提高存儲和查詢性能用的。

public static void main(String[] args) {
		System.out.println("Aa".hashCode());//2112
		System.out.println("BB".hashCode());//2112
	}

3.1.5 equals()

public boolean equals(Object obj):用于判斷當前對象this與指定對象obj是否“相等”

①默認情況下,equals方法的實現(xiàn)等價于與“==”,比較的是對象的地址值

②我們可以選擇重寫,重寫有些要求:

1.如果重寫equals,那么一定要一起重寫hashCode()方法,因為規(guī)定:

? a:如果兩個對象調用equals返回true,那么要求這兩個對象的hashCode值一定是相等的;

? b:如果兩個對象的hashCode值不同的,那么要求這個兩個對象調用equals方法一定是false;

? c:如果兩個對象的hashCode值相同的,那么這個兩個對象調用equals可能是true,也可能是false

2.如果重寫equals,那么一定要遵循如下幾個原則:

? a:自反性:x.equals(x)返回true

? b:傳遞性:x.equals(y)為true, y.equals(z)為true,然后x.equals(z)也應該為true

? c:一致性:只要參與equals比較的屬性值沒有修改,那么無論何時調用結果應該一致

? d:對稱性:x.equals(y)與y.equals(x)結果應該一樣

? e:非空對象與null的equals一定是false

到此這篇關于深入講解Java中的多態(tài)和抽象類的文章就介紹到這了,更多相關Java多態(tài)和抽象類內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • JavaWeb入門教程之分頁查詢功能的簡單實現(xiàn)

    JavaWeb入門教程之分頁查詢功能的簡單實現(xiàn)

    這篇文章主要介紹了JavaWeb入門教程之分頁查詢功能的簡單實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • AsyncConfigurerSupport自定義異步線程池處理異常

    AsyncConfigurerSupport自定義異步線程池處理異常

    這篇文章主要為大家介紹了AsyncConfigurerSupport自定義異步線程池處理異常詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • Spring?Boot監(jiān)控SQL運行情況的全過程

    Spring?Boot監(jiān)控SQL運行情況的全過程

    這篇文章主要給大家介紹了關于Spring?Boot監(jiān)控SQL運行情況的相關資料,文中通過實例代碼介紹的非常詳細,對大家學習或者使用SpringBoot具有一定的參考學習價值,需要的朋友可以參考下
    2022-02-02
  • MyBatis如何使用(三)

    MyBatis如何使用(三)

    這篇文章主要介紹了MyBatis如何使用(三)的相關資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-07-07
  • Socket+JDBC+IO實現(xiàn)Java文件上傳下載器DEMO詳解

    Socket+JDBC+IO實現(xiàn)Java文件上傳下載器DEMO詳解

    這篇文章主要介紹了Socket+JDBC+IO實現(xiàn)Java文件上傳下載器DEMO詳解,需要的朋友可以參考下
    2017-05-05
  • Java實現(xiàn)字符串的分割(基于String.split()方法)

    Java實現(xiàn)字符串的分割(基于String.split()方法)

    Java中的我們可以利用split把字符串按照指定的分割符進行分割,然后返回字符串數(shù)組,下面這篇文章主要給大家介紹了關于Java實現(xiàn)字符串的分割的相關資料,是基于jDK1.8版本中的String.split()方法,需要的朋友可以參考下
    2022-09-09
  • Java使用HttpUtils實現(xiàn)發(fā)送HTTP請求

    Java使用HttpUtils實現(xiàn)發(fā)送HTTP請求

    這篇文章主要介紹了Java使用HttpUtils實現(xiàn)發(fā)送HTTP請求,HTTP請求,在日常開發(fā)中,還是比較常見的,今天給大家分享HttpUtils如何使用,需要的朋友可以參考下
    2023-05-05
  • java的異常與處理機制分析【附面試題】

    java的異常與處理機制分析【附面試題】

    這篇文章主要介紹了java的異常與處理機制,結合實例形式分析了Java異常與處理機制的概念、原理、相關操作技巧與注意事項,并附帶面試題分析供大家參考,需要的朋友可以參考下
    2019-05-05
  • 淺析java中密文的創(chuàng)建和校驗

    淺析java中密文的創(chuàng)建和校驗

    這篇文章主要為大家詳細介紹了java中密文的創(chuàng)建和校驗的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-04-04
  • Java web實現(xiàn)賬號單一登錄,防止同一賬號重復登錄(踢人效果)

    Java web實現(xiàn)賬號單一登錄,防止同一賬號重復登錄(踢人效果)

    這篇文章主要介紹了Java web實現(xiàn)賬號單一登錄,防止同一賬號重復登錄,有點類似于qq登錄踢人效果,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-10-10

最新評論