JavaRush /Java Blog /Random-TW /抽象類別和介面的區別

抽象類別和介面的區別

在 Random-TW 群組發布
你好!在本次講座中,我們將討論抽象類別與介面的不同之處,並查看常見抽象類別的範例。 抽象類別和介面的區別 - 1我們專門用一個單獨的講座來討論抽象類別和介面之間的差異,因為這個主題非常重要。在未來 90% 的面試中,你都會被問到這些概念之間的差異。因此,請務必理解您所閱讀的內容,如果您不完全理解某些內容,請閱讀其他來源。所以,我們知道什麼是抽象類,什麼是介面。現在讓我們來看看它們的差異。
  1. 介面僅描述行為。他沒有財富。但抽象類別有一個狀態:它描述了兩者。

    讓我們以抽象類別Bird和介面為例Flyable

    public abstract class Bird {
       private String species;
       private int age;
    
       public abstract void fly();
    
       public String getSpecies() {
           return species;
       }
    
       public void setSpecies(String species) {
           this.species = species;
       }
    
       public int getAge() {
           return age;
       }
    
       public void setAge(int age) {
           this.age = age;
       }
    }

    讓我們建立一個 Bird 類別Mockingjay(mockingjay) 並繼承自Bird

    public class Mockingjay extends Bird {
    
       @Override
       public void fly() {
           System.out.println("Fly, birdie!");
       }
    
       public static void main(String[] args) {
    
           Mockingjay someBird = new Mockingjay();
           someBird.setAge(19);
           System.out.println(someBird.getAge());
       }
    }

    正如您所看到的,我們可以輕鬆存取抽象類別的狀態 - 它的變數species(類型)和age(年齡)。

    但如果我們嘗試對介面做同樣的事情,圖片就會有所不同。我們可以嘗試在其中加入變數:

    public interface Flyable {
    
       String species = new String();
       int age = 10;
    
       public void fly();
    }
    
    public interface Flyable {
    
       private String species = new String(); // error
       private int age = 10; // also an error
    
       public void fly();
    }

    我們甚至無法在介面內建立私有變數。為什麼?因為建立private修飾符是為了向使用者隱藏實作。但介面內部沒有實現:那裡沒有什麼可隱藏的。

    介面僅描述行為。因此,我們將無法在介面內實作 getter 和 setter。這就是介面的本質:它旨在處理行為,而不是狀態。

    Java8 引入了具有實作的預設介面方法。你已經知道它們了,所以我們不再重複。

  2. 抽象類別連結並組合具有非常密切關係的類別。同時,相同的介面可以由完全沒有共同點的類別來實現。

    讓我們回到鳥類的例子。

    Bird需要我們的抽象類別來基於它創建鳥類。只有鳥,沒有其他人!當然他們會有所不同。

    抽象類別和介面的區別 - 2

    有了介面,Flyable一切都不同了。它只是描述了與其名字相對應的行為——「飛行」。「飛行」、「能夠飛行」的定義包括許多彼此不相關的物體。

    抽象類別和介面的區別 - 3

    這 4 個實體彼此之間沒有任何關係。我能說什麼,並不是所有的動物都是有生命的。然而,它們都有Flyable飛行能力。

    我們無法使用抽象類別來描述它們。它們沒有共同的狀態或相同的欄位。為了描述一架飛機的特徵,我們可能需要「型號」、「製造年份」和「最大乘客數量」欄位。對於卡爾森來說,有他今天吃的所有糖果的字段,以及他將與孩子一起玩的遊戲列表。對於一隻蚊子...呃-呃...我們甚至不知道...也許是「煩惱程度」?:)

    最主要的是我們不能使用抽象類別來描述它們。他們太不同了。但有一個共同的行為:它們會飛。此介面非常適合描述世界上所有可以飛行、游泳、跳躍或具有其他行為的事物。

  3. 類別可以實作任意多個接口,但只能從一個類別繼承。

    我們已經不只一次討論過這個問題。Java中沒有多重繼承,但有多重實作。這一點在某種程度上源於上一點:介面連接了許多不同的類,這些類通常沒有任何共同點,而抽象類是為一組彼此非常接近的類創建的。因此,您只能從一個這樣的類別繼承是合乎邏輯的。抽象類別描述了「是」關係。

標準輸入流和輸出流接口

我們已經了解了負責流輸入和輸出的各種類別。我們來看看InputStreamOutputStream。一般來說,這些不是接口,而是真正的抽象類別。現在您知道它們是什麼,因此使用它們會更容易:) InputStream- 這是一個負責位元組輸入的抽象類別。Java 有一系列繼承自InputStream. 它們中的每一個都被配置為從不同的來源接收資料。因為InputStream它是父級,所以它提供了幾種方便處理資料流的方法。每個孩子都有這些方法InputStream
  • int available()傳回可供讀取的位元組數;
  • close()關閉輸入源;
  • int read()返回流中下一個可用位元組的整數表示形式。如果到達流末尾,則返回數字-1;
  • int read(byte[] buffer)嘗試將位元組讀入緩衝區,傳回讀取的位元組數。當到達文件末尾時,返回-1;
  • int read(byte[] buffer, int byteOffset, int byteCount)讀取位元組區塊的一部分。當資料塊可能未完全填滿時使用。當到達文件末尾時,返回-1;
  • long skip(long byteCount)Skips byteCount,一個輸入位元組,傳回忽略的位元組數。
