国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java語法之 Java 的多態、抽象類和接口

Java語法之 Java 的多態、抽象類和接口

2022-01-11 13:55吞吞吐吐大魔王 Java教程

上節介紹了 Java 基礎語法之解析 Java 的包和繼承,如果這類知識有點疑惑的小伙伴,可以去 Java 的包和繼承 這章看看,或許可以幫你解決一些疑惑喲!今天這篇文章我們將講解的是 Java 的多態、抽象類和接口,感興趣的小伙伴可以

上一篇文章:Java 基礎語法之解析 Java 的包和繼承

今天這章主要介紹多態和抽象類,希望接下來的內容對你有幫助!

 

一、多態

在了解多態之前我們先了解以下以下的知識點

 

1. 向上轉型

什么是向上轉型呢?簡單講就是

把子類對象賦值給了父類對象的引用

這是什么意思呢,我們可以看下列代碼

// 假設 Animal 是父類,Dog 是子類
public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Animal("動物");
        Dog dog=new Dog("二哈");
        animal=dog;
    }
}

其中將子類引用 dog 的對象賦值給了父類的引用,而上述代碼也可以簡化成

public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Dog("二哈");
    }
}

這個其實和上述代碼一樣,這種寫法都叫“向上轉型”,將子類對象的引用賦值給了父類的引用

其實向上轉型以后可能用到的比較多,那么我們什么時候需要用它呢?

  • 直接賦值
  • 方法傳參
  • 方法返回

其中直接賦值就是上述代碼的樣子,接下來讓我們看一下方法傳參的實例

// 假設 Animal 是父類,Dog 是子類
public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Dog("二哈");
        func(animal);
    }
    public static void func1(Animal animal){
        
    }
}

我們寫了一個函數,形參就是父類的引用,而傳遞的實參就是子類引用的對象。也可以寫成

public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Animal("動物");
        Dog dog=new Dog("二哈");
        func(dog);
    }
    public static void func1(Animal animal){
        
    }
}

那么方法返回又是啥樣的呢?其實也很簡單,如

// 假設 Animal 是父類,Dog 是子類
public class TestDemo{
    public static void main(String[] args){
        
    }
    public static Animal func2(){
        Dog dog=new Dog("二哈");
        return dog;
    }
}

其中在 func2 方法中,將子類的對象返回給父類的引用。還有一種也算是方法返回

public class TestDemo{
    public static void main(String[] args){
        Animal animal=func2();
    }
    public static Dog func2(){
        Dog dog=new Dog("二哈");
        return dog;
    }
}

方法的返回值是子類的引用,再將其賦值給父類的對象,這種寫法也叫“向上轉型”。

那么既然我們父類的引用指向了子類引用的對象,那么父類可以使用子類的一些方法嗎?試一試

class Animal{
    public String name;
    public Animal(String name){
        this.name=name;
    }
    public void eat(){
        System.out.println(this.name+"吃東西"+"(Animal)");
    }
}
class Dog extends Animal{
    public Dog(String name){
        super(name);
    }
    public void eatDog(){
        System.out.println(this.name+"吃東西"+"(Dog)");
    }
}
public class TestDemo{
    public static void main(String[] args){
        Animal animal1=new Animal("動物");
        Animal animal2=new Dog("二哈");
        animal1.eat();
        animal2.eatdog();
    }
}

結果是不可以

Java語法之 Java 的多態、抽象類和接口

因為本質上 animal 的引用類型是 Animal,所以只能使用自己類里面的成員和方法

 

2. 動態綁定

那么我們的 animal2 可以使用 Dog 類中的 eatDog 方法嗎?其實是可以的,只要我們將這個 eatDog 改名叫 eat 就行

class Dog extends Animal{
    public Dog(String name){
        super(name);
    }
    public void eat(){
        System.out.println(this.name+"吃東西"+"(Dog)");
    }
}

修改后的部分代碼如上,此時,我們之前的 animal2 直接調用 eat,就可以得到下面的結果

Java語法之 Java 的多態、抽象類和接口

這也就是說明此時

  • animal1.eat() 實際調用的是父類的方法
  • animal2.eat() 實際調用的是子類的方法

那么為什么將 eatDog 改成 eat 之后,animal2.eat 調用的就是子類的方法呢?

