CodeGym /Java Blog /Toto sisi /抽像類和接口的區別
John Squirrels
等級 41
San Francisco

抽像類和接口的區別

在 Toto sisi 群組發布
你好!在本課中,我們將討論抽像類與接口的不同之處,並考慮一些常見抽像類的示例。 抽像類和接口的區別 - 1我們專門用一節課來介紹抽像類和接口之間的區別,因為這個主題非常重要。在 90% 的未來面試中,你會被問及這些概念之間的區別。這意味著您應該確保弄清楚您正在閱讀的內容。如果您不完全理解某些內容,請閱讀其他資源。所以,我們知道什麼是抽像類,什麼是接口。現在我們將討論它們的差異。
  1. 接口僅描述行為。它沒有狀態。但是抽像類包括狀態:它描述了兩者。

    Bird以抽像類和接口為例CanFly

    
    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;
       }
    }
    

    讓我們創建一個MockingJay鳥類並讓它繼承Bird

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

    如您所見,我們可以輕鬆訪問抽像類的狀態——itsspeciesagevariables。

    但如果我們嘗試對界面做同樣的事情,情況就不一樣了。我們可以嘗試給它添加變量:

    
    public interface CanFly {
    
       String species = new String();
       int age = 10;
    
       public void fly();
    }
    
    public interface CanFly {
    
       private String species = new String(); // Error
       private int age = 10; // Another error
    
       public void fly();
    }
    

    我們甚至不能在接口內聲明私有變量。為什麼?因為創建private修飾符是為了對用戶隱藏實現。接口內部沒有實現:沒有什麼可隱藏的。

    接口僅描述行為。因此,我們不能在接口內實現 getter 和 setter。這是接口的本質:它們需要處理行為,而不是狀態。

    Java 8 為具有實現的接口引入了默認方法。你已經知道他們了,所以我們不會重複自己。

  2. 抽像類連接和聯合非常密切相關的類。同時,單個接口可以由完全沒有共同點的類實現。

    讓我們回到鳥類的例子。

    需要我們的Bird抽像類來創建基於該類的鳥類。只有鳥,沒有別的!當然,會有不同種類的鳥。

    抽像類和接口的區別——2

    有了CanFly界面,每個人都以自己的方式前進。它僅描述與其名稱相關的行為(飛行)。許多不相關的東西“可以飛”。

    抽像類和接口的區別——3

    這 4 個實體彼此無關。他們甚至都不是活著的。然而,他們都CanFly

    我們無法使用抽像類來描述它們。它們不共享相同的狀態或相同的字段。要定義一架飛機,我們可能需要型號、生產年份和最大乘客人數等字段。對於 Carlson,我們需要一些田地來存放他今天吃的所有糖果,以及他將與他的弟弟一起玩的遊戲列表。對於一隻蚊子,……呃……我什至不知道……也許,一個“煩人程度”?:)

    關鍵是我們不能用一個抽像類來描述它們。他們太不一樣了。但它們確實有共同的行為:它們會飛。界面非常適合描述世界上所有可以飛行、游泳、跳躍或表現出其他行為的事物。

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

    我們已經不止一次提到過這一點。Java沒有類的多繼承,但是支持接口的多繼承。這一點部分源自前一點:一個接口連接許多通常沒有其他共同點的不同類,而抽像類是為一組非常密切相關的類創建的。因此,您只能繼承一個這樣的類是有道理的。抽像類描述了一種“is-a”關係。

標準接口:InputStream 和 OutputStream

我們已經了解了負責輸入和輸出流的各種類。讓我們考慮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)跳過輸入流中的 byteCount 個字節,並返回忽略的字節數。
我建議您研究完整的方法列表。實際上有十多個子類。例如,這裡有一些:
  1. FileInputStream: 最常見的類型InputStream。它用於從文件中讀取信息;
  2. StringBufferInputStream: 另一種有用的類型InputStream。它將字符串轉換為InputStream;
  3. BufferedInputStream:緩衝輸入流。它最常用於提高性能。
還記得我們過去BufferedReader說你不必使用它嗎? 當我們寫:

BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
......你不必使用BufferedReader: AnInputStreamReader就可以完成這項工作。但是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 read: " + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
在此示例中,我們從位於計算機“ D:/Users/UserName/someFile.txt ”的文件中讀取數據。我們創建了 2 個對象——aFileInputStream和一個BufferedInputStream“包裝”它的對象。然後我們從文件中讀取字節並將它們轉換為字符。我們這樣做直到文件結束。如您所見,這裡沒有什麼複雜的。您可以復制此代碼並在計算機上的真實文件上運行它:) 該類OutputStream是表示字節輸出流的抽像類。如您所知,這與InputStream. 它不負責從某處讀取數據,而是負責將數據發送到某處。就像 一樣InputStream,這個抽像類為它的所有後代提供了一組方便的方法:
  • void close()關閉輸出流;
  • void flush()清除所有輸出緩衝區;
  • abstract void write(int oneByte)將 1 個字節寫入輸出流;
  • void write(byte[] buffer)將字節數組寫入輸出流;
  • void write(byte[] buffer, int offset, int count)從數組的偏移位置開始寫入一系列 count 字節。
以下是該類的一些後代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 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'll convert this string to a byte array and write it to a file
    
               byte[] buffer = text.getBytes();
    
               bufferedStream.write(buffer, 0, buffer.length);
         }
    }
    

    同樣,您可以自己試用此代碼並驗證它是否適用於您計算機上的真實文件。

我們將有一節關於FileInputStream,FileOutputStream和 的單獨課程BuffreredInputStream,所以對於初次相識的人來說,這些信息已經足夠了。 就是這樣!我們希望您了解接口和抽像類之間的區別,並準備好回答任何問題,甚至是技巧性問題 :)
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION