-
介面僅描述行為。他沒有財富。但抽象類別有一個狀態:它描述了兩者。
讓我們以抽象類別
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 引入了具有實作的預設介面方法。你已經知道它們了,所以我們不再重複。
-
抽象類別連結並組合具有非常密切關係的類別。同時,相同的介面可以由完全沒有共同點的類別來實現。
讓我們回到鳥類的例子。
Bird
需要我們的抽象類別來基於它創建鳥類。只有鳥,沒有其他人!當然他們會有所不同。有了介面,
Flyable
一切都不同了。它只是描述了與其名字相對應的行為——「飛行」。「飛行」、「能夠飛行」的定義包括許多彼此不相關的物體。這 4 個實體彼此之間沒有任何關係。我能說什麼,並不是所有的動物都是有生命的。然而,它們都有
Flyable
飛行能力。我們無法使用抽象類別來描述它們。它們沒有共同的狀態或相同的欄位。為了描述一架飛機的特徵,我們可能需要「型號」、「製造年份」和「最大乘客數量」欄位。對於卡爾森來說,有他今天吃的所有糖果的字段,以及他將與孩子一起玩的遊戲列表。對於一隻蚊子...呃-呃...我們甚至不知道...也許是「煩惱程度」?:)
最主要的是我們不能使用抽象類別來描述它們。他們太不同了。但有一個共同的行為:它們會飛。此介面非常適合描述世界上所有可以飛行、游泳、跳躍或具有其他行為的事物。
-
類別可以實作任意多個接口,但只能從一個類別繼承。
我們已經不只一次討論過這個問題。Java中沒有多重繼承,但有多重實作。這一點在某種程度上源於上一點:介面連接了許多不同的類,這些類通常沒有任何共同點,而抽象類是為一組彼此非常接近的類創建的。因此,您只能從一個這樣的類別繼承是合乎邏輯的。抽象類別描述了「是」關係。
標準輸入流和輸出流接口
我們已經了解了負責流輸入和輸出的各種類別。我們來看看InputStream
和OutputStream
。一般來說,這些不是接口,而是真正的抽象類別。現在您知道它們是什麼,因此使用它們會更容易:) 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)
SkipsbyteCount
,一個輸入位元組,傳回忽略的位元組數。
FileInputStream
:最常見的類型InputStream
。用於從文件中讀取資訊;StringBufferInputStream
:另一種有用的類型InputStream
。它將字串轉換為輸入資料流InputStream
;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 個物件 -FileInputStream
並BufferedInputStream
作為它的「包裝器」。之後,我們從文件中讀取位元組並將其轉換為字元。依此類推,直到文件結束。正如您所看到的,這裡沒有什麼複雜的。您可以複製此程式碼並在電腦上儲存的某個真實檔案上運行它:) 類別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
:
-
DataOutputStream
。包含用於編寫標準 Java 資料類型的方法的輸出流。一個非常簡單的類,用於編寫原始 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()
。 -
班級
FileOutputStream
。實作一種將資料傳送到磁碟上的檔案的機制。順便說一句,我們已經在前面的範例中使用了它,您注意到了嗎?我們將它傳遞到 DataOutputStream 內部,它充當「包裝器」。 -
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
”中閱讀有關繼承人的內容。哦,我們還會有一個單獨的講座,所以對於第一次認識他們來說,有足夠的資訊。就這樣!我們希望您能夠很好地理解介面和抽象類別之間的差異,並準備好回答任何問題,即使是一個棘手的問題:) FileInputStream
FileOutputStream
BufferedInputStream
GO TO FULL VERSION