-
Eine Schnittstelle beschreibt lediglich Verhalten. Es hat keinen Staat. Aber eine abstrakte Klasse beinhaltet den Zustand: Sie beschreibt beides.
Nehmen Sie zum Beispiel die
Bird
abstrakte Klasse und dieCanFly
Schnittstelle: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
Lassen Sie uns eine Vogelklasse erstellen und diese erbenBird
: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()); } }
Wie Sie sehen, können wir leicht auf den Status der abstrakten Klasse zugreifen – ihre
species
undage
Variablen.Wenn wir jedoch versuchen, dasselbe mit einer Schnittstelle zu erreichen, ergibt sich ein anderes Bild. Wir können versuchen, Variablen hinzuzufügen:
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(); }
Wir können nicht einmal private Variablen innerhalb einer Schnittstelle deklarieren. Warum? Weil der private Modifikator erstellt wurde, um die Implementierung vor dem Benutzer zu verbergen. Und eine Schnittstelle enthält keine Implementierung: Es gibt nichts zu verbergen.
Eine Schnittstelle beschreibt lediglich Verhalten. Dementsprechend können wir Getter und Setter nicht innerhalb einer Schnittstelle implementieren. Das liegt in der Natur von Schnittstellen: Sie werden benötigt, um mit Verhalten zu arbeiten, nicht mit Zuständen.
Mit Java 8 wurden Standardmethoden für Schnittstellen eingeführt, die über eine Implementierung verfügen. Sie kennen sie bereits, deshalb wiederholen wir uns nicht.
-
Eine abstrakte Klasse verbindet und vereint Klassen, die sehr eng miteinander verbunden sind. Gleichzeitig kann eine einzelne Schnittstelle durch Klassen implementiert werden, die absolut nichts gemeinsam haben.
Kehren wir zu unserem Beispiel mit Vögeln zurück.
Unsere
Bird
abstrakte Klasse wird zum Erstellen von Vögeln benötigt, die auf dieser Klasse basieren. Nur Vögel und sonst nichts! Natürlich wird es verschiedene Vogelarten geben.Mit der
CanFly
Schnittstelle kommt jeder auf seine Weise zurecht. Es beschreibt nur das mit seinem Namen verbundene Verhalten (Fliegen). Viele Dinge, die nichts miteinander zu tun haben, können fliegen.Diese 4 Einheiten sind nicht miteinander verbunden. Sie leben noch nicht einmal alle. Allerdings sind sie alle
CanFly
.Wir konnten sie nicht mit einer abstrakten Klasse beschreiben. Sie haben nicht denselben Status oder identische Felder. Um ein Flugzeug zu definieren, benötigen wir wahrscheinlich Felder für das Modell, das Produktionsjahr und die maximale Passagierzahl. Für Carlson bräuchten wir Felder für alle Süßigkeiten, die er heute gegessen hat, und eine Liste der Spiele, die er mit seinem kleinen Bruder spielen wird. Für eine Mücke ... äh ... ich weiß nicht einmal ... Vielleicht ein „Belästigungsniveau“? :) :)
Der Punkt ist, dass wir sie nicht mit einer abstrakten Klasse beschreiben können. Sie sind zu unterschiedlich. Aber sie haben ein gemeinsames Verhalten: Sie können fliegen. Eine Schnittstelle ist perfekt, um alles auf der Welt zu beschreiben, was fliegen, schwimmen, springen oder ein anderes Verhalten zeigen kann.
-
Klassen können beliebig viele Schnittstellen implementieren, aber nur eine Klasse erben.
Das haben wir bereits mehrfach erwähnt. Java bietet keine Mehrfachvererbung von Klassen, unterstützt jedoch die Mehrfachvererbung von Schnittstellen. Dieser Punkt folgt teilweise aus dem vorherigen: Eine Schnittstelle verbindet viele verschiedene Klassen, die oft sonst nichts gemeinsam haben, während eine abstrakte Klasse für eine Gruppe sehr eng verwandter Klassen erstellt wird. Daher ist es sinnvoll, dass Sie nur eine solche Klasse erben können. Eine abstrakte Klasse beschreibt eine „Ist-ein“-Beziehung.
Standardschnittstellen: InputStream und OutputStream
Wir haben bereits verschiedene Klassen besprochen, die für Eingabe- und Ausgabeströme verantwortlich sind. Betrachten wirInputStream
und OutputStream
. Im Allgemeinen handelt es sich hierbei überhaupt nicht um Schnittstellen, sondern um völlig echte abstrakte Klassen. Jetzt wissen Sie, was das bedeutet, daher wird es viel einfacher, damit zu arbeiten :) InputStream
ist eine abstrakte Klasse, die für die Byte-Eingabe verantwortlich ist. Java hat mehrere Klassen, die erben InputStream
. Jeder von ihnen ist darauf ausgelegt, Daten aus unterschiedlichen Quellen zu empfangen. Da InputStream
es das übergeordnete Element ist, bietet es mehrere Methoden, die die Arbeit mit Datenströmen erleichtern. Jeder Nachkomme von InputStream
hat diese Methoden:
int available()
gibt die Anzahl der zum Lesen verfügbaren Bytes zurück;close()
schließt den Eingabestream;int read()
gibt eine ganzzahlige Darstellung des nächsten verfügbaren Bytes im Stream zurück. Wenn das Ende des Streams erreicht ist, wird -1 zurückgegeben;int read(byte[] buffer)
versucht, Bytes in den Puffer zu lesen und gibt die Anzahl der gelesenen Bytes zurück. Wenn das Ende der Datei erreicht ist, wird -1 zurückgegeben.int read(byte[] buffer, int byteOffset, int byteCount)
schreibt einen Teil eines Byteblocks. Es wird verwendet, wenn das Byte-Array möglicherweise nicht vollständig gefüllt ist. Wenn das Ende der Datei erreicht ist, wird -1 zurückgegeben.long skip(long byteCount)
überspringt byteCount Bytes im Eingabestream und gibt die Anzahl der ignorierten Bytes zurück.
FileInputStream
: die häufigste Art vonInputStream
. Es wird verwendet, um Informationen aus einer Datei zu lesen;StringBufferInputStream
: Eine weitere hilfreiche Art vonInputStream
. Es konvertiert einen String in einInputStream
;BufferedInputStream
: Ein gepufferter Eingabestream. Es wird am häufigsten zur Leistungssteigerung eingesetzt.
BufferedReader
und sagten, dass Sie es nicht benutzen müssen? Wenn wir schreiben:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…Sie müssen nicht verwenden BufferedReader
: Ein InputStreamReader
kann den Job machen. Verbessert aber BufferedReader
die Leistung und kann statt einzelner Zeichen auch ganze Datenzeilen lesen. Das Gleiche gilt für BufferedInputStream
! Die Klasse sammelt Eingabedaten in einem speziellen Puffer, ohne ständig auf das Eingabegerät zuzugreifen. Betrachten wir ein Beispiel:
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();
}
}
}
In diesem Beispiel lesen wir Daten aus einer Datei, die sich auf einem Computer unter „ D:/Users/UserName/someFile.txt “ befindet. Wir erstellen zwei Objekte – ein FileInputStream
und ein, BufferedInputStream
das es „umhüllt“. Dann lesen wir Bytes aus der Datei und wandeln sie in Zeichen um. Und das machen wir, bis die Datei zu Ende ist. Wie Sie sehen, gibt es hier nichts Kompliziertes. Sie können diesen Code kopieren und in einer echten Datei auf Ihrem Computer ausführen :) Die OutputStream
Klasse ist eine abstrakte Klasse, die einen Ausgabestrom von Bytes darstellt. Wie Sie bereits wissen, ist dies das Gegenteil eines InputStream
. Es ist nicht dafür verantwortlich, Daten von irgendwoher zu lesen, sondern vielmehr dafür, Daten irgendwohin zu senden . Diese abstrakte Klasse stellt beispielsweise InputStream
allen ihren Nachkommen eine Reihe praktischer Methoden zur Verfügung:
void close()
schließt den Ausgabestream;void flush()
löscht alle Ausgabepuffer;abstract void write(int oneByte)
schreibt 1 Byte in den Ausgabestream;void write(byte[] buffer)
schreibt ein Byte-Array in den Ausgabestream;void write(byte[] buffer, int offset, int count)
schreibt einen Bereich von Zählbytes aus einem Array, beginnend an der Offset-Position.
OutputStream
Klasse:
-
DataOutputStream
. Ein Ausgabestream, der Methoden zum Schreiben von Standard-Java-Datentypen enthält.Eine sehr einfache Klasse zum Schreiben primitiver Java-Datentypen und -Strings. Sie werden den folgenden Code wahrscheinlich auch ohne Erklärung verstehen:
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); } }
Für jeden Typ gibt es separate Methoden –
writeDouble()
,writeLong()
,writeShort()
usw. FileOutputStream
. Diese Klasse implementiert einen Mechanismus zum Senden von Daten an eine Datei auf der Festplatte. Wir haben es übrigens bereits im letzten Beispiel verwendet. Hast du bemerkt? Wir haben es an DataOutputStream übergeben, der als „Wrapper“ fungierte.BufferedOutputStream
. Ein gepufferter Ausgabestream. Auch hier gibt es nichts Kompliziertes. Sein Zweck ist analog zuBufferedInputStream
(oderBufferedReader
). Anstelle des üblichen sequentiellen Lesens von Daten werden Daten mithilfe eines speziellen „kumulativen“ Puffers geschrieben. Der Puffer ermöglicht es, die Anzahl der Zugriffe auf die Datensenke zu reduzieren und dadurch die Leistung zu steigern.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); } }
Auch hier können Sie selbst mit diesem Code herumspielen und überprüfen, ob er mit echten Dateien auf Ihrem Computer funktioniert.
FileInputStream
, FileOutputStream
und haben BuffreredInputStream
, daher sind dies genügend Informationen für eine erste Bekanntschaft. Das ist es! Wir hoffen, dass Sie die Unterschiede zwischen Schnittstellen und abstrakten Klassen verstehen und bereit sind, jede Frage zu beantworten, auch Trickfragen :)
GO TO FULL VERSION