JavaRush /Java-Blog /Random-DE /Unterschied zwischen abstrakten Klassen und Schnittstelle...

Unterschied zwischen abstrakten Klassen und Schnittstellen

Veröffentlicht in der Gruppe Random-DE
Hallo! In dieser Vorlesung sprechen wir darüber, wie sich abstrakte Klassen von Schnittstellen unterscheiden, und schauen uns Beispiele mit gängigen abstrakten Klassen an. Unterschied zwischen abstrakten Klassen und Schnittstellen – 1Den Unterschieden zwischen einer abstrakten Klasse und einem Interface haben wir einen eigenen Vortrag gewidmet, da das Thema sehr wichtig ist. In 90 % der zukünftigen Interviews werden Sie nach dem Unterschied zwischen diesen Konzepten gefragt. Stellen Sie daher sicher, dass Sie verstehen, was Sie lesen, und lesen Sie zusätzliche Quellen, wenn Sie etwas nicht vollständig verstehen. Wir wissen also, was eine abstrakte Klasse und was eine Schnittstelle ist. Lassen Sie uns nun ihre Unterschiede durchgehen.
  1. Eine Schnittstelle beschreibt lediglich Verhalten. Er hat kein Vermögen. Aber eine abstrakte Klasse hat einen Zustand: Sie beschreibt beides.

    Nehmen wir als Beispiel eine abstrakte Klasse Birdund Schnittstelle 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;
       }
    }

    Erstellen wir eine Vogelklasse Mockingjay(Mockingjay) und erben von Bird:

    public class Mockingjay extends Bird {
    
       @Override
       public void fly() {
           System.out.println(„Flieg, Vögelchen!“);
       }
    
       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 Variablen species(Typ) und age(Alter).

    Aber wenn wir versuchen, dasselbe mit der Schnittstelle zu machen, wird das Bild anders sein. Wir können versuchen, Variablen hinzuzufügen:

    public interface Flyable {
    
       String species = new String();
       int age = 10;
    
       public void fly();
    }
    
    public interface Flyable {
    
       private String species = new String(); // Fehler
       private int age = 10; // auch ein Fehler
    
       public void fly();
    }

    Wir werden nicht einmal in der Lage sein, private Variablen innerhalb der Schnittstelle zu erstellen. Warum? Weil der private Modifikator erstellt wurde, um die Implementierung vor dem Benutzer zu verbergen. Aber es gibt keine Implementierung innerhalb der Schnittstelle: Es gibt dort nichts zu verbergen.

    Die Schnittstelle beschreibt nur das Verhalten. Dementsprechend können wir Getter und Setter nicht innerhalb der Schnittstelle implementieren. Das liegt in der Natur einer Schnittstelle: Sie soll sich mit dem Verhalten befassen, nicht mit dem Zustand.

    Java8 hat Standardschnittstellenmethoden eingeführt, die über eine Implementierung verfügen. Sie kennen sie bereits, daher werden wir sie nicht wiederholen.

  2. Eine abstrakte Klasse verknüpft und vereint Klassen, die eine sehr enge Beziehung zueinander haben. Gleichzeitig kann dieselbe Schnittstelle durch Klassen implementiert werden, die überhaupt nichts gemeinsam haben.

    Kehren wir zu unserem Beispiel mit Vögeln zurück.

    Unsere abstrakte Klasse Birdwird benötigt, um darauf basierende Vögel zu erstellen. Nur Vögel und sonst niemand! Natürlich werden sie unterschiedlich sein.

    Unterschied zwischen abstrakten Klassen und Schnittstellen - 2

    Bei der Schnittstelle Flyableist alles anders. Es beschreibt nur das Verhalten, das seinem Namen entspricht – „Fliegen“. Die Definition von „fliegend“, „flugfähig“ umfasst viele Objekte, die nicht miteinander in Zusammenhang stehen.

    Unterschied zwischen abstrakten Klassen und Schnittstellen - 3

    Diese 4 Einheiten sind in keiner Weise miteinander verbunden. Was soll ich sagen, nicht alle davon sind überhaupt belebt. Sie sind jedoch alle Flyableflugfähig.

    Wir könnten sie nicht mit einer abstrakten Klasse beschreiben. Sie haben keinen gemeinsamen Zustand oder identische Felder. Um ein Flugzeug zu charakterisieren, benötigen wir voraussichtlich die Felder „Modell“, „Baujahr“ und „maximale Passagierzahl“. Für Carlson gibt es Felder für alle Süßigkeiten, die er heute gegessen hat, und eine Liste von Spielen, die er mit dem Kind spielen wird. Für eine Mücke ... ähm ... wir wissen es nicht einmal ... Vielleicht „Belästigungsgrad“? :) :)

    Die Hauptsache ist, dass wir sie nicht mit einer abstrakten Klasse beschreiben können. Sie sind zu unterschiedlich. Aber es gibt ein gemeinsames Verhalten: Sie können fliegen. Die Schnittstelle ist ideal, um alles auf der Welt zu beschreiben, was fliegen, schwimmen, springen oder ein anderes Verhalten zeigen kann.

  3. Klassen können beliebig viele Schnittstellen implementieren, aber nur von einer Klasse erben.

    Darüber haben wir bereits mehr als einmal gesprochen. In Java gibt es keine Mehrfachvererbung, aber eine Mehrfachimplementierung. Dieser Punkt folgt teilweise aus dem vorherigen: Eine Schnittstelle verbindet viele verschiedene Klassen, die oft nichts gemeinsam haben, und eine abstrakte Klasse wird für eine Gruppe von Klassen erstellt, die sehr nahe beieinander liegen. Daher ist es logisch, dass Sie nur von einer solchen Klasse erben können. Eine abstrakte Klasse beschreibt die „ist ein“-Beziehung.

