Külön leckét szenteltünk az absztrakt osztály és az interfész közötti különbségeknek, mert ez a téma nagyon fontos. A jövőbeli interjúk 90%-ában megkérdezik majd, hogy mi a különbség ezek között a fogalmak között. Ez azt jelenti, hogy biztosan rá kell jönnie, hogy mit olvas. És ha valamit nem értesz teljesen, olvass el további forrásokat. Tehát tudjuk, mi az absztrakt osztály és mi az interfész. Most áttekintjük a különbségeiket.
-
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
Birdabsztrakt osztályt és aCanFlyfelü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
MockingJaymadá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ésagevá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
Birdszü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
CanFlyfelü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 :) InputStreamegy 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 InputStreama szülő, számos olyan módszert biztosít, amelyek megkönnyítik az adatfolyamokkal való munkát. Mindegyik leszármazottja InputStreamrendelkezik 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 InputStreamReaderképes elvégezni a munkát. De BufferedReaderjaví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 OutputStreamosztá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