這就是我們接下來要講的重寫

 

3. 方法重寫

什么叫做重寫呢?

子類實現父類的同名方法,并且

  • 方法名相同
  • 方法的返回值一般相同
  • 方法的參數列表相同
  •  

滿足上述的情況就稱為:重寫、覆寫、覆蓋(Override)

注意事項:

  • 重寫的方法不能為密封方法(即被 final 修飾的方法)。我們之前了解過關鍵字 final,而被他修飾的方法就叫做密封方法,該方法則不能再被重寫,如
// 假如這是父類中的方法
public final void eat(){
    System.out.println(this.name+"要吃東西");
}

此類方法是不能被重寫的

  • 子類的訪問修飾限定符權限一定要大于等于父類的權限,但是父類不能是被 private修飾
  • 方法不能被 static 修飾
  • 一般針對重寫的方法,可以使用 @Override 注解來顯示指定。加了他有什么好處呢?看下面代碼
// 假如下面的 eat 是被重寫的方法
class Dog extends Animal{
    @Override
    private void eat(){
        // ...
    }
}

當我們如出現 eat 被寫成了 ate 時候,那么編譯器就會發現父類中是沒有 ate 方法的,就會編譯報錯,提示無法構成重寫

  • 重寫時可以修改返回值,方法名和參數類型及個數都不可以修改。僅當返回值為類類型時,重寫的方法才可以修改返回值類型,且必須是父類方法返回值的子類;要么就不修改,與父類返回值類型相同

了解到這,大家對于重寫肯定有了一個概念。此時我們再回憶一下之前學過的重載,可以做一個表格來進行對比

 

區別 重載(Overload) 重寫(Override)
概念 方法名稱相同、參數列表不同、返回值無要求 方法名稱相同、參數列表相同、返回類型一般相同
范圍 重載不是必須在一個類當中(繼承) 繼承關系
限制 沒有權限要求 被覆寫的方法不能擁有比父類更嚴格的訪問控制權限

 

比較結果就是,兩者沒啥關系呀

講到這里,我們好像一直沒有說明上一小節的標題動態綁定是啥

那么什么叫做動態綁定呢?發生的條件如下

  • 發生向上轉型(父類引用需要引用子類對象)
  • 通過父類引用,來調用子類和父類的同名覆蓋方法

那為啥是叫動態的呢?經過反匯編我們可以發現

編譯的時候: 調用的是父類的方法
但是運行的時候: 實際上調用的是子類的方法

因此這其實是一個動態的過程,也可以叫其運行時綁定

 

4. 向下轉型

既然介紹了向上轉型,那肯定也缺不了向下轉型呀!什么時向下轉型呢?想想向上轉型就可以猜到它就是

把父類對象賦值給了子類對象的引用

那么換成代碼就是

// 假設 Animal 是父類,Dog 是子類
public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Animal("動物");
        Dog dog=animal;
    }
}

但是只是上述這樣寫是不行的,會報錯

 Java語法之 Java 的多態、抽象類和接口

為什么呢?我們可以這樣想一下

狗是動物,但是動物不能說是狗,這相當于是一個包含的關系。

因此可以將狗的對象直接賦值給動物,但是不能將動物的對象賦值給狗

我們就可以使用強制類型轉換,這樣上述代碼就不會報錯了

public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Animal("動物");
        Dog dog=(Dog)animal;
    }
}

我們接著用 dog 引用去運行一下 eat 方法

public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Animal("動物");
        Dog dog=(Dog)animal;
        dog.eat();
    }
}

運行后出現了錯誤

Java語法之 Java 的多態、抽象類和接口

動物不能被轉換成狗!

 那我們該怎么做呢?我們要記住一點:

使用向下轉型的前提是:一定要發生了向上轉型

public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Dog("二哈");
        Dog dog=(Dog)animal;
        dog.eat();
    }
}

這樣就沒問題啦!

像上述我們提到使用向下轉型的前提是要發生向上轉型。我們其實可以理解為,我們在使用向上轉型的時候,有些功能無法做到,故我們再使用向下轉型來完善代碼(emmm,純屬個人愚見啦)。就比如

// 假設我的 Dog 類中有一個看家的方法 guard
public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Dog("二哈");
        animal.guard();
    }
}

