Be- és kimenet kezelése Készítő: Kurdi Zsombor
Bemenet és kimenet absztrakciója Bemenet: konzol, fájl, adatbázis, hálózati kapcsolat stb. Kimenet: képernyő, fájl, hálózati kapcsolat, adatbázis, nyomtató stb. Absztrakció: csatorna (Stream)
Mire jó ez? Ugyanazok a műveletek használhatók minden esetben Jól megszervezett I/O könyvtár (java.io) Célok: Egyszerűség Rugalmasság Kifejezőerő
Példa (Hello World) class Hello { public static void main(String args[]) { System.out.println(”Hello world!”); }
Példa (Ugyanaz fájlba) import java.io.*; class Hello { public static void main(String args[]) throws IOException { FileWriter fw = new FileWriter(”hw.txt”); PrintWriter pw = new PrintWriter(fw); pw.println(”Hello world!”); pw.close(); }
A java.io csomag Sok pici osztály Mindegyik egy kis funkcionalitást valósít meg Könnyen komponálhatók
A java.io csomag rendszerezése Különböző szempontok szerint csoportosíthatjuk az osztályokat Szimmetria (Input és Output) Az osztályhierarchia is követi a logikai tagolást Az osztályok neve sokat elárul (a névkomponensek segítik az osztály hovatartozásának megállapítását)
Az osztályok csoportosítása Irány szerint Bemenet Kimenet Funkció szerint Tárolás módjának specifikálása Extra funkcionalitás hozzáadása Szervezés szerint Bájt Karakter
A csatornák iránya Bemenet Kimenet Szimmetria Amiről olvasni lehet Pl.: InputStream, Reader Kimenet Amire írni lehet Pl.: OutputStream, Writer Szimmetria Pl.: FileInputStream FileReader FileOutputStream FileWriter
A csatornák funkciója A tárolás módja Hol vannak az adatok (honnan olvassuk vagy hova írjuk azokat) Pl.: FileWriter (fájlba írunk) StringReader (string-ből olvasunk) Szabványos műveletek Minimális funkcinalitás
A csatornák funkciója (2) Extra funkcionalitás Mi módon szeretnénk írni/olvasni az adatokat Pl.: PrintWriter (println() metódussal) BufferedReader (bufferelve) Bonyolultabb műveletek Komponálhatók (Szűrők)
Az adatok szervezése Bájtszervezésű Karakterszervezésű A különböző karakterkódolási szabványok figyelmen kívül hagyása InputStream vagy OutputStream Karakterszervezésű A különböző karakterkódolási szabványokból származó eltérések kezelése (ASCII, EBCDIC, ISO Latin-1, ISO Latin-2, Mac, IBM, Unicode stb.) Reader vagy Writer
Karakterszervezésű csatornák Java-ban char és String két bájtos (Unicode) Az operációs rendszerek és a nem Java alkalmazások többségében egy bájtos karakterábrázolást használnak Leképezés a kettő között: karaterkódolási szabvány
Bázis Csatornaosztályok InputStream Bemeneti bájtcsatorna OutputStream Kimeneti bájtcsatorna Reader Bemeneti karaktercsatorna Writer Kimeneti karaktercsatorna
Csatornaosztályok A négy bázisosztály absztakt A leszármazottak valósítják meg a funkcionalitást Pl.: FileInputStream egy olyan InputStream, ami az alap csatornafunkcionalitást fájlokon valósítja meg A leszármazottak neve tükrözi, hogy melyik osztályból származnak Pl.: FileInputStream, FileReader, FileOutputStream, FileWriter stb. Szimmetria
Bemeneti csatornák Csatorna megnyitása Csatorna lezárása Olvasás a csatornáról Csatornán lévő adatok mennyiségének lekérdezése Könyvjelző mechanizmus
Kimeneti csatornák Csatorna megnyitása Csatorna lezárása Írás a csatornára Buffer ürítése (flush)
Csatornák megnyitása A megfelelő osztályú csatorna objektum létrehozásával Pl.: FileInputStream fin = new FileInputStream(”a.txt”); FileWriter fw = new FileWriter(”b.txt”);
Csatornák lezárása A close() művelettel Ne felejtsük el! Kimeneti csatorna esetében magában foglalja a bufferelt adatok tényleges kiírását (flush) is. Pl.: fin.close();
Beolvasás read() műveletek segítségével Majdnem ugyanaz InputStream-ek és Reader-ek esetén Háromfajta read() művelet
read() : InputStream Egy bájt beolvasása int read() throws IOException Egy tömbnyi bájt beolvasása int read(byte[] b) throws IOException Egy résztömbnyi bájt beolvasása int read(byte[] b, int off, int len) throws IOException off-tól len hosszan olvas
read() : Reader int read() throws IOException int read(char[] c) throws IOException int read(char[] c, int off, int len) throws IOException Ugyanaz, csak byte helyett char
Kiírás write() műveletek segítségével Majdnem ugyanaz OutputStream-ek és Writer-ek esetén Szimmetrikus a beolvasó read() műveletekkel Háromfajta alap write() művelet (plusz egy kis kiegészítés)
write() : OutputStream Egy bájt kiírása void write(int i) throws IOException Az i alsó bájtját írja ki Egy tömbnyi bájt beolvasása void write(byte[] b) throws IOException Egy résztömbnyi adat beolvasása void write(byte[] b, int off, int len) throws IOException
write() : Writer void write(int i) throws IOException void write(char[] c) throws IOException void write(char[] c, int off, int len) throws IOException void write(String s) throws IOException void write(String s, int off, int len) throws IOException
Véget ért és üres csatornák Két különböző dolog Üres: lehet, hogy még nem ért véget, csak éppen nem érkezett még meg az adat Véget ért: üres, és üres is marad
Véget ért és üres csatornák (2) Ha véget ért a csatorna, a beolvasás befejeződik, és a visszatérési érték jelzi, hogy nincs több adat Első read: -1 a visszaadott érték Másik két read: a beolvasott adatok számát adja meg Ha üres a csatorna a beolvasó művelet blokkolódik, várja, hogy érkezzen adat
Szűrők Egy meglévő csatorna fölé hozhatunk létre szűrőket Extra funkcionalitás hozzáadása a csatornához (pl.: bufferelés) A szűrő csatorna konstruktorának paraméterként meg kell adni a szűrt csatornát Pl.: FileWriter fw = new FileWriter(”a.txt”); PrintWriter pw = new PrintWriter(fw);
A legfontosabb szűrők Bufferelés Adattípus-értékek olvasása és írása Szöveges formátumban történő kiírás (print)
Bufferelés BufferedInputStream, BufferedReader BufferedOutputStream, BufferedWriter Ezek a csatornák bufferelnek InputStream bin = new BufferedInputStream( new FileInputStream(”a.txt”));
Bináris kiírás DataOutputStream Adattípus-értékek bináris kiírása Ha nem egyszerűen bájtokat szeretnénk kiírni Pl. kiírhatjuk mind a négy bájtot, ami egy float értéket reprezentál DataOutputStream dout = new DataOutputStream( new FileOutputStream(”a.txt”)); dout.writeFloat((float)12.0);
Bináris beolvasás DataInputStream Ugyanaz visszafelé (beolvasunk négy bájtot, ami egy float érték) DataInputStream din = new DataInputStream( new FileInputStream(”a.txt”)); float f = din.readFloat();
Szöveges kiírás PrintStream és PrintWriter Kinyomtathatunk adattípus értékeket és objektumokat is (a toString() metóduson keresztül) System.out.println(”42”); System.out.println(42);
Bájtcsatornák felett definiált karaktercsatornák Kapcsolat a kétféle csatorna között Különböző karakterkódolási szabványok támogatása InputStreamReader és OutputStreamWriter Specifikálhatjuk, hogy melyik karakter melyik bájtra legyen leképezve
Könyvjelző mechanizmus void mark(int readlimit) throws IOException boolean markSupported() void reset() throws IOException Elhelyezhetünk egy könyvjelzőt Ha a csatornaosztály támogatja Visszaugorhatunk ehhez a jelzéshez
Adatelérhetőség-vizsgálat IputStream: int available() Reader: boolean ready() Meg lehet tudni, hogy a beolvasás blokkolódni fog-e. (Az available csak becslést ad)
Adat visszatevése a bemeneti csatornára PushbackReader és PushbackInputStream Művelet: unread
Objektumok tárolása ObjectInputStream, ObjectOutputStream A DataInputStream és DataOutputStream kiterjesztése Nem csak adattípus-értékeket lehet elmenteni/beolvasni, hanem objektumokat is void writeObject(Object o) throws IOException Object readObject() throws IOException
Szerializáció Amikor egy objektumot elmentünk, az általa hivatkozott objektumokat is el kell menteni Rekurzív folyamat A többször is előforduló hivatkozott objektumokat elég egyszer elmenteni
Serializable interfész Ezt a bonyolult feladatot a Java megcsinálja helyettünk Csak a Serializable interfészt kell megvalósítani Nem specifikál műveleteket Csak oda kell írni az osztály definíciójába
Mi történik a szerializációkor? Metainformáció is mentésre kerül Az objektum osztálya, verziószám stb. Mentésre kerülnek az adattagok Mentésre kerülnek a hivatkozott objektumok is (rekurzívan) A már mentett objektumok helyett csak egy mutató kerül mentésre
Beállítások A mentések folyamatát lehet szabályozni (mi kerüljön mentésre az objektum által tárolt adatok közül?) Több szinten van lehetőség a szabályozásra: transient readObject/writeObject felüldefiniálása serialPersistantFields Externalizable
transient Ez egy módosítószó Mint pl.: public, final, abstract stb. Ha egy adattagot a szerializáció során nem szeretnénk menteni, akkor írjuk elé, hogy transient Mikor használjuk? Egyszeri adat (pl.: erőforrás-leíró) Bizalmas adat Redundáns információ
A fájlrendszer fájljaihoz való hozzáférés A File osztályon keresztül A File objektumok fájlneveket reprezentálnak Egy File objektumon keresztül nem lehet olvasni vagy írni a fájlt!
Műveletek File objektumokkal Fájl létrehozása Fájl törlése Átnevezés Olvasható/írható-e a fájl Könyvtár listázása Stb.