Savladajte sistem Java IO znanja u 6000 reči
"Lao Wang, Java IO baš ide u glavu, zar ne?" požalio se početnik Xiao Er ćelavom Lao Wongu. "Pogledaj, tek sam podelio IO po načinu prenosa, a već iskopao toliko stvari!"

Već odavno nije radio sa IO-om, pa je i Lao Wang bio iznenađen kad je video ovaj mentalni map. Razmislio je i sam — kada je učio Java IO i njemu je bilo teško: more klasa, sve gušće, verovatno najviše klasa u svim Java paketima; sad Input, sad Output, sad Reader, sad Writer — zaista se ne zna šta su dizajneri Jave mislili.
Gledajući Xiao Er-a kome su pluća skoro pucala od besa, Lao Wang je duboko udahnuo i strpljivo mu rekao: "Pre svega, dizajneri Jave su mnogo toga uzeli u obzir, zato IO deluje haotično. Hajde da ti to razjasnim."
00. Uvod u Java IO
IO, odnosno in i out, znači ulaz i izlaz, i odnosi se na prenos podataka između aplikacije i spoljnih uređaja; uobičajeni spoljni uređaji obuhvataaju fajlove, cevi i mrežne veze.
U Javi se IO obrađuje preko tokova. Šta je zapravo tok?
Tok (Stream) apstraktan je pojam koji označava niz podataka (znakova ili bajtova) i predstavlja kanal koji šalje informacije po principu "prvi ušao, prvi izašao" (FIFO).
Kada program treba da pročita podatke, otvara tok prema izvoru podataka — taj izvor može biti fajl, memorija ili mrežna veza. Slično tome, kada program treba da upiše podatke, otvara tok prema odredištu. Tada možete zamisliti da podaci doslovno "teku" kroz taj tok.
Opšte uzevši, tok ima sledeće osobine:
- FIFO (prvi ušao, prvi izašao): podaci koji se prvi upišu u izlazni tok prvi se i pročitaju u ulaznom toku.
- Sekvencijalni pristup: u tok se može jedan po jedan upisivati niz bajtova, a i čitanje će pratiti redosled upisa; ne može se pristupiti proizvoljnim podacima u sredini. (Izuzetak je RandomAccessFile.)
- Samo čitanje ili samo pisanje: svaki tok je isključivo ulazni ili isključivo izlazni, ne može istovremeno imati obe funkcije; ulazni tok služi samo za čitanje, a izlazni samo za pisanje. Ako u jednom kanalu za prenos podataka želimo i da pišemo i da čitamo, moramo obezbediti dva odvojena toka.
01. Podela po načinu prenosa
Vratimo se na onaj mentalni map koji si nacrtao.
Postoje dva načina prenosa — bajtovi i znakovi — pa prvo moramo razumeti razliku između bajta i znaka, zar ne?
Bajt (bajt) jedinica je za merenje memorijskog kapaciteta u računaru; najčešće jedan bajt ima 8 bitova (bit).
Znak (char) može biti slovo, broj ili simbol koji se koristi u računaru, na primer A, 1, $ i slično.
Obično se jedno slovo ili jedan znak smatra u jednom bajtu, a jedan kineski znak u dva bajta.