上述代碼就會報錯,因為 Animal 類中是沒有 guard 方法的。因此我們就要借用向下轉型

public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Dog("二哈");
        Dog dog =animal;
        dog.guard();
    }
}

注意:

其實向下轉型不常使用,使用它可能會不小心犯一些錯誤。如果我們上述的代碼又要繼續使用一些其他動物的特有方法,如果忘了它們沒有發生向上轉型,就會報錯。

為了避免這種錯誤: 我們可以使用 instanceof

instanceof:可以判定一個引用是否是某個類的實例,如果是則返回 true,不是則返回 false,如

public class TestDemo{
    public static void main(String[] args){
        Animal animal=new Dog("二哈");
        if(animal instanceof Bird){
            Bird bird=(Bird)animal;
            bird.fly();
        }
    }
}

上述代碼就是先判斷 Animal 的引用是否是 Bird 的實例,我們知道它應該是 Dog 的實例,故返回 false

 

5. 關鍵字 super

其實上章就講解過了 super 關鍵字,這里我再用一個表格比較下 this 和 super,方便理解

Java語法之 Java 的多態、抽象類和接口

 

6. 在構造方法中調用重寫方法(坑)

接下來我們看一段代碼,大家可以猜猜結果是啥哦!

class Animal{
    public  String name;
    public Animal(String name){
        eat();
        this.name=name;
    }
    public void eat(){
        System.out.println(this.name+"在吃食物(Animal)");
    }
}
class Dog extends Animal{
    public Dog(String name){
        super(name);
    }
    public void eat(){
        System.out.println(this.name+"在吃食物(Dog)");
    }
}
public class TestDemo{
    public static void main(String[] args){
        Dog dog=new Dog("二哈");
    }
}

結果就是

 Java語法之 Java 的多態、抽象類和接口

 如果沒猜對的,一般有兩個疑惑:

  • 沒有調用 eat 方法,但為什么結果是這樣的?
  • 為啥是 null?

解答:

疑惑一: 因為子類繼承父類需要幫父類構造方法,所以子類創建對象時,就構造了父類的構造方法,就執行了父類的 eat 方法
疑惑二: 由于父類構造方法是先執行 eat 方法,而 name 的賦值在后面一步,多以此時的 name 是 null

結論:

構造方法中可以調用重寫的方法,并且發生了動態綁定

 

7. 理解多態

介紹到這里,我們終于要開始正式介紹我們今天的一大重點多態了!那什么是多態呢?其實他和繼承一樣是一種思想,我們可以先看一段代碼

class Shape{
    public void draw(){

    }
}
class Cycle extends Shape{
    @Override
    public void draw() {
        System.out.println("畫一個圓?");
    }
}
class Rect extends Shape{
    @Override
    public void draw() {
        System.out.println("畫一個方片♦");
    }
}
class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("畫一朵花?");
    }
}
public class TestDemo{
    public static void main(String[] args) {
        Cycle shape1=new Cycle();
        Rect shape2=new Rect();
        Flower shape3=new Flower();
        drawMap(shape1);
        drawMap(shape2);
        drawMap(shape3);
    }
    public static void drawMap(Shape shape){
        shape.draw();
    }
}

我們發現 drawMap 這個方法被調用者使用時,都是經過父類調用了其中的 draw 方法,并且最終的表現形式是不一樣的。而這種思想就叫做多態。

更簡單的說,多態就是

一個引用能表現出多種不同的形態

而多態是一種思想,實現它的前提有兩點

  • 向上轉型
  • 調用同名的覆蓋方法

而一種思想的傳承總有它獨到的好處,那么使用多態有什么好處呢?

1)類調用者對類的使用成本進一步降低

  • 封裝是讓類的調用者不需要知道類的實現細節
  • 多態能讓類的調用者連這個類的類型是什么都不必知道,只需要這個對象具有某種方法即可

2)能夠降低代碼的“圈復雜度”,避免使用大量的 if-else 語句

圈復雜度:

是一種描述一段代碼復雜程度的方式。可以將一段代碼中條件語句和循環語句出現的個數看作是“圈復雜度”,這個個數越多,就認為理解起來更復雜。

我們可以看一段代碼

