
-
Een interface beschrijft alleen gedrag. Het heeft geen staat. Maar een abstracte klasse omvat staat: het beschrijft beide.
Neem bijvoorbeeld de
Bird
abstracte klasse en deCanFly
interface: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; } }
Laten we een
MockingJay
vogelklasse maken en deze ervenBird
: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()); } }
Zoals je kunt zien, hebben we gemakkelijk toegang tot de status van de abstracte klasse - zijn
species
enage
variabelen.Maar als we hetzelfde proberen te doen met een interface, ziet het plaatje er anders uit. We kunnen proberen er variabelen aan toe te voegen:
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(); }
We kunnen zelfs geen privévariabelen in een interface declareren. Waarom? Omdat de privémodifier is gemaakt om de implementatie voor de gebruiker te verbergen. En een interface heeft geen implementatie erin: er is niets te verbergen.
Een interface beschrijft alleen gedrag. Daarom kunnen we geen getters en setters in een interface implementeren. Dit is de aard van interfaces: ze zijn nodig om met gedrag te werken, niet met staten.
Java 8 introduceerde standaardmethoden voor interfaces die een implementatie hebben. Je kent ze al, dus we zullen onszelf niet herhalen.
-
Een abstracte klasse verbindt en verenigt klassen die zeer nauw verwant zijn. Tegelijkertijd kan een enkele interface worden geïmplementeerd door klassen die absoluut niets gemeen hebben.
Laten we terugkeren naar ons voorbeeld met vogels.
Onze
Bird
abstracte klasse is nodig om vogels te maken die op die klasse zijn gebaseerd. Alleen vogels en niets anders! Natuurlijk zullen er verschillende soorten vogels zijn.Met de
CanFly
interface gaat iedereen op zijn eigen manier verder. Het beschrijft alleen het gedrag (vliegen) dat bij zijn naam hoort. Veel niet-gerelateerde dingen 'kunnen vliegen'.Deze 4 entiteiten zijn niet aan elkaar gerelateerd. Ze leven niet eens allemaal. Ze zijn echter allemaal
CanFly
.We konden ze niet beschrijven met behulp van een abstracte klasse. Ze delen niet dezelfde status of identieke velden. Om een vliegtuig te definiëren, hebben we waarschijnlijk velden nodig voor het model, het productiejaar en het maximum aantal passagiers. Voor Carlson zouden we velden nodig hebben voor alle snoepjes die hij vandaag at, en een lijst van de spelletjes die hij met zijn kleine broertje gaat spelen. Voor een mug, ...uh... ik weet het niet eens... Misschien een 'ergernisniveau'? :)
Het punt is dat we geen abstracte klasse kunnen gebruiken om ze te beschrijven. Ze zijn te verschillend. Maar ze hebben wel een gedeeld gedrag: ze kunnen vliegen. Een interface is perfect om alles in de wereld te beschrijven dat kan vliegen, zwemmen, springen of ander gedrag vertonen.
-
Klassen kunnen zoveel interfaces implementeren als u wilt, maar ze kunnen slechts één klasse erven.
We hebben dit al meer dan eens genoemd. Java heeft geen meervoudige overerving van klassen, maar ondersteunt wel meervoudige overerving van interfaces. Dit punt volgt gedeeltelijk uit het vorige: een interface verbindt veel verschillende klassen die vaak verder niets gemeen hebben, terwijl een abstracte klasse wordt gemaakt voor een groep zeer nauw verwante klassen. Daarom is het logisch dat je maar één zo'n klasse kunt erven. Een abstracte klasse beschrijft een 'is-een'-relatie.
Standaardinterfaces: InputStream en OutputStream
We hebben al verschillende klassen besproken die verantwoordelijk zijn voor invoer- en uitvoerstromen. Laten we eens kijken naarInputStream
en OutputStream
. Over het algemeen zijn dit helemaal geen interfaces, maar eerder volledig echte abstracte klassen. Nu weet je wat dat betekent, dus het zal veel gemakkelijker zijn om ermee te werken :) InputStream
is een abstracte klasse die verantwoordelijk is voor byte-invoer. Java heeft verschillende klassen die InputStream
. Elk van hen is ontworpen om gegevens uit verschillende bronnen te ontvangen. Omdat InputStream
het de ouder is, biedt het verschillende methoden die het gemakkelijk maken om met gegevensstromen te werken. Elke afstammeling van InputStream
heeft deze methoden:
int available()
geeft het aantal bytes terug dat beschikbaar is om te lezen;close()
sluit de invoerstroom;int read()
retourneert een gehele representatie van de volgende beschikbare byte in de stream. Als het einde van de stream is bereikt, wordt -1 geretourneerd;int read(byte[] buffer)
probeert bytes in de buffer te lezen en retourneert het aantal gelezen bytes. Wanneer het het einde van het bestand bereikt, retourneert het -1;int read(byte[] buffer, int byteOffset, int byteCount)
schrijft een deel van een bytesblok. Het wordt gebruikt wanneer de byte-array mogelijk niet volledig is gevuld. Wanneer het het einde van het bestand bereikt, retourneert het -1;long skip(long byteCount)
slaat byteCount bytes over in de invoerstroom en retourneert het aantal genegeerde bytes.
FileInputStream
: de meest voorkomende vorm vanInputStream
. Het wordt gebruikt om informatie uit een bestand te lezen;StringBufferInputStream
: Nog een nuttig typeInputStream
. Het zet een string om in eenInputStream
;BufferedInputStream
: Een gebufferde invoerstroom. Het wordt het vaakst gebruikt om de prestaties te verbeteren.
BufferedReader
en zeiden dat je het niet hoeft te gebruiken? Als we schrijven:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…u hoeft niet te gebruiken BufferedReader
: An InputStreamReader
kan het werk doen. Maar BufferedReader
verbetert de prestaties en kan ook hele regels gegevens lezen in plaats van individuele tekens. Hetzelfde geldt voor BufferedInputStream
! De klasse verzamelt invoergegevens in een speciale buffer zonder voortdurend toegang te krijgen tot het invoerapparaat. Laten we een voorbeeld bekijken:
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 dit voorbeeld lezen we gegevens uit een bestand dat zich op een computer bevindt op ' D:/Users/UserName/someFile.txt '. We maken 2 objecten - een FileInputStream
en een BufferedInputStream
die het 'omhult'. Vervolgens lezen we bytes uit het bestand en zetten deze om in tekens. En dat doen we tot het dossier eindigt. Zoals je kunt zien, is hier niets ingewikkelds. U kunt deze code kopiëren en uitvoeren op een echt bestand op uw computer :) De OutputStream
klasse is een abstracte klasse die een uitvoerstroom van bytes vertegenwoordigt. Zoals je al weet, is dit het tegenovergestelde van een InputStream
. Het is niet verantwoordelijk voor het ergens vandaan lezen van gegevens, maar eerder voor het ergens naartoe sturen van gegevens . Zoals InputStream
, deze abstracte klasse geeft al zijn afstammelingen een reeks handige methoden:
void close()
sluit de uitvoerstroom;void flush()
wist alle uitvoerbuffers;abstract void write(int oneByte)
schrijft 1 byte naar de uitvoerstroom;void write(byte[] buffer)
schrijft een byte-array naar de uitvoerstroom;void write(byte[] buffer, int offset, int count)
schrijft een reeks telbytes uit een array, beginnend bij de offsetpositie.
OutputStream
klas:
-
DataOutputStream
. Een uitvoerstroom die methoden bevat voor het schrijven van standaard Java-gegevenstypen.Een zeer eenvoudige klasse voor het schrijven van primitieve Java-gegevenstypen en strings. U zult waarschijnlijk de volgende code begrijpen, zelfs zonder uitleg:
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); } }
Het heeft aparte methoden voor elk type —
writeDouble()
,writeLong()
,writeShort()
, enzovoort. FileOutputStream
. Deze klasse implementeert een mechanisme voor het verzenden van gegevens naar een bestand op schijf. We gebruikten het trouwens al in het laatste voorbeeld. Is het je opgevallen? We gaven het door aan DataOutputStream, dat fungeerde als een 'wrapper'.BufferedOutputStream
. Een gebufferde uitvoerstroom. Er is hier ook niets ingewikkelds. Het doel is analoog aanBufferedInputStream
(ofBufferedReader
). In plaats van het gebruikelijke sequentiële lezen van gegevens, schrijft het gegevens met behulp van een speciale 'cumulatieve' buffer. De buffer maakt het mogelijk om het aantal keren dat de data-sink wordt geopend te verminderen, waardoor de prestaties toenemen.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); } }
Nogmaals, u kunt zelf met deze code spelen en controleren of deze werkt op echte bestanden op uw computer.
FileInputStream
, FileOutputStream
en BuffreredInputStream
, dus dit is genoeg informatie voor een eerste kennismaking. Dat is het! We hopen dat je de verschillen tussen interfaces en abstracte klassen begrijpt en klaar bent om elke vraag te beantwoorden, zelfs strikvragen :)
GO TO FULL VERSION