Tačno trajanje zavisi od kodiranja znakova. Na primer, u UTF-8 kodiranju jedno englesko slovo (bez obzira na velika/mala) zauzima jedan bajt, a jedan kineski znak tri bajta; u Unicode kodiranju jedno englesko slovo zauzima jedan bajt, a jedan kineski znak dva bajta.
PS: Više o kodiranju znakova potražite u prethodnom poglavlju: Mojebani znakovi pri kodiranju
Kada razumemo razliku između bajta i znaka, biće nam mnogo lakše da razumemo bajtovne i karakterne tokove.
Bajtovni tok služi za obradu binarnih fajlova, na primer slika, MP3 datoteka, videa.
Karakterni tok služi za obradu tekstualnih fajlova; tekstualni fajl se može posmatrati kao posebna vrsta binarnog fajla, samo je kodiran tako da bude čitljiv ljudima.
Drugim rečima, bajtovni tok može obraditi bilo koji fajl, dok karakterni tok može obraditi samo tekst.
Iako ima mnogo IO klasa, suština je svega četiri apstraktne klase: InputStream, OutputStream, Reader i Writer.
(Apstrakcija je zaista sjajna stvar)
Iako IO klase imaju mnogo metoda, suština je samo dve: read i write.
Klasa InputStream
int read(): čita podatkeint read(bajt b[], int off, int len): čita počevši od pozicije off, dužine len bajtova, i smešta ih u niz blong skip(long n): preskače zadati broj bajtovaint available(): vraća broj dostupnih bajtova za čitanjevoid close(): zatvara tok i oslobađa resurse
Klasa OutputStream
void write(int b): upisuje jedan bajt; iako je parametar tipa int, upisuje se samo niskih 8 bitova, a viših 24 bita se odbacuje (o tome više kasnije)void write(bajt b[], int off, int len): upisuje bajtove iz niza b, počevši od pozicije off, dužine lenvoid flush(): prinudno ispisuje podatke iz baferavoid close(): zatvara tok
Klasa Reader
int read(): čita jedan znakint read(char cbuf[], int off, int len): čita počevši od pozicije off, dužine len znakova, i smešta ih u niz blong skip(long n): preskače zadati broj znakovaint ready(): da li je spremno za čitanjevoid close(): zatvara tok i oslobađa resurse
Klasa Writer
void write(int c): upisuje jedan znakvoid write( char cbuf[], int off, int len): upisuje znakove iz niza cbuf, počevši od pozicije off, dužine lenvoid flush(): prinudno ispisuje podatke iz baferavoid close(): zatvara tok
Kada razumemo ove metode, u suštini smo već savladali dušu IO-a.
Razlika između bajtovnog i karakternog toka:
- Bajtovni tok se obično koristi za obradu slika, videa, zvuka, PPT-a, Word-a i sličnih tipova fajlova. Karakterni tok se obično koristi za obradu čistog teksta, kao što su TXT fajlovi, ali ne može obraditi slike, video i druge ne-tekstualne fajlove. Jednom rečenicom: bajtovni tok može obraditi sve, a karakterni tok samo čist tekst.
- Bajtovni tok sam po sebi nema bafer; baferovani bajtovni tok u poređenju sa običnim daje veoma veliki skok efikasnosti. Karakterni tok već ima svoj bafer, pa baferovani karakterni tok u odnosu na obični ne daje toliki skok efikasnosti.
Da bismo to ilustrovali pisanjem u fajl, pogledajmo izvorni kod karakternog toka i uverimo se da zaista koristi bafer:
// deklaracija niza tipa char za upis u izlazni tok
private char[] writeBuffer;
// definicija veličine niza writeBuffer, mora biti >= 1
private static final int WRITE_BUFFER_SIZE = 1024;
// upis dela datog niza znakova u izlazni tok
public void write(String str, int off, int len) throws IOException {
// synchronized blok za bezbednost niti
synchronized (lock) {
char cbuf[];
// ako je len <= WRITE_BUFFER_SIZE, koristi niz writeBuffer
if (len <= WRITE_BUFFER_SIZE) {
// ako je writeBuffer null, kreira novi niz char veličine WRITE_BUFFER_SIZE
if (writeBuffer == null) {
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
cbuf = writeBuffer;
} else { // ako je len > WRITE_BUFFER_SIZE, ne alociraj trajno veoma veliki bafer
// kreira novi niz char veličine len
cbuf = new char[len];
}
// kopira deo niza str (od off, dužine len) u niz cbuf
str.getChars(off, (off + len), cbuf, 0);
// upisuje podatke iz niza cbuf u izlazni tok
write(cbuf, 0, len);
}
}Ovaj kod je metoda write klase OutputStreamWriter iz Java IO biblioteke; vidimo da je veličina bafera 1024 znaka (char).
Uporedimo sada karakterni i bajtovni tok za fajlove — razlika u kodu je minimalna.
// bajtovni tok
try (FileInputStream fis = new FileInputStream("input.txt");
FileOutputStream fos = new FileOutputStream("output.txt")) {
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
// karakterni tok
try (FileReader fr = new FileReader("input.txt");
FileWriter fw = new FileWriter("output.txt")) {
char[] buffer = new char[1024];
int len;
while ((len = fr.read(buffer)) != -1) {
fw.write(buffer, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}02. Podela prema objektu operacije
Xiao Er, razmisli — IO je zapravo samo unos i izlaz (Input/Output):
- Input: podaci se spolja čitaju u memoriju, npr. fajl se s diska pročita u memoriju, ili se podaci preko mreže pročitaju u memoriju itd.
- Output: podaci iz memorije upisuju se spolja, npr. podaci iz memorije upisuju se u fajl, ili iz memorije šalju na mrežu itd.
Svi programi, dok se izvršavaju, rade u memoriji; čim se isključi struja, podaci u memoriji nestaju. Ako želimo da ih sačuvamo trajno, moramo podatke iz memorije izvesti napolje, na primer u fajl.
Operacije nad fajlovima spadaju u najtipičnije i najučestalije IO operacije. Možeš na to gledati i iz drugog ugla — prema objektu IO operacije, IO se može podeliti na: fajlove, nizove, cevi, primitivne tipove podataka, baferovanje, štampu, serijalizaciju/deserijalizaciju objekata i konverziju.

1) Fajlovi
Tokovi ka fajlovima direktno rade sa fajlovima; mogu se podeliti na bajtovne tokove (FileInputStream i FileOutputStream) i karakterne tokove (FileReader i FileWriter).
Primer sa FileInputStream:
// deklaracija promenljive b tipa int za čuvanje pročitanog bajta
int b;
// kreiranje objekta FileInputStream za čitanje podataka iz fajla fis.txt
FileInputStream fis1 = new FileInputStream("fis.txt");
// petlja koja čita podatke iz fajla
while ((b = fis1.read()) != -1) {
// pretvaranje pročitanog bajta u odgovarajući ASCII znak i ispis na konzolu
System.out.println((char)b);
}
// zatvaranje FileInputStream objekta i oslobađanje resursa
fis1.close();Primer sa FileOutputStream:
// kreiranje objekta FileOutputStream za upis podataka u fajl fos.txt
FileOutputStream fos = new FileOutputStream("fos.txt");
// upis podataka u fajl — ovde se upisuje niz bajtova koji odgovara nizu znakova "Chenmo Wang Er"
fos.write("Chenmo Wang Er".getBytes());
// zatvaranje FileOutputStream objekta i oslobađanje resursa
fos.close();Primer sa FileReader:
// deklaracija promenljive b tipa int za čuvanje pročitanog znaka
int b = 0;
// kreiranje objekta FileReader za čitanje podataka iz fajla read.txt
FileReader fileReader = new FileReader("read.txt");
// petlja koja čita podatke iz fajla
while ((b = fileReader.read()) != -1) {
// eksplicitno pretvaranje pročitanog znaka u tip char i ispis na konzolu
System.out.println((char)b);
}
// zatvaranje FileReader objekta i oslobađanje resursa
fileReader.close();Primer sa FileWriter:
// kreiranje objekta FileWriter za upis podataka u fajl fw.txt
FileWriter fileWriter = new FileWriter("fw.txt");
// pretvaranje niza znakova "Chenmo Wang Er" u niz char
char[] chars = "Chenmo Wang Er".toCharArray();
// upis podataka u fajl — ovde se upisuju svi znakovi iz niza chars
fileWriter.write(chars, 0, chars.length);
// zatvaranje FileWriter objekta i oslobađanje resursa
fileWriter.close();Tokovi ka fajlovima mogu se koristiti i za kreiranje, brisanje i preimenovanje fajlova. Drugi parametar konstruktora FileOutputStream i FileWriter može odrediti da li se podaci dodaju na kraj fajla.
Primer koda:
// kreiranje fajla
File file = new File("test.txt");
if (file.createNewFile()) {
System.out.println("Fajl uspešno kreiran");
} else {
System.out.println("Fajl već postoji");
}
// brisanje fajla
if (file.delete()) {
System.out.println("Fajl uspešno obrisan");
} else {
System.out.println("Brisanje fajla nije uspelo");
}
// preimenovanje fajla
File oldFile = new File("old.txt");
File newFile = new File("new.txt");
if (oldFile.renameTo(newFile)) {
System.out.println("Fajl uspešno preimenovan");
} else {
System.out.println("Preimenovanje fajla nije uspelo");
}Kada savladaš ulaz i izlaz za fajlove, ostalo će ti ići lako — sve je slično.
2) Nizovi (memorija)
Za uobičajene operacije čitanja i pisanja nad fajlovima dovoljni su fajl tokovi u kombinaciji sa baferovanim tokovima. Ali da bi se povećala efikasnost, nije preterano dobro često pisati i čitati fajlove, pa su se pojavili nizni tokovi, poznati i kao memorijski tokovi.
Primer sa ByteArrayInputStream:
// kreiranje objekta ByteArrayInputStream za čitanje podataka iz niza bajtova
InputStream is = new BufferedInputStream(
new ByteArrayInputStream(
"Chenmo Wang Er".getBytes(StandardCharsets.UTF_8)));
// definicija niza bajtova za čuvanje pročitanih podataka
byte[] flush = new byte[1024];
// definicija promenljive za čuvanje broja bajtova pročitanih u svakom koraku
int len = 0;
// petlja koja čita podatke iz niza bajtova i ispisuje ih na konzolu
while (-1 != (len = is.read(flush))) {
// pretvaranje pročitanih bajtova u odgovarajući niz znakova i ispis na konzolu
System.out.println(new String(flush, 0, len));
}
// zatvaranje ulaznog toka i oslobađanje resursa
is.close();Primer sa ByteArrayOutputStream:
// kreiranje objekta ByteArrayOutputStream za upis podataka u memorijski bafer
ByteArrayOutputStream bos = new ByteArrayOutputStream();
// definicija niza bajtova sa podacima koje želimo da upišemo u memorijski bafer
byte[] info = "Chenmo Wang Er".getBytes();
// upis podataka u memorijski bafer — ovde se upisuju svi bajtovi iz niza info
bos.write(info, 0, info.length);
// pretvaranje podataka iz memorijskog bafera u niz bajtova
byte[] dest = bos.toByteArray();
// zatvaranje objekta ByteArrayOutputStream i oslobađanje resursa
bos.close();Nizni tokovi služe za čitanje i pisanje podataka u memoriji, npr. za čuvanje podataka u nizu bajtova radi kompresije, šifrovanja, serijalizacije i sl. Njihova prednost je što nije potrebno kreiranje privremenih fajlova, što povećava efikasnost programa. Imaju i manu — mogu čuvati samo ograničenu količinu podataka; ako ih je previše, doći će do prelivanja memorije (OutOfMemoryError).
3) Cevi
Cevi (pipe) u Javi razlikuju se od cevi u Unix/Linux-u. U Unix/Linux-u različiti procesi mogu komunicirati preko cevi, ali u Javi obe strane koje komuniciraju moraju biti u istom procesu, odnosno u istoj JVM; cevi obezbeđuju mogućnost komunikacije između niti.
Podaci koje jedna nit upiše preko PipedOutputStream-a druga nit može pročitati preko povezanog PipedInputStream-a.
// kreiranje objekata PipedOutputStream i PipedInputStream
final PipedOutputStream pipedOutputStream = new PipedOutputStream();
final PipedInputStream pipedInputStream = new PipedInputStream(pipedOutputStream);
// kreiranje niti koja upisuje podatke u PipedOutputStream
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
// pretvaranje niza znakova "Chenmo Wang Er" u niz bajtova i upis u PipedOutputStream
pipedOutputStream.write("Chenmo Wang Er".getBytes(StandardCharsets.UTF_8));
// zatvaranje PipedOutputStream-a i oslobađanje resursa
pipedOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
// kreiranje niti koja čita podatke iz PipedInputStream-a i ispisuje ih na konzolu
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
// definicija niza bajtova za čuvanje pročitanih podataka
byte[] flush = new byte[1024];
// definicija promenljive za čuvanje broja bajtova pročitanih u svakom koraku
int len = 0;
// petlja koja čita podatke i ispisuje ih na konzolu
while (-1 != (len = pipedInputStream.read(flush))) {
// pretvaranje pročitanih bajtova u niz znakova i ispis na konzolu
System.out.println(new String(flush, 0, len));
}
// zatvaranje PipedInputStream-a i oslobađanje resursa
pipedInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
// pokretanje niti 1 i niti 2
thread1.start();
thread2.start();Cevovodni tokovi omogućavaju prenos podataka između različitih niti i mogu se koristiti za komunikaciju i razmenu podataka među nitima. Ipak, imaju i ograničenja — mogu se koristiti samo između niti u istoj JVM, a ne između različitih JVM procesa.
4) Primitivni tipovi podataka
Tokovi za primitivne tipove podataka su bajtovni tokovi koji ne mogu da čitaju i pišu samo bajtove i znakove, već i primitivne tipove podataka.
DataInputStream nudi niz metoda za čitanje primitivnih tipova:
// kreiranje objekta DataInputStream za čitanje podataka iz fajla
DataInputStream dis = new DataInputStream(new FileInputStream("das.txt"));
// čita jedan bajt i pretvara ga u tip byte
byte b = dis.readByte();
// čita dva bajta i pretvara ih u tip short
short s = dis.readShort();
// čita četiri bajta i pretvara ih u tip int
int i = dis.readInt();
// čita osam bajtova i pretvara ih u tip long
long l = dis.readLong();
// čita četiri bajta i pretvara ih u tip float
float f = dis.readFloat();
// čita osam bajtova i pretvara ih u tip double
double d = dis.readDouble();
// čita jedan bajt i pretvara ga u tip boolean
boolean bb = dis.readBoolean();
// čita dva bajta i pretvara ih u tip char
char ch = dis.readChar();
// zatvaranje DataInputStream-a i oslobađanje resursa
dis.close();DataOutputStream nudi niz metoda za pisanje primitivnih tipova:
// kreiranje objekta DataOutputStream za upis podataka u fajl
DataOutputStream das = new DataOutputStream(new FileOutputStream("das.txt"));
// upis podataka tipa byte u fajl
das.writeByte(10);
// upis podataka tipa short u fajl
das.writeShort(100);
// upis podataka tipa int u fajl
das.writeInt(1000);
// upis podataka tipa long u fajl
das.writeLong(10000L);
// upis podataka tipa float u fajl
das.writeFloat(12.34F);
// upis podataka tipa double u fajl
das.writeDouble(12.56);
// upis podataka tipa boolean u fajl
das.writeBoolean(true);
// upis podataka tipa char u fajl
das.writeChar('A');
// zatvaranje DataOutputStream-a i oslobađanje resursa
das.close();Pored DataInputStream i DataOutputStream, Java IO nudi i druge klase tokova za čitanje i pisanje primitivnih tipova i nizova znakova, uključujući ObjectInputStream i ObjectOutputStream (za čitanje i pisanje objekata).
Primer koda:
public static void main(String[] args) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.dat"))) {
Person p = new Person("Zhang San", 20);
oos.writeObject(p);
} catch (IOException e) {
e.printStackTrace();
}
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.dat"))) {
Person p = (Person) ois.readObject();
System.out.println(p);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}Gornji kod kreira objekat Person, upisuje ga u fajl, zatim ga iz fajla čita i ispisuje na konzoli.
5) Baferovanje
CPU je veoma brz — stotinu puta brži od memorije i milion puta brži od diska. To znači da je interakcija programa sa memorijom veoma brza, dok je interakcija sa diskom relativno spora, što može dovesti do problema sa performansama.
Da bi se smanjila interakcija programa sa diskom i povećala efikasnost, uvedeni su baferovani tokovi — to su oni čije ime počinje sa Buffer, na primer BufferedInputStream, BufferedOutputStream, BufferedReader i BufferedWriter.