public static void drawShapes(){
    Rect rect = new Rect(); 
    Cycle cycle = new Cycle(); 
    Flower flower = new Flower(); 
    String[] shapes = {"cycle", "rect", "cycle", "rect", "flower"}; 
    for (String shape : shapes) { 
     if (shape.equals("cycle")) { 
       cycle.draw(); 
      } else if (shape.equals("rect")) { 
       rect.draw(); 
      } else if (shape.equals("flower")) { 
       flower.draw(); 
   }
    }
}

這段代碼的意思就是要分別打印圓、方片、圓、方片、花,如果不使用多態的話,我們一般就會寫出上面這種方法。而使用多態的話,代碼就會顯得很簡單,如

public static void drawShapes() { 
    // 我們創建了一個 Shape 對象的數組. 
    Shape[] shapes = {new Cycle(), new Rect(), new Cycle(), new Rect(), new Flower()}; 
    for (Shape shape : shapes) { 
     shape.draw(); 
    } 
}

我們可以通過下面這種圖理解上面的代碼

 Java語法之 Java 的多態、抽象類和接口

而整體看起來,使用了多態的代碼就簡單了很多

3)可擴展能力強

如上述畫圖的代碼,如果我們要新增一種新的形狀,使用多態的方式改動成本也比較低,如

// 增加三角形
class Triangle extends Shape { 
    @Override 
    public void draw() { 
     System.out.println("△"); 
    } 
}

運用多態的話,我們擴展的代碼增加一個新類就可以。而對于不使用多態的情況,就還需要對 if-else 語句進行一定的修改,故改動成本會更高

 

8. 小結

到此為止,面向對象的三大特點:封裝、繼承、多態已經全部介紹完了。由于我個人的理解也有限,所以講的可能不好、不足,希望大家多多理解呀。

 接下來將會介紹抽象類和接口,其中也會進一步運用到多態,大家可以多多練習,加深思想的理解。

 

二、抽象類

 

1. 概念

我們上面剛寫過一個畫圖型的代碼,其中父類的定義是這樣的

class Shape{
    public void draw(){

    }
}

我們發現,父類中的 draw 方法里面沒有內容,而繪圖都是通過各種子類的 draw 方法完成的。

像上述代碼,這種沒有實際工作的方法,我們可以通過 abstract 來設計設計成一個抽象方法,而包含抽象方法的類就是抽象類

設計之后的代碼就是這樣的

abstract class Shape{
    public abstract void draw();
}

 

2. 注意事項

方法和類都要由 abstract 修飾

抽象類中可以定義其他數據成員和成員方法,如

abstract class Shape{
    public int a;
    public void b(){
        // ...
    }
    public abstract void draw();
}

但要使用這些成員和方法,需要靠子類通過 super 才能使用

  • 抽象類不可以被實例化
  • 抽象方法不能是被 private 修飾的
  • 抽象方法不能是被 final 修飾的,它與 abstract 不能被共存
  • 如果子類繼承了抽象類,但不需要重寫父類的抽象方法,則可以將子類用 abstract 修飾,如
abstract class Shape{
    public abstract void draw();
}
abstract Color extends Shape{
    
}

此時該子類中既可以定義普通方法也可以定義抽象方法

一個抽象類 A 可以被另外的抽象類 B 繼承,但是如果有其他的普通類繼承了抽象類 B,則該普通類需要重寫 A 和 B 中的所有抽象方法

 

3. 抽象類的意義

我們要知道抽象類的意義就是為了被繼承

從注意事項中就知道抽象類本身是不能被實例化的,要想使用它,只能創建子類去繼承,就比如

abstract class Shape{
    public int a;
    public void b(){
        // ...
    }
    public abstract void draw();
}
class Cycle extends Shape{
    @Override
    public void draw(){
        System.out.println("畫一個?");
    }
}
public class TestDemo{
    public static void main(String[] args){
        Shape shape=new Cycle();
    }
}

要注意子類需要重寫父類的所有抽象方法,不然代碼就會報錯

 

3. 抽象類的作用

那么抽象類既然不能被實例化,那為什么要用它呢?

使用了抽象類就相當于多了一重編譯器的效驗

啥意思呢?就比如按照上述畫圖的代碼,實際工作其實是由子類完成的,如果不小心誤用了父類,父類不是抽象類的話是不會報錯的,因此將父類設計成抽象類,它會在父類被實例化的時候報錯,讓我們盡早地發現錯誤

 

