// casual input stream
InputStream is = System.in;
// bis enhances stream with buffering option
BufferedInputStream bis = new BufferedInputStream(is);
Tomáš Pitner, Radek Ošlejšek, Marek Šabo
V/V operace jsou založeny na vstupně/výstupních proudech (streams).
Tím pádem je možno značnou část logiky programu psát nezávisle na tom, o který konkrétní typ V/V zařízení jde.
Současně s tím jsou díky tomu V/V operace plně platformově nezávislé.
typ dat | vstupní | výstupní |
binární | InputStream | OutputStream |
znakové | Reader | Writer |
Proudy jsou koncipovány jako "stavebnice" — lze je spojovat za sebe a tím přidávat vlastnosti:
// casual input stream
InputStream is = System.in;
// bis enhances stream with buffering option
BufferedInputStream bis = new BufferedInputStream(is);
Neplést si streamy (proudy dat) s lambda streamy! |
Jedná se o použití návrhové vzoru Decorator:
"Nízkoúrovňové" třídy (např. FileReader
) odpovídají třídě ConcreteComponent
ze vzoru a poskytují základní funkcionalitu.
"Obalující" třídy, jejichž konstruktor vyžaduje již existující proud (např. BuferedReader
) jsou dekorátory,
které se "předsadí" před původní objekt a poskytují dodatečné metody. Na pozadí přitom komunikují s původním
objektem.
Klientský kód může komunikovat jak s dekorátorem, tak přímo s původním objektem.
Closable | bytes closable | characters closable | lines closable |
Input | InputStream | InputStreamReader | BufferedReader |
Output | OutputStream | OutputStreamWriter | BufferedWriter |
Základem znakových vstupních proudů je abstraktní třída Reader (pro výstupní Writer).
Ze vstupního binárního proudu InputStream (čili každého) je možné vytvořit znakový Reader.
Ale pozor. Jedná se dvě různé hierarchie. Nelze tedy například vytvořit binární proud a konvertovat ho na buffered reader:
FileInputStream is = new FileInputStream("file.txt");
BufferedReader reader = new BufferedReader(is); // Syntax error - incompatible type of is
Musí se použít k tomu určená třída InputStreamReader
(obdobně pro výstupní proudy OutputStreamWriter
).
// binary input stream
InputStream is = ...
// character stream, decoding uses standard charset
Reader reader = new InputStreamReader(is);
// charsets are defined in java.nio package
Charset charset = java.nio.Charset.forName("ISO-8859-2");
// character stream, decoding uses ISO-8859-2 charset
Reader reader2 = new InputStreamReader(is, charset);
Podporované názvy znakových sad naleznete na webu IANA Charsets .
Na zjištění, jestli je možné z čtenáře číst, se používa metoda reader.ready() . |
InputStreamReader
(i OutputStreamWriter
) implementuje návrhový vzor Bridge:
Hierarchie tříd InputStream
představuje implementaci čtení binárních dat ze vstupních proudů.
InputStreamReader
pak převádí tato binární data do abstrakce textových dat.
InputStreamReader isr = new InputStreamReader(is);
// takes another Reader and makes it bufferable
BufferedReader br = new BufferedReader(isr);
// BufferedReader supports read by line
String firstLine = br.readLine();
String secondLine = br.readLine();
Jedná se o protějšky k vstupním proudům, názvy jsou konstruovány analogicky (např. FileReader → FileWriter).
Místo generických metod read mají write(…).
OutputStream os = System.out;
os.write("Hello World!");
// we have to use generic newline separator
os.write(System.lineSeparator());
// bw has special method for that
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
bw.newline();
Soubory zavíráme vždy.
Proudy nezavíráme.
Když zavřeme System.out
, metoda println
pak přestane vypisovat text.
Při otevření souboru (a konverzi na proud) se musíme postarat o dodatečné uzavření souboru.
Před Java 7 se to muselo dělat blokem finally:
public String readFirstLine(String path) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(path));
try {
return br.readLine();
} finally {
if (br != null) br.close();
}
}
Nově sa dá použít tzv. try-with-resources:
public String readFirstLine(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
Pomocí try-with-resources lze ošetřit i více proudů současně — zavřou se pak všechny.
try (
ZipFile zf = new ZipFile(zipFileName);
BufferedWriter writer = new BufferedWriter(outputFilePath, charset)
) {
...
}
Obecně lze do hlavičky try-with-resources dát nejen proud, ale cokoli, co implementuje java.io.Closeable. |
je typ proudu standardního výstupu System.out (a chybového System.err).
Vytváří se z binárního proudu, lze jím přenášet i binární data.
Většina operací nevyhazuje výjimky, čímž uspoří neustálé hlídání (try-catch).
Na chybu se lze zeptat pomocí checkError().
pro znaková data
Vytváří se ze znakového proudu, lze specifikovat kódování.
Příklad s nastavením kódování:
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, "UTF-8"));
Třída java.io.Scanner
- pro čtení z obecného proudu (ale i stdin)
Nebo třída java.io.Console
- přímo vždy z konzoly
Čtení z konzoly je typické pro aplikace spouštěné z příkazové řádky a není tudíž vždy možné - např. když spouštíme na serveru, v cloudu… |
Scanner
Console
Postupy ukládání a rekonstrukce objektů:
postup, jak z objektu vytvořit sekvenci bajtů perzistentně uložitelnou na paměťové médium (disk) a později restaurovatelnou do podoby výchozího javového objektu.
je právě zpětná rekonstrukce objektu
Aby objekt bylo možno serializovat, musí implementovat prázdné rozhraní java.io.Serializable. |
Proměnné objektu, které nemají být serializovány, musí být označeny modifikátorem, klíčovým slovem, transient.
Pokud požadujeme "speciální chování" při (de)serializaci, musí objekt definovat metody:
private void writeObject(ObjectOutputStream stream) throws IOException
private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException
ObjectInputStream je proud na čtení serializovaných objektů.
(De)serializace souvisí návrhovým vzorem Memento:
Vzor umožňuje odložit si aktuální stav objektu a později ho obnovit.
(De)serializace v Javě tak může sloužit ke snadné implementaci tohoto vzoru.