Baferovani tok u memoriji postavlja bafer; razmena sa memorijom ili diskom odvija se tek kada bafer prikupi dovoljno podataka za obradu. Jednostavno rečeno — čitaj ili piši više odjednom, a ređe, čime rastu performanse programa.
Primer čitanja fajla pomoću BufferedInputStream:
// kreiranje objekta BufferedInputStream za čitanje podataka iz fajla
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("data.txt"));
// kreiranje niza bajtova koji služi kao bafer
byte[] buffer = new byte[1024];
// čitanje podataka iz fajla i smeštanje u bafer
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
// obrada podataka u baferu
// ovde se niz pročitanih bajtova jednostavno pretvara u niz znakova i ispisuje
System.out.println(new String(buffer, 0, bytesRead));
}
// zatvaranje BufferedInputStream-a i oslobađanje resursa
bis.close();U gornjem kodu prvo je kreiran objekat BufferedInputStream za čitanje podataka iz fajla. Zatim je kreiran niz bajtova kao bafer, u koji se pri svakom čitanju smeštaju podaci. Čitanje se obavlja u while petlji, a nakon svakog čitanja obrađuju se podaci u baferu. Na kraju se BufferedInputStream zatvara i resursi oslobađaju.
Primer pisanja u fajl pomoću BufferedOutputStream:
// kreiranje objekta BufferedOutputStream za upis podataka u fajl
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("data.txt"));
// kreiranje niza bajtova kao bafera
byte[] buffer = new byte[1024];
// upis podataka u fajl
String data = "Chenmo Wang Er je stvarno velika budala!";
buffer = data.getBytes();
bos.write(buffer);
// ispiranje bafera — podaci iz bafera upisuju se u fajl
bos.flush();
// zatvaranje BufferedOutputStream-a i oslobađanje resursa
bos.close();U gornjem kodu prvo je kreiran objekat BufferedOutputStream za upis podataka u fajl. Zatim je kreiran niz bajtova kao bafer u koji se upisuju podaci. Upisivanje se obavlja metodom write(), kojoj se kao argument prosleđuje niz bajtova.
Na kraju, metodom flush() podaci iz bafera upisuju se u fajl. Prilikom pisanja, pošto se koristi BufferedOutputStream, podaci se najpre upisuju u bafer, a u fajl tek kada se bafer popuni ili kada se pozove flush().
Primer čitanja fajla pomoću BufferedReader:
// kreiranje objekta BufferedReader za čitanje podataka iz fajla
BufferedReader br = new BufferedReader(new FileReader("data.txt"));
// čitanje podataka iz fajla i smeštanje u niz znakova
String line;
while ((line = br.readLine()) != null) {
// obrada pročitanih podataka
// ovde se svaki pročitani red jednostavno ispisuje
System.out.println(line);
}
// zatvaranje BufferedReader-a i oslobađanje resursa
br.close();U gornjem kodu prvo je kreiran objekat BufferedReader za čitanje podataka iz fajla. Zatim se metodom readLine() čitaju podaci iz fajla, pri čemu se svaki put pročita jedan red i smesti u niz znakova. Čitanje se obavlja u while petlji.
Primer pisanja u fajl pomoću BufferedWriter:
// kreiranje objekta BufferedWriter za upis podataka u fajl
BufferedWriter bw = new BufferedWriter(new FileWriter("data.txt"));
// upis podataka u fajl
String data = "Chenmo Wang Er, zaista zgodan";
bw.write(data);
// ispiranje bafera — podaci iz bafera upisuju se u fajl
bw.flush();
// zatvaranje BufferedWriter-a i oslobađanje resursa
bw.close();U gornjem kodu prvo je kreiran objekat BufferedWriter za upis podataka u fajl. Zatim se metodom write() podaci upisuju u bafer; postupak je sličan kao kod FileWriter. Treba imati na umu da prilikom pisanja preko BufferedWriter-a podaci idu prvo u bafer, a u fajl tek kada se bafer popuni ili kada se pozove flush().
Na kraju, metodom flush() podaci iz bafera upisuju se u fajl, a metodom close() BufferedWriter se zatvara i resursi oslobađaju.
Baferovani tokovi mogu znatno da povećaju efikasnost čitanja i pisanja smanjivanjem broja pristupa disku ili mreži, čime rastu performanse programa. Ipak, prilikom korišćenja treba obratiti pažnju na veličinu bafera i trenutak njegovog pražnjenja, kako ne bi došlo do gubitka ili nepotpunosti podataka.
6) Štampa
Java tokovi za štampu skup su klasa za ispis podataka, uključujući dve klase: PrintStream i PrintWriter.
Verovatno su tokovi za štampu najčešće korišćene klase u životu Java programera: System.out zapravo vraća objekat klase PrintStream kojim se mogu ispisivati različiti objekti.
System.out.println("Chenmo Wang Er je zaista prava budala!");PrintStream na kraju ispisuje bajtne podatke, dok PrintWriter proširuje interfejs Writer, pa njegove metode print()/println() na kraju ispisuju karakterne podatke. U upotrebi je gotovo identičan PrintStream-u.
StringWriter buffer = new StringWriter();
try (PrintWriter pw = new PrintWriter(buffer)) {
pw.println("Chenmo Wang Er");
}
System.out.println(buffer.toString());7) Serijalizacija/deserijalizacija objekata
Serijalizacija u suštini znači pretvaranje Java objekta u niz bajtova, koji se zatim može sačuvati u fajl ili preneti preko mreže na udaljenu stranu.
// kreiranje objekta ByteArrayOutputStream buffer za čuvanje podataka
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
// try-with-resources naredba kreira objekat ObjectOutputStream output i povezuje ga sa bufferom
try (ObjectOutputStream output = new ObjectOutputStream(buffer)) {
// metoda writeUTF() upisuje niz znakova "Chenmo Wang Er" u bafer
output.writeUTF("Chenmo Wang Er");
}
// metoda toByteArray() pretvara podatke iz bafera u niz bajtova i ispisuje na konzolu
System.out.println(Arrays.toString(buffer.toByteArray()));Suprotno serijalizaciji stoji deserijalizacija — proces pretvaranja niza bajtova nazad u Java objekat.
try (ObjectInputStream input = new ObjectInputStream(new FileInputStream(
new File("Person.txt")))) {
String s = input.readUTF();
}Ovaj kod prvenstveno koristi Java klase ByteArrayOutputStream i ObjectOutputStream da upišu niz znakova "Chenmo Wang Er" u memorijski bafer (niz bajtova), a zatim podatke iz bafera pretvaraju u niz bajtova i ispisuju na konzoli.
Konkretni tok izvršenja je sledeći:
- Kreira se objekat ByteArrayOutputStream buffer za čuvanje podataka.
- try-with-resources naredbom kreira se objekat ObjectOutputStream output i povezuje sa bufferom.
- Metodom writeUTF() niz znakova "Chenmo Wang Er" upisuje se u bafer.
- Kada se try-with-resources blok završi, automatski se poziva metoda close() objekta output, čime se izlazni tok zatvara i resursi oslobađaju.
- Metodom toByteArray() podaci iz bafera pretvaraju se u niz bajtova.
- Metodom Arrays.toString() niz bajtova pretvara se u niz znakova i ispisuje na konzoli.
8) Konverzija
InputStreamReader je most od bajtovnog toka ka karakternom toku; on pomoću zadatog skupa znakova čita bajtove i dekodira ih u znakove.
// kreiranje objekta InputStreamReader isr; FileInputStream čita sadržaj fajla demo.txt i pretvara ga u karakterni tok
InputStreamReader isr = new InputStreamReader(new FileInputStream("demo.txt"));
// kreiranje niza char cha za čuvanje pročitanih znakova; 1024 je dužina niza
char[] cha = new char[1024];
// metoda read() čita podatke iz isr-a i smešta ih u niz cha; povratna vrednost len označava broj pročitanih znakova
int len = isr.read(cha);
// deo niza cha od indeksa 0 dužine len pretvara se u niz znakova i ispisuje na konzoli
System.out.println(new String(cha, 0, len));
// zatvaranje objekta InputStreamReader isr i oslobađanje resursa
isr.close();Ovaj kod prvenstveno koristi Java klase InputStreamReader i FileInputStream: iz fajla demo.txt čita podatke, pretvara ih u karakterni tok, smešta pročitane znakove u niz znakova i na kraju ispisuje rezultat pretvoren u niz znakova na konzoli.
OutputStreamWriter pretvara izlazni objekat karakternog toka u izlazni objekat bajtovnog toka; on je most od karakternog ka bajtovnom toku.
// kreiranje objekta File f koji predstavlja fajl test.txt
File f = new File("test.txt");
// kreiranje objekta OutputStreamWriter out; FileOutputStream upisuje podatke u fajl f, a bajtovni tok se pretvara u karakterni
Writer out = new OutputStreamWriter(new FileOutputStream(f));
// metoda write() upisuje niz znakova "Chenmo Wang Er!!" u fajl f
out.write("Chenmo Wang Er!!");
// zatvaranje objekta Writer out i oslobađanje resursa
out.close();Konverzioni tokovi omogućavaju zgodno pretvaranje između bajtovnih i karakternih tokova. Pri čitanju i pisanju tekstualnih fajlova obično se koriste karakterni tokovi, dok se pri mrežnom prenosu ili komunikaciji sa uređajima najčešće koriste bajtovni tokovi.
Pored toga, prilikom korišćenja konverzionih tokova treba obratiti pažnju na pitanje kodiranja znakova. Ako se kodiranje ne zada, koristi se podrazumevano, što može dovesti do neprepoznatljivih znakova (mojeba). Zato se preporučuje da uvek zadata tačno kodiranje, kako biste izbegli takve probleme.
"Vidiš, Xiao Er", nastavio je Lao Wang, "kada se ovako sredi, uopšte nema mnogo stvari u IO-u! Za različite scenarije i poslovne potrebe izabereš odgovarajući tok, a u upotrebi se sve svodi na čitanje i pisanje." Rekavši to u jednom dahu, Lao Wang je dugo i s olakšanjem izdahnuo.
Xiao Er je još uvek bio zanesen bujicom reči. Ne samo da je pomislio kako je Lao Wang zaista izdržljiv — plućni kapacitet mu je ogroman — već je i zaključio da stari kadar sa preko deset godina iskustva zaista zaslužuje svoju titulu: u jednom dahu mu je razjasnio onaj IO zbog koga mu je pučila glava.
Ako ti se ovaj članak svideo, lajkuj i prati