三、接口

我們上面介紹了抽象類,抽象類中除了抽象方法還可以包含普通的方法和成員。

而接口中也可包含方法和字段,但只能是抽象方法和靜態常量。

 

1. 語法規則

我們可以將上述 Shape 改寫成一個 接口,代碼如下

interface IShape{
    public static void draw();
}

具體的語法規則如下:

接口是使用 interface 定義的

接口的命名一般以大寫字母 I 開頭

接口中的方法一定是抽象的、被 public 修飾的方法,因此其中抽象方法可以簡化代碼為

interface IShape{
    void draw();
}

這樣寫默認是 public abstract

接口中也可以包含被 public 修飾的靜態常量,并且可以省略 public static final,如

interface IShape{
    public static final int a=10;
    public static int b=10;
    public int c=10;
    int d=10;
}

接口不能被單獨實例化,和抽象類一樣需要被子類繼承使用,但是接口中使用 implements 繼承,如

interface IShape{
    public static void draw();
}
class Cycle implements IShape{
    @Override
    public void draw(){
        System.out.println("畫一個圓預?");
    }
}

extends 表達含義是”擴展“不同,implements 表達的是”實現“,即表示當前什么都沒有,一切需要從頭構造

基礎接口的類需要重寫接口中的全部抽象方法

一個類可以使用 implements 實現多個接口,每個接口之間使用逗號分隔開就可以,如

interface A{
    void func1();
}
interface B{
    void func2();
}
class C implements A,B{
    @Override
    public void func1(){
        
    }
    @Override
    public void func2{
        
    }
}

注意這個類要重寫所有繼承的接口的所有抽象方法,在 IDEA 中使用 ctrl + i ,快速實現接口

接口和接口之間的關系可以使用 extends 來維護,這是意味著”擴展“,即某個接口擴展了其他接口的功能,如

interface A{
    void func1();
}
interface B{
    void func2();
}
interface D implements A,B{
    @Override
    public void func1(){
          
    }
    @Override
    public void func2{
          
    }
    void func3();
}

注意:

在 JDK1.8 開始,接口當中的方法可以是普通方法,但前提是:這個方法是由 default 修飾的(即是這個接口的默認方法),如

interface IShape{
    void draw();
    default public void func(){
        System.out.println("默認方法");
    }
}

 

2. 實現多個接口

我們之前介紹過,Java 中的繼承是單繼承,即一個類只能繼承一個父類

但是可以同時實現多個接口,故我們可以通過多接口去達到多繼承類似的效果

接下來通過代碼來理解吧!

class Animal{
    public String name;
    public Animal(String name){
        this.name=name;
    }
}
class Bird extends Animal{
    public Bird(String name){
        super(name);
    }
}

此時子類 Bird 繼承了父類 Animal,但是不能再繼承其他類了,但是還可以繼續實現其他的接口,如

class Animal{
    public String name;
    public Animal(String name){
        this.name=name;
    }
}
interface ISwing{
    void swing();
}
interface IFly{
    void fly();
}
class Bird extends Animal implements ISwing,IFly{
    public Bird(String name){
        super(name);
    }
    @Override
    public void swing(){
        System.out.println(this.name+"在游");
    }
    @Override
    public void fly(){
        System.out.println(this.name+"在飛");
    }
}

上述代碼就相當于實現了多繼承,因此接口的出現很好的解決了 Java 單繼承的問題

并且我們可以感受到,接口表達的好像是具有了某種屬性,因此有了接口以后,類的使用者就不必關注具體的類型了,而只要關注該類是否具備某個能力,比如

public class TestDemo {
    public static void fly(IFly flying){
        flying.fly();
    }
    public static void main(String[] args) {
        IFly iFly=new Bird("飛鳥");
        fly(iFly);
    }
}

因為飛鳥本身具有飛的屬性,所以我們不必關注具體的類型,因為只要會飛的都可以實現飛的屬性,如超人也會飛,就可以定義一個超人的類

class SuperMan implements IFly{
    @Override
    public void fly(){
        System.out.println("超人在飛");
    }
}
public class TestDemo {
    public static void fly(IFly flying){
        flying.fly();
    }
    public static void main(String[] args) {
        fly(new SuperMan());
    }
}

注意:

子類先繼承父類再實現接口

 