Standard-InputStream- und OutputStream-Schnittstellen

Wir haben bereits die verschiedenen Klassen durchgegangen, die für die Streaming-Eingabe und -Ausgabe verantwortlich sind. Schauen wir uns an InputStreamund OutputStream. Im Allgemeinen handelt es sich hierbei nicht um Schnittstellen, sondern um echte abstrakte Klassen. Jetzt wissen Sie, was sie sind, sodass die Arbeit mit ihnen viel einfacher wird :) InputStream- Dies ist eine abstrakte Klasse, die für die Byte-Eingabe verantwortlich ist. Java verfügt über eine Reihe von Klassen, die von erben InputStream. Jeder von ihnen ist so konfiguriert, dass er Daten aus verschiedenen Quellen empfängt. Da InputStreames sich um ein übergeordnetes Element handelt, bietet es mehrere Methoden zum bequemen Arbeiten mit Datenströmen. Jedes Kind hat diese Methoden InputStream:
  • int available()gibt die Anzahl der zum Lesen verfügbaren Bytes zurück;
  • close()schließt die Eingabequelle;
  • int read()gibt eine ganzzahlige Darstellung des nächsten verfügbaren Bytes im Stream zurück. Wenn das Ende des Streams erreicht ist, wird die Zahl -1 zurückgegeben;
  • int read(byte[] buffer)versucht, Bytes in einen 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)liest einen Teil eines Byteblocks. Wird verwendet, wenn die Möglichkeit besteht, dass der Datenblock nicht vollständig gefüllt wurde. Wenn das Ende der Datei erreicht ist, wird -1 zurückgegeben.
  • long skip(long byteCount)überspringt byteCountein Byte der Eingabe und gibt die Anzahl der ignorierten Bytes zurück.
Ich empfehle Ihnen , die vollständige Liste der Methoden zu studieren . Tatsächlich gibt es mehr als ein Dutzend Nachfolgeklassen. Hier ein paar Beispiele:
  1. FileInputStream: der häufigste Typ InputStream. Wird zum Lesen von Informationen aus einer Datei verwendet.
  2. StringBufferInputStream: ein weiterer nützlicher Typ InputStream. Es wandelt eine Zeichenfolge in einen Eingabedatenstrom um InputStream.
  3. BufferedInputStream: gepufferter Eingabestream. Es wird am häufigsten zur Verbesserung der Effizienz eingesetzt.
