
-
接口僅描述行為。它沒有狀態。但是抽像類包括狀態:它描述了兩者。
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()); } }
如您所見,我們可以輕鬆訪問抽像類的狀態——its
species
和age
variables。但如果我們嘗試對界面做同樣的事情,情況就不一樣了。我們可以嘗試給它添加變量:
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 為具有實現的接口引入了默認方法。你已經知道他們了,所以我們不會重複自己。
-
抽像類連接和聯合非常密切相關的類。同時,單個接口可以由完全沒有共同點的類實現。
讓我們回到鳥類的例子。
需要我們的
Bird
抽像類來創建基於該類的鳥類。只有鳥,沒有別的!當然,會有不同種類的鳥。有了
CanFly
界面,每個人都以自己的方式前進。它僅描述與其名稱相關的行為(飛行)。許多不相關的東西“可以飛”。這 4 個實體彼此無關。他們甚至都不是活著的。然而,他們都
CanFly
。我們無法使用抽像類來描述它們。它們不共享相同的狀態或相同的字段。要定義一架飛機,我們可能需要型號、生產年份和最大乘客人數等字段。對於 Carlson,我們需要一些田地來存放他今天吃的所有糖果,以及他將與他的弟弟一起玩的遊戲列表。對於一隻蚊子,……呃……我什至不知道……也許,一個“煩人程度”?:)
關鍵是我們不能用一個抽像類來描述它們。他們太不一樣了。但它們確實有共同的行為:它們會飛。界面非常適合描述世界上所有可以飛行、游泳、跳躍或表現出其他行為的事物。
-
類可以實現任意多個接口,但只能繼承一個類。
我們已經不止一次提到過這一點。Java沒有類的多繼承,但是支持接口的多繼承。這一點部分源自前一點:一個接口連接許多通常沒有其他共同點的不同類,而抽像類是為一組非常密切相關的類創建的。因此,您只能繼承一個這樣的類是有道理的。抽像類描述了一種“is-a”關係。
標準接口:InputStream 和 OutputStream
我們已經了解了負責輸入和輸出流的各種類。讓我們考慮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)
跳過輸入流中的 byteCount 個字節,並返回忽略的字節數。
FileInputStream
: 最常見的類型InputStream
。它用於從文件中讀取信息;StringBufferInputStream
: 另一種有用的類型InputStream
。它將字符串轉換為InputStream
;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
:
-
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
(orBufferedReader
)。它不是通常的順序讀取數據,而是使用特殊的“累積”緩衝區寫入數據。緩衝區可以減少訪問數據接收器的次數,從而提高性能。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
,所以對於初次相識的人來說,這些信息已經足夠了。 就是這樣!我們希望您了解接口和抽像類之間的區別,並準備好回答任何問題,甚至是技巧性問題 :)
更多閱讀: |
---|
GO TO FULL VERSION