3. 接口的繼承

語法規則里面就介紹了,接口和接口之間可以使用 extends 來維護,可以使某個接口擴展其他接口的功能

這里就不再重述了

下面我們再學習一些接口,來加深對于接口的理解

 

4. Comparable 接口

我們之前介紹過 Arrays 類中的 sort 方法,它可以幫我們進行排序,比如

public class TestDemo {
    public static void main(String[] args) {
        int[] array={2,9,4,1,7};
        System.out.println("排序前:"+Arrays.toString(array));
        Arrays.sort(array);
        System.out.println("排序后:"+Arrays.toString(array));

    }
}

而接下來我想要對一個學生的屬性進行排序。

首先我實現一個 Student 類,并對 toString 方法進行了重寫

class Student{
    private String name;
    private int age;
    private double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name="" + name + """ +
                ", age=" + age +
                ", score=" + score +
                "}";
    }
}

接下來我寫了一個數組,并賦予了學生數組一些屬性

public class TestDemo {
    public static void main(String[] args) {
        Student[] student=new Student[3];
        student[0]=new Student("張三",18,96.5);
        student[0]=new Student("李四",19,99.5);
        student[0]=new Student("王五",17,92.0);
    }
}

那么我們可以直接通過 sort 函數進行排序嗎?我們先寫如下代碼

public class TestDemo {
    public static void main(String[] args) {
        Student[] student=new Student[3];
        student[0]=new Student("張三",18,96.5);
        student[1]=new Student("李四",19,99.5);
        student[2]=new Student("王五",17,92.0);
        System.out.println("排序前:"+student);
        Arrays.sort(student);
        System.out.println("排序后:"+student);
    }
}

最終結果卻是

Java語法之 Java 的多態、抽象類和接口

我們來分析一下

ClassCastException:類型轉換異常,說 Student 不能被轉換為 java.lang.Comparable

這是什么意思呢?我們思考由于 Student 是我們自定義的類型,里面包含了多個類型,那么 sort 方法怎么對它進行排序呢?好像沒有一個依據。

此時我通過報錯找到了 Comparable

 Java語法之 Java 的多態、抽象類和接口

可以知道這個應該是一個接口,那我們就可以嘗試將我們的 Student 類繼承這個接口,其中后面的 < T > 其實是泛型的意思,這里改成 < Student > 就行

class Student implements Comparable<Student>{
    public String name;
    public int age;
    public double score;

    public Student(String name, int age, double score) {
        this.name = name;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name="" + name + """ +
                ", age=" + age +
                ", score=" + score +
                "}";
    }
}

但此時還不行,因為繼承需要重寫接口的抽象方法,所以經過查找,我們找到了

Java語法之 Java 的多態、抽象類和接口

增加的重寫方法就是

@Override
public int compareTo(Student o) {
    // 新的比較的規則
}

這里應該就是比較規則的設定地方了,我們再看看 sort 方法中的交換

Java語法之 Java 的多態、抽象類和接口

也就是說如果此時的左值大于右值,則進行交換

那么如果我想對學生的年齡進行排序,重寫后的方法應該就是

@Override
public int compareTo(Student o) {
    // 新的比較的規則
    return this.age-o.age;
}

此時再運行代碼,結果就是

Java語法之 Java 的多態、抽象類和接口

而到這里我們可以更深刻的感受到,接口其實就是某種屬性或者能力,而上述 Student 這個類繼承了這個比較的接口,就擁有了比較的能力

缺點:

當我們比較上述代碼的姓名時,就要將重寫的方法改為

@Override
public int compareTo(Student o) {
    // 新的比較的規則
    return this.name.compareTo(o.name);
}

當我們比較上述代碼的分數時,就要將重寫的方法改為

@Override
public int compareTo(Student o) {
    // 新的比較的規則
    return int(this.score-o.score);
}

我們發現當我們要修改比較的東西時,就可能要重新修改重寫的方法。這個局限性就比較大

為了解決這個缺陷,就出現了下面的接口 Comparator

 

4. Comparator 接口

我們進入 sort 方法的定義中還可以看到一個比較方法,其中有兩個參數數組與 Comparator 的對象

Java語法之 Java 的多態、抽象類和接口

這里就用到了 Comparator 接口