Erinnern Sie sich, als wir vorbeikamen BufferedReaderund sagten, dass wir es nicht benutzen müssen? Wenn wir schreiben:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
... BufferedReaderSie müssen es nicht verwenden: InputStreamReaderEs wird den Zweck erfüllen. Aber BufferedReaderes macht das effizienter und kann darüber hinaus Daten in ganzen Zeilen statt einzelner Zeichen lesen. Alles BufferedInputStreamist das selbe! Die Klasse sammelt Eingabedaten in einem speziellen Puffer, ohne ständig auf das Eingabegerät zuzugreifen. Schauen wir uns ein Beispiel an:
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(„Charakter wurde gelesen“ + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
In diesem Beispiel lesen wir Daten aus einer Datei, die sich auf dem Computer unter der Adresse „D:/Users/UserName/someFile.txt“ befindet . Wir erstellen 2 Objekte – FileInputStreamund BufferedInputStreamzwar als deren „Hülle“. Danach lesen wir die Bytes aus der Datei und wandeln sie in Zeichen um. Und so weiter, 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 ausführen, die auf Ihrem Computer gespeichert ist :) Eine Klasse OutputStreamist eine abstrakte Klasse, die eine Byte-Stream-Ausgabe definiert. Wie Sie bereits verstehen, ist dies der Antipode von InputStream„a“. Es ist nicht dafür verantwortlich, woher Daten gelesen werden, sondern dafür, wohin sie gesendet werden . Diese abstrakte Klasse stellt allen Nachkommen eine InputStreamGruppe von Methoden zur bequemen Arbeit zur Verfügung:
  • int 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 Array von Bytes in den Ausgabestream;
  • void write (byte[] buffer, int offset, int count)schreibt einen Bereich von Zählbytes aus dem Array, beginnend beim Positionsoffset.
Hier sind einige Nachkommen der Klasse OutputStream:
  1. DataOutputStream. Ein Ausgabestream, der Methoden zum Schreiben von Standard-Java-Datentypen enthält.

    Eine sehr einfache Klasse zum Schreiben primitiver Java-Typen und -Strings. Sicherlich werden Sie den geschriebenen Code 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()usw.writeShort()

  2. Klasse FileOutputStream . Implementiert einen Mechanismus zum Senden von Daten an eine Datei auf der Festplatte. Wir haben es übrigens bereits im vorherigen Beispiel verwendet, ist Ihnen das aufgefallen? Wir haben es innerhalb des DataOutputStream übergeben, der als „Wrapper“ fungierte.

  3. BufferedOutputStream. Gepufferter Ausgabestream. Auch nichts Kompliziertes, das Wesentliche ist dasselbe wie in BufferedInputStream(oder BufferedReader'a). Anstelle der üblichen sequentiellen Datenaufzeichnung wird die Aufzeichnung über einen speziellen „Speicher“-Puffer verwendet. Durch die Verwendung eines Puffers können Sie die Anzahl der Roundtrips zum Datenziel reduzieren und dadurch die Effizienz verbessern.

    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!"; // Wir werden diesen String in ein Array von Bytes konvertieren und in eine Datei schreiben
    
           byte[] buffer = text.getBytes();
    
           bufferedStream.write(buffer, 0, buffer.length);
           bufferedStream.close();
       }
    }

    Auch hier können Sie selbst mit diesem Code „spielen“ und prüfen, wie er mit echten Dateien auf Ihrem Computer funktioniert.

Über die Erben können Sie auch im Material „ InputStreamEin- /Ausgabesystem “ nachlesen. Oh , und wir werden auch einen separaten Vortrag halten, sodass für das erste Kennenlernen genügend Informationen darüber vorhanden sind. Das ist alles! Wir hoffen, dass Sie die Unterschiede zwischen Schnittstellen und abstrakten Klassen gut verstehen und bereit sind, jede Frage zu beantworten, auch eine knifflige :) OutputStreamFileInputStreamFileOutputStreamBufferedInputStream
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION