-
Az interfész csak a viselkedést írja le. Nincs állama. De egy absztrakt osztály magában foglalja az állapotot: mindkettőt leírja.
Vegyük például az
Bird
absztrakt osztályt és aCanFly
felületet: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; } }
Hozzunk létre egy
MockingJay
madárosztályt, és örököljükBird
: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()); } }
Amint látja, könnyen hozzáférhetünk az absztrakt osztály állapotához – annak
species
ésage
változóihoz.De ha ugyanezt egy interfésszel próbáljuk megtenni, akkor más a kép. Megpróbálhatunk változókat hozzáadni hozzá:
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(); }
Még privát változókat sem deklarálhatunk egy interfészen belül. Miért? Mert a privát módosító azért jött létre, hogy elrejtse a megvalósítást a felhasználó elől. És egy felületen nincs implementáció: nincs rejtegetnivaló.
Az interfész csak a viselkedést írja le. Ennek megfelelően nem implementálhatunk gettereket és settereket egy felületen belül. Ez az interfészek természete: a viselkedéssel, nem pedig az állapottal való munkához szükségesek.
A Java 8 alapértelmezett metódusokat vezetett be a megvalósítással rendelkező interfészekhez. Ön már tud róluk, ezért nem ismételjük magunkat.
-
Az absztrakt osztály olyan osztályokat köt össze és egyesít, amelyek nagyon szorosan kapcsolódnak egymáshoz. Ugyanakkor egyetlen interfészt megvalósíthatnak olyan osztályok, amelyekben semmi közös.
Térjünk vissza a madarakkal kapcsolatos példánkhoz.
Absztrakt osztályunkra
Bird
szükség van az osztályon alapuló madarak létrehozásához. Csak madarak és semmi más! Természetesen lesznek különféle madarak.A
CanFly
felülettel mindenki a maga módján halad. Csak a nevéhez kapcsolódó viselkedést (repülést) írja le. Sok független dolog „repülhet”.Ez a 4 entitás nem kapcsolódik egymáshoz. Még csak nem is mind élnek. Azonban mindannyian
CanFly
.Nem tudtuk leírni őket absztrakt osztály segítségével. Nem osztják meg ugyanazt az állapotot vagy nem azonos mezőket. A repülőgép meghatározásához valószínűleg a modellre, a gyártási évre és a maximális utasszámra lenne szükségünk. Carlsonnak szüksége lenne egy mezőre az összes édesség számára, amelyet ma evett, és egy listát azokról a játékokról, amelyeket az öccsével fog játszani. Egy szúnyognak... ööö... nem is tudom... Talán egy „bosszankodási szint”? :)
A lényeg az, hogy nem használhatunk absztrakt osztályt ezek leírására. Túlságosan különböznek egymástól. De közös a viselkedésük: tudnak repülni. Egy interfész tökéletes a világon minden dolog leírására, ami képes repülni, úszni, ugrani vagy más viselkedést mutatni.
-
Az osztályok tetszőleges számú interfészt implementálhatnak, de csak egy osztályt örökölhetnek.
Ezt már többször említettük. A Java nem rendelkezik osztályok többszörös öröklésével, de támogatja az interfészek többszörös öröklését. Ez a pont részben következik az előzőből: egy interfész sok különböző osztályt köt össze, amelyekben gyakran semmi más nem közös, míg egy absztrakt osztály a nagyon szorosan kapcsolódó osztályok csoportjához jön létre. Ezért logikus, hogy csak egy ilyen osztályt örökölhet. Az absztrakt osztály egy „is-a” kapcsolatot ír le.
Szabványos interfészek: InputStream és OutputStream
Már átnéztük a bemeneti és kimeneti adatfolyamokért felelős különböző osztályokat. TekintsükInputStream
és OutputStream
. Általában ezek egyáltalán nem interfészek, hanem teljesen eredeti absztrakt osztályok. Most már tudod, hogy ez mit jelent, így sokkal könnyebb lesz velük dolgozni :) InputStream
egy absztrakt osztály, amely a bájtbevitelért felelős. A Java-nak számos osztálya van, amelyek öröklik InputStream
. Mindegyiket úgy tervezték, hogy különböző forrásokból fogadjon adatokat. Mivel InputStream
a szülő, számos olyan módszert biztosít, amelyek megkönnyítik az adatfolyamokkal való munkát. Mindegyik leszármazottja InputStream
rendelkezik a következő módszerekkel:
int available()
visszaadja az olvasásra rendelkezésre álló bájtok számát;close()
bezárja a bemeneti adatfolyamot;int read()
a folyam következő elérhető bájtjának egész számmal történő megjelenítését adja vissza. Ha a folyam végét elértük, -1-et ad vissza;int read(byte[] buffer)
bájtokat próbál beolvasni a pufferbe, és visszaadja az olvasott bájtok számát. Amikor eléri a fájl végét, akkor -1;int read(byte[] buffer, int byteOffset, int byteCount)
egy bájtblokk egy részét írja. Akkor használatos, ha a bájttömb esetleg nincs teljesen kitöltve. Amikor eléri a fájl végét, akkor -1;long skip(long byteCount)
kihagyja a byteCount byte-ot a bemeneti adatfolyamban, és visszaadja a figyelmen kívül hagyott bájtok számát.
FileInputStream
: a leggyakoribb típus aInputStream
. Fájlból információk olvasására szolgál;StringBufferInputStream
: Egy másik hasznos típusInputStream
. Egy karakterláncot alakít átInputStream
;BufferedInputStream
: Pufferelt bemeneti adatfolyam. Leggyakrabban a teljesítmény növelésére használják.
BufferedReader
és azt mondtuk, hogy nem kell használnod? Amikor írunk:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
…nem kell használnod BufferedReader
: Egy InputStreamReader
képes elvégezni a munkát. De BufferedReader
javítja a teljesítményt, és az egyes karakterek helyett egész adatsort tud olvasni. Ugyanez vonatkozik rá BufferedInputStream
! Az osztály egy speciális pufferben gyűjti a bemeneti adatokat anélkül, hogy folyamatosan hozzáférne a beviteli eszközhöz. Nézzünk egy példát:
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();
}
}
}
Ebben a példában a „ D:/Users/UserName/someFile.txt ” címen található számítógépen található fájlból olvasunk adatokat . Létrehozunk 2 objektumot – a-t FileInputStream
és a-t BufferedInputStream
, amelyek „becsomagolják”. Ezután bájtokat olvasunk ki a fájlból, és karakterekké alakítjuk. És ezt addig tesszük, amíg a fájl véget nem ér. Amint látja, nincs itt semmi bonyolult. Ezt a kódot lemásolhatod és egy valós fájlon futtathatod a számítógépeden :) Az OutputStream
osztály egy absztrakt osztály, amely egy bájtokból álló kimeneti adatfolyamot képvisel. Mint már tudja, ez az ellentéte egy InputStream
. Nem azért felelős, hogy adatokat olvasson valahonnan, hanem azért, hogy adatokat küldjön valahova . Hasonlóan InputStream
, ez az absztrakt osztály minden leszármazottjának kényelmes metódusokat ad:
void close()
bezárja a kimeneti adatfolyamot;void flush()
törli az összes kimeneti puffert;abstract void write(int oneByte)
1 bájtot ír a kimeneti adatfolyamba;void write(byte[] buffer)
bájttömböt ír a kimeneti adatfolyamba;void write(byte[] buffer, int offset, int count)
egy tömbből egy számláló bájt tartományt ír ki, az eltolási pozíciótól kezdve.
OutputStream
:
-
DataOutputStream
. Kimeneti adatfolyam, amely szabványos Java adattípusok írási módszereit tartalmazza.Egy nagyon egyszerű osztály primitív Java adattípusok és karakterláncok írásához. Valószínűleg magyarázat nélkül is megérti a következő kódot:
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); } }
Minden típushoz külön metódusai vannak –
writeDouble()
,writeLong()
,writeShort()
, és így tovább. FileOutputStream
. Ez az osztály egy olyan mechanizmust valósít meg, amellyel adatokat küldhet a lemezen lévő fájlba. Egyébként az utolsó példában már használtuk. Észrevetted? Átadtuk a DataOutputStreamnek, amely „csomagolóként” működött.BufferedOutputStream
. Pufferelt kimeneti adatfolyam. Itt sincs semmi bonyolult. Célja analógBufferedInputStream
(vagyBufferedReader
). A szokásos szekvenciális adatolvasás helyett egy speciális „halmozott” puffer segítségével ír adatokat. A puffer lehetővé teszi az adatnyelőhöz való hozzáférések számának csökkentését, ezáltal növelve a teljesítményt.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); } }
Ismét játszhat ezzel a kóddal, és ellenőrizheti, hogy működik-e a számítógépén lévő valódi fájlokon.
FileInputStream
, FileOutputStream
és a -ról BuffreredInputStream
, így ez elég információ az első ismerkedéshez. Ez az! Reméljük, megérti az interfészek és az absztrakt osztályok közötti különbségeket, és készen áll válaszolni bármilyen kérdésre, még trükkös kérdésekre is :)
GO TO FULL VERSION