這個接口啥嘞?我們可以先定義一個年齡比較類 AgeComparator,就是專門用來比較年齡,并讓他繼承這個類

class AgeCompartor implements Comparator<Student>{

}

再通過按住 ctrl 并點擊它,我們可以跳轉到它的定義,此時我們可以發現它里面有一個方法是

 Java語法之 Java 的多態、抽象類和接口

這個與上述 Comparable 中的 compareTo 不同,那我先對它進行重寫

class AgeCompartor implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age-o2.age;
    }
}

我們再按照 sort 方法的描述,寫如下代碼

public class TestDemo {
    public static void main(String[] args) {
        Student[] student=new Student[3];
        student[0]=new Student("張三",18,96.5);
        student[1]=new Student("李四",19,99.5);
        student[2]=new Student("王五",17,92.0);
        
        System.out.println("排序前:"+Arrays.toString(student));
        AgeComparator ageComparator=new AgeComparator();
        Arrays.sort(student,ageComparator);
        System.out.println("排序后:"+Arrays.toString(student));
    }
}

這樣就可以正常的對學生的年齡進行比較了,而此時我們要再對姓名進行排序,我們就可以創建一個姓名比較類 NameComparator

class NameComparator implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.name.compareTo(o2.name);
    }
}

而我們也只需要將 sort 方法的參數 ageComparator 改成 nameComparator 就可以了

 我們可以將上述 AgeComparator NameComparator 理解成比較器 ,而使用 Comparator 這個接口比 Comparable 的局限性小很多,我們如果要對某個屬性進行比較只要增加它的比較器即可

 

5. Cloneable 接口和深拷貝

首先我們可以看這樣的代碼

class Person{
    public String name ="LiXiaobo";

    @Override
    public String toString() {
        return "Person{" +
                "name="" + name + """ +
                "}";
    }
}
public class TestDemo {
    public static void main(String[] args) {
        Person person=new Person();
    }
}

那什么是克隆呢?應該就是搞一個副本出來,比如

Java語法之 Java 的多態、抽象類和接口

那么既然這次講 Cloneable 接口,我就對其進行繼承唄!

class Person implements Cloneable{
    public String name ="LiXiaobo";

    @Override
    public String toString() {
        return "Person{" +
                "name="" + name + """ +
                "}";
    }
}

但是我們發現就算繼承之后,我們也不能通過創建的引用去找到一個克隆的方法。此時我們可以點到 Cloneable的定義看看

 Java語法之 Java 的多態、抽象類和接口

太牛了,啥都沒有!

  • 我們發現,Cloneable 這個接口是一個空接口(也叫標記接口),而這個接口的作用就是:如果一個類實現了這個接口,就證明它是可以被克隆的
  • 而在使用它之前,我們還需要重寫 Object 的克隆方法(所有的類默認繼承于 Object 類)

怎樣重寫克隆方法呢?通過 ctrl + o,就可以看到

Java語法之 Java 的多態、抽象類和接口

再選擇 clone 就

延伸 · 閱讀

精彩推薦
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7482021-02-04
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 久久成人久久爱 | 男女免费观看在线爽爽爽视频 | 国产精品一二三区 | 久久精品在线视频 | 久久国产福利 | 国产精品99久久久久久动医院 | 亚洲成人一区在线观看 | 校园春色av | 欧美在线观看免费观看视频 | 美足av | 夜色影院在线观看 | 久久国产综合 | 在线免费观看av的网站 | 欧美午夜一区二区三区免费大片 | 超碰一区二区三区 | 日韩色综合 | 久久久久久久av | 久久久久久久久久久久久久av | 欧美黄色一区二区 | www操com | 日韩午夜电影 | 在线观看免费黄色 | 国产日韩精品一区二区 | 黄色av大全 | 亚洲精品久久 | 国产日韩欧美在线 | 免费网站在线观看黄 | 日韩精品中文字幕在线观看 | 欧美精品一区二区三区在线播放 | 高清一区在线 | 亚洲黄网在线观看 | 精品一区二区三区免费毛片爱 | 中文字幕不卡一区 | 在线国产一区二区 | 午夜成人免费视频 | 久久亚洲一区 | 美女超碰| 日韩午夜电影 | 天天天天操 | 在线观看毛片网站 | 亚洲一区二区三区高清 |