我建議您研究完整的方法清單。後繼班其實還有十幾個。這裡有一些例子:
  1. FileInputStream:最常見的類型InputStream。用於從文件中讀取資訊;
  2. StringBufferInputStream:另一種有用的類型InputStream。它將字串轉換為輸入資料流InputStream
  3. BufferedInputStream:緩衝輸入流。它最常用於提高效率。
你還記得當我們走過時BufferedReader就說我們不必使用它嗎? 當我們寫:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
……BufferedReader無需使用它:InputStreamReader它會完成這項工作。但BufferedReader它的效率更高,而且可以讀取整行數據,而不是單一字元。一切BufferedInputStream都一樣!此類別將輸入資料累積在特殊的緩衝區中,而無需不斷存取輸入裝置。讓我們來看一個例子:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;

public class BufferedInputExample {

   public static void main(String[] args) throws Exception {
       InputStream inputStream = null;
       BufferedInputStream buffer = null;

       try {

           inputStream = new FileInputStream("D:/Users/UserName/someFile.txt");

           buffer = new BufferedInputStream(inputStream);

           while(buffer.available()>0) {

               char c = (char)buffer.read();

               System.out.println("Character was read" + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
在此範例中,我們從位於電腦上位址「D:/Users/UserName/someFile.txt」的檔案中讀取資料。我們創建 2 個物件 -FileInputStreamBufferedInputStream作為它的「包裝器」。之後,我們從文件中讀取位元組並將其轉換為字元。依此類推,直到文件結束。正如您所看到的,這裡沒有什麼複雜的。您可以複製此程式碼並在電腦上儲存的某個真實檔案上運行它:) 類別OutputStream是定義位元組流輸出的抽象類別。正如您已經了解的,這是InputStream'a 的對映體。它不負責從哪裡讀取數據,而是負責將數據發送到哪裡。與 一樣InputStream,這個抽象類別為所有後代提供了一組方法以方便工作:
  • int close()關閉輸出流;
  • void flush()清除所有輸出緩衝區;
  • abstract void write (int oneByte)將 1 個位元組寫入輸出流;
  • void write (byte[] buffer)將位元組數組寫入輸出流;
  • void write (byte[] buffer, int offset, int count)從陣列中寫入一系列 count 位元組,從位置 offset 開始。
以下是該類別的一些後代OutputStream
  1. DataOutputStream。包含用於編寫標準 J​​ava 資料類型的方法的輸出流。

    一個非常簡單的類,用於編寫原始 Java 類型和字串。即使不解釋,你也一定會理解寫的程式碼:

    import java.io.*;
    
    public class DataOutputStreamExample {
    
       public static void main(String[] args) throws IOException {
    
           DataOutputStream dos = new DataOutputStream(new FileOutputStream("testFile.txt"));
    
           dos.writeUTF("SomeString");
           dos.writeInt(22);
           dos.writeDouble(1.21323);
           dos.writeBoolean(true);
    
       }
    }

    它對每種類型都有單獨的方法 - writeDouble()writeLong()writeShort()

  2. 班級 FileOutputStream。實作一種將資料傳送到磁碟上的檔案的機制。順便說一句,我們已經在前面的範例中使用了它,您注意到了嗎?我們將它傳遞到 DataOutputStream 內部,它充當「包裝器」。

  3. BufferedOutputStream。緩衝輸出流。BufferedInputStream也不複雜,本質與(or 'a)中的相同BufferedReader。使用透過特殊「儲存」緩衝區進行記錄,而不是通常的順序資料記錄。透過使用緩衝區,您可以減少到資料目的地的往返次數,從而提高效率。

    import java.io.*;
    
    public class DataOutputStreamExample {
    
       public static void main(String[] args) throws IOException {
    
           FileOutputStream outputStream = new FileOutputStream("D:/Users/Username/someFile.txt");
           BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream);
    
           String text = "I love Java!"; // we will convert this string into an array of bytes and write it to a file
    
           byte[] buffer = text.getBytes();
    
           bufferedStream.write(buffer, 0, buffer.length);
           bufferedStream.close();
       }
    }

    同樣,您可以自己“玩”此程式碼,並檢查它如何在您電腦上的真實檔案上運行。

您也可以在材料“InputStream輸入/輸出系統OutputStream”中閱讀有關繼承人的內容。哦,我們還會有一個單獨的講座,所以對於第一次認識他們來說,有足夠的資訊。就這樣!我們希望您能夠很好地理解介面和抽象類別之間的差異,並準備好回答任何問題,即使是一個棘手的問題:) FileInputStreamFileOutputStreamBufferedInputStream
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION