5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 1
5. Strumienie wej
ś
cia – wyj
ś
cia
5.1 Klasy strumieni WE-WY
5.2 Korzystanie ze strumieni
5.3 Serializacja obiektów
5.4 Pliki o dost
ę
pie swobodnym
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 2
5.1 Klasy strumieni WE-WY
Klasy w pakiecie
java.io
słu
żą
do obsługi
strumieni wej
ś
cia - wyj
ś
cia
(które mog
ą
zosta
ć
otwarte dla
pliku, pami
ę
ci, gniazdka,
itd., a nast
ę
pnie
odczytane lub zapisane sekwencyjnie).
Strumień danych
ODCZYT
Ź
ródło
Strumień danych
ZAPIS
Cel
Bez wzgl
ę
du na rodzaj strumienia sposób współpracy jest ten sam:
Odczyt
Zapis
OTWÓRZ
while JEST_INFORMACJA
ODCZYT
ZAMKNIJ_STRUMIEŃ
OTWÓRZ
while JEST_INFORMACJA
ZAPIS
ZAMKNIJ_STRUMIEŃ
Podział klas we-wy w
java.io
- ze wzgl
ę
du
na dane
na których operuj
ą
:
znakowe strumienie
i
bajtowe strumienie.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 3
1) Strumienie znakowe
Reader, Writer
to abstrakcyjne klasy bazowe dla strumieni znakowych
(zawieraj
ą
implementacje cz
ęś
ci metod).
Klasy pochodne od
Reader
i
Writer
:
- dla odczytu/zapisu danych (na szarym tle)
- wykonuj
ą
ce pewne przetwarzanie (na białym tle).
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 4
2) Strumienie bajtowe
InputStream, OutputStream
- bazowe klasy dla odczytu/zapisu 8-
bitowych
binarnych
danych.
Klasy pochodne
od
InputStream, OutputStream
:
- dla odczytu/zapisu danych (na szarym tle)
- wykonuj
ą
ce pewne przetwarzanie (na białym tle).
-
ObjectInputStream, ObjectOutputStream
- klasy przeznaczone do
deserializacji i serializacji obiektu
.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 5
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 6
3) Klasa
Reader
Klasa
Reader
zawiera metody dla
odczytu
znaków i tablic
znaków
.
// Deklaracja klasy i jej składowych
public abstract class Reader extends Object {
// Pole dla synchronizacji dost
ę
pu
protected Object lock;
// Zamiast obiektu klasy
Reader
synchronizacj
ę
dost
ę
pu w
ą
tków do obiektu strumieniowego zapewni jego pole.
// Konstruktory
protected Reader();
// Konstruktor strumienia znakowego dla odczytu -
// synchronizacja na obiekcie klasy.
protected Reader(Object lock);
// - synchronizacja na zadanym obiekcie.
// Metody steruj
ą
ce stanem i informuj
ą
ce o stanie strumienia
abstract void close();
// Zamyka strumień.
void mark (int readAheadLimit);
// Wstawia znacznik w aktualnej pozycji.
boolean markSupported();
//Informuje czy strumień posiada operację mark
boolean ready ();
// Czy strumień jest gotowy do odczytu
.
void reset ();
// Powróć do ostatniego znacznika.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 7
long skip (long n);
// Pomiń n znaków.
// 3 wersje metody read()
int read ();
// Czytaj pojedynczy znak.
int read (char[] cbuf);
// Wczytaj znaki do tablicy.
abstract int read (char[] cbuf, int off, int len);
// Wczytaj znaki do tablicy w
// określone miejsce
.
}
4) Klasa
InputStream
Klasa
InputStream
posiada metody dla odczytu bajtów i tablic bajtów.
// Deklaracja klasy i jej składowych.
public abstract class InputStream extends Object {
public InputStream();
// Konstruktor
// 3 wersje metody read()
public abstract int read() throws IOException;
// Metoda abstrakcyjna
public int read(byte[] b) throws IOException;
// 2
public int read(byte[] b, int offset, int length) throws IOException;
// 3
// Wersje 2 i 3 s
ą
zdefiniowane.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 8
public void close() throws IOException;
// Zamyka strumie
ń
i zwalnia
// jego zasoby - metoda musi by
ć
nadpisana.
//
// Sterowanie pozycj
ą
znacznika w strumieniu.
public long skip(long n) throws IOException;
// Przeskoczy
ć
pewn
ą
// liczb
ę
bajtów – metoda musi by
ć
nadpisana
public int available() throws IOException;
// Zwraca liczb
ę
bajtów, które
// mog
ą
zosta
ć
odczytane bez blokowania strumienia - metod
ę
trzeba
// nadpisa
ć
.
public void reset() throws IOException;
// Powraca do ostatniego
// znacznika - metoda musi zosta
ć
nadpisana w klasach pochodnych.
public void mark(int readlimit);
// Wstawia znacznik i ustawia limit
// odczytu - metoda musi zosta
ć
nadpisana w klasach pochodnych
public boolean markSupported();
// Sprawdza czy istniej
ą
mark()
i
reset()
}
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 9
5) Klasa
Writer
Klasa
Writer
posiada metody dla zapisu znaków i tablic znaków.
public abstract class Writer extends Object {
// Konstrukcja i synchronizacja
protected Objeck lock;
// Zamiast obiektu synchronizację zapewni jego pole
.
protected Writer();
// Konstrukcja strumienia samo-synchronizowanego.
protected Writer(Object lock
);//Konstrukcja strumienia synchronizowanego
// przekazanym obiektem.
// Sterowanie stanem
abstract void close();
// Wykonaj flush() i zamknij strumień
.
abstract void flush();
// Zapisz wszystko co jest w buforze do strumienia.
// 5 wersji metody
write()
void write(char[] cbuf);
// Zapisz tablicę znaków.
abstract void write(char[] cbuf, int off, int len);
// Zapisz część tablicy
void write (int c);
// Zapisz pojedynczy znak.
void write(String str);
// Zapisz string.
void write(String str, int off, int len);
// Zapisz część string-a.
}
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 10
6) Klasa
OutputStream
Klasa
OutputStream
posiada metody zapisu dla bajtów.
public abstract class OutputStream extends Object {
// Konstruktor
public OutputStream();
// Sterowanie stanem
void close();
// Zamknij strumień, zwolnij jego zasoby systemowe.
void flush();
// Wypełnij strumień i wypisz wszystko z bufora.
// 3 wersje metody write()
void write (byte[] b);
// Wypisz liczbę (b.length) bajtów z tablicy b
.
void write (byte[] b, int off, int len);
// Wypisz liczbę (len) bajtów z tablicy.
abstract void write (int b);
// Wypisz podany bajt.
}
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 11
7) Otwarcie i zamkni
ę
cie strumienia
Otwarcie strumienia:
automatycznie w chwili utworzenia obiektu strumienia typu
Reader,
Writer, InputStream, OutputStream
.
Zamkni
ę
cie strumienia:
wykonanie metody
close
strumienia, lub poleganie na "
Garbage
Collector
".
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 12
5.2 Korzystanie ze strumieni
1) Strumienie plikowe (
FileReader, FileWriter, FileInputStream,
FileOutputStream
)
Mo
ż
na utworzy
ć
strumie
ń
plikowy:
dla pliku przekazanego w postaci napisu (
string)
,
dla obiektu klasy
File
lub
dla obiektu klasy
FileDescription
.
Przykład.
Wykorzystanie klas
FileReader
i
FileWriter
dla skopiowania
zawarto
ś
ci pliku
plikWE.txt
do pliku
plikWY.txt
.
import java.io.*;
public class PrzykladKopiowaniaPlikow {
public static void main(String[] args) throws IOException {
File inputFile = new File("plikWE.txt");
// Obiekt klasy File
File outputFile = new File("plikWY.txt");
// Obiekt klasy File
FileReader in = new FileReader(inputFile);
// Utwórz "FileReader-a"
FileWriter out = new FileWriter(outputFile);
// Utwórz "FileWriter-a"
int c;
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 13
while ((c = in.read()) != -1)
out.write(c);
// Odczyt z pliku we. i zapis do pliku wy.
in.close();
// Zamknij plik wejściowy
out.close();
// Zamknij plik wyjściowy
}
}
2) Pochodne klasy strumieniowe w
java.io
Typ we-wy
Klasy strumieni
Opis
CharArrayReader
CharArrayWriter
ByteArrayInputStream
ByteArrayOutputStream
Strumieniowy odczyt/zapis z /do tablicy
w pami
ę
ci programu.
Pami
ęć
StringReader
StringWriter
StringBufferInputStream
StringReader
- odczyt znaków ze
String
-
u w pami
ę
ci.
StringWriter
- zapis
znaków z wewn
ę
trznego
StringBuffer
do
String
-a.
StringBufferInputStream
-
odczyt bajtów ze
StringBuffer
.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 14
Plik
FileReader
FileWriter
FileInputStream
FileOutputStream
Odczyt/zapis z/do pliku zewn
ę
trznego.
Potok
PipedReader
PipedWriter
PipedInputStream
PipedOutputStream
Potoki przeprowadzaj
ą
wyj
ś
cie z
jednego strumienia w wej
ś
cie dla
drugiego.
Konkate-
nacja
znakowe - N/A
SequenceInputStream
Ł
ą
czy wiele strumieni wej
ś
ciowych w
jeden.
Serializacja
obiektu
znakowe - N/A
ObjectInputStream
ObjectOutputStream
Utrwalenie stanu obiektu i odczytanie
wcze
ś
niej utrwalonego stanu obiektu.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 15
Konwersja
danych
znakowe - N/A
DataInputStream
DataOutputStream
Odczyt/zapis prostych typów danych
do formatu niezale
ż
nego od maszyny.
Zliczanie
LineNumberReader
LineNumberInputStream
Zlicza liczb
ę
wierszy podczas odczytu.
Podgl
ą
d
wprzód
PushbackReader
PushbackInputStream
Strumienie wej
ś
ciowe z buforem
stosowym - umo
ż
liwia podgl
ą
d wprzód.
Drukowanie
PrintWriter
PrintStream
Zawieraj
ą
metody dla drukowania.
Buforowanie
BufferedReader
BufferedWriter
BufferedInputStream
BufferedOutputStream
Buforowane strumienie we/wy.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 16
Filtracja
FilterReader
FilterWriter
FilterInputStream
FilterOutputStream
Klasy abstrakcyjne definiuj
ą
ce interfejs
dla strumieni filtrujacych -
wzmacniaj
ą
cych odczyt lub zapis.
Konwersja
pomi
ę
dzy
bajtami a
znakami
InputStreamReader
OutputStreamWriter
InputStreamReader
- odczytuje bajty z
InputStream
i zamienia je na znaki.
OutputStreamWriter
- konwertuje znaki
na bajty i zapisuje je w
OutputStream
.
System.getProperty("file.encoding")
- daje
domy
ś
lny sposób kodowania znaków.
Własny system kodowania znaków:
Kodowanie 16-bitowe znaków odbywa si
ę
według domy
ś
lnego systemu:
System.getProperty("file.encoding")
;
// Uzyskanie informacji o domy
ś
lnym
// sposobie kodowania znaków.
Własny sposób kodowania:
utworzy
ć
obiekt klasy
OutputStreamWriter
pracuj
ą
cy na strumieniu
FileOutputStream
i poda
ć
sposób kodowania.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 17
3) Strumienie "potokowe - przesyłowe" (
PipedReader, PipedWriter
)
Niech klasa posiada metody słu
żą
ce do przetwarzania tekstów, takie jak
sortowanie słów i odwracanie słów.
Bez strumieni przesyłowych
wyniki
po
ś
rednie dla kolejnych wywoła
ń
tych metod zapisywane musiałyby by
ć
w pami
ę
ci programu lub pliku, np.:
Przy istnieniu
strumieni przesyłowych
wyj
ś
cie z jednej metody mo
ż
e
bezpo
ś
rednio stanowi
ć
wej
ś
cie dla drugiej metody:
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 18
Przykład.
U
ż
ycie klas
PipedReader
i
PipedWriter
dla poł
ą
czenia wej
ś
cia i wyj
ś
cia
pewnych metod
reverse
i
sort
w celu przetworzenia listy słów.
Mo
ż
liwe b
ę
dzie poł
ą
czone wywołanie obu metod:
// Utworzenie obiektu strumienia wej
ś
ciowego:
FileReader words = new FileReader("words.txt");
// Przetwarzanie potokowe i przekazanie wyniku - obiektu klasy
Reader:
Reader processedWords = reverse(sort(reverse(words)));
W tym celu wymagane s
ą
odpowiednie definicje
reverse()
i
sort(
).
Potok
W ka
ż
dej z tych metod zostan
ą
utworzone i poł
ą
czone ze sob
ą
dwa
obiekty: (1) obiekt klasy
PipedReader
"osadzony" zostaje na (2) obiekcie
klasy
PipedWriter
- wszystko to co zostanie zapisane do obiektu klasy
PipedWriter
mo
ż
e zosta
ć
odczytane przez obiekt klasy
PipedReader
.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 19
// Definicja metody
reverse()
mo
ż
e by
ć
nast
ę
puj
ą
ca:
public static Reader reverse(Reader source) throws IOException {
// Buforuj strumie
ń
wej
ś
ciowy b
ę
d
ą
cy argumentem metody
BufferedReader in = new BufferedReader(source);
// source
in
// Utwórz i powi
ąż
ze sob
ą
2 obiekty "potokowe"
PipedWriter pipeOut = new PipedWriter();
// 1-szy obiekt
PipedReader pipeIn = new PipedReader(pipeOut);
// 2-gi obiekt:
// pipeOut
pipeIn
// Strumie
ń
dla wydruku przeka
ż
e dane do
pipeOut :
PrintWriter out = new PrintWriter(pipeOut);
// out
pipeOut
// Wła
ś
ciwe przetwarzanie odb
ę
dzie si
ę
w nowym w
ą
tku
new ReverseThread(out, in).start();
// Wątek do odwracania kolejności:
// in
(odwróć) out
return pipeIn;
// Metoda zwraca obiekt potokowy
pipeIn
typu
//
PipedReader
powi
ą
zany ze stworzonym obiektem
pipeOut
.
}
Metoda
sort
jest prawie identyczna z
reverse
z t
ą
ró
ż
nic
ą
,
ż
e tworzy i
woła obiekt pewnej klasy
SortThread
.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 20
"Opakowanie" strumienia
W powy
ż
szym przykładzie metoda
reverse
zawiera m.in. dwie dalsze
instrukcje powi
ą
zania obu obiektów strumieni potokowych z dalszymi
strumieniami (typu
BufferedReader
i
PrintWriter
), w celu skorzystania z
dogodnych metod tych klas do odczytu i zapisu wierszy znaków.
BufferedReader in = new BufferedReader(source);
// source
in
Nast
ę
puje tu otwarcie strumienia typu
BufferedReader
dla
ź
ródła, które
jest
strumieniem
wej
ś
ciowym,
jednak
o
innym
typie.
W
ą
tek
przetwarzaj
ą
cy czyta z obiektu typu
BufferedReader
, który z kolei czyta
ze
strumienia
ź
ródłowego.
Dzi
ę
ki
"opakowaniu"
ź
ródła
klas
ą
BufferedReader
w
ą
tek mo
ż
e zastosowa
ć
metod
ę
readLine
tej klasy.
...
PrintWriter out = new PrintWriter(pipeOut);
// out
pipeOut
Podobnie obiekt typu
PipedWriter
zostaje opakowany przez obiekt typu
PrintWriter
i program mo
ż
e zastosowa
ć
dogodn
ą
metod
ę
println
klasy
PrintWriter
.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 21
4) Ł
ą
czenie (konkatenacja) plików (
SequenceInputStream
)
Przykład.
U
ż
ycie klasy
SequenceInputStream
dla poł
ą
czenia plików w
jeden strumie
ń
odpowiednio do ich kolejno
ś
ci w wywołaniu programu.
// Klasa główna programu:
import java.io.*;
public class PolaczenieStrumieni {
public static void main(String[] args) throws IOException {
// Najpierw tworzony jest obiekt klasy
ListaPlikow
o nazwie
mylist
// inicjalizowany list
ą
plików podan
ą
w linii wywołania programu.
// Klasa
ListPlikow
musi implementowa
ć
interfejs
Enumeration
, czyli
// posiada
ć
m.in. metod
ę
nextElement
().
ListaPlikow mylist = new ListaPlikow(args);
// Konstruktor klasy
SequenceInputStream
inicjalizuje obiekt
// sekwencj
ą
obiektów typu
InputStream
tworzonych wywołaniami
//metody
nextElement
dla obiektu klasy implementuj
ą
cej
Enumeration
SequenceInputStream str = new SequenceInputStream(mylist);
int bajt;
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 22
// Metoda
main
po utworzeniu obiektu klasy
SequenceInputStream
,
// odczytuje z niego bajt po bajcie.
while ((bajt = str.read()) != -1)
System.out.write( bajt );
str.close();
}
}
Klasa
ListaPlików
mo
ż
e by
ć
zdefiniowana nast
ę
puj
ą
co:
import java.util.*;
import java.io.*;
public class ListPlikow implements Enumeration {
private String[] listOfFiles;
private int current = 0;
public ListPlikow(String[] listOfFiles) {
this.listOfFiles = listOfFiles;
}
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 23
public boolean hasMoreElements() {
if (current < listOfFiles.length)
return true;
else
return false;
}
// Metoda
nextElement
klasy
ListaPlikow
tworzy strumie
ń
typu
// FileInputStream
dla nast
ę
pnej nazwy pliku na li
ś
cie i zwraca ten
// obiekt. Je
ś
li lista nazw wyczerpie si
ę
to zwracane jest
null
.
public Object nextElement() {
// Zwróci obiekt typu
FileInputStream
InputStream in = null;
if (!hasMoreElements() )
throw new NoSuchElementException("Brak dalszych plików.");
else {
String nextElement = listOfFiles[current];
current++;
try {
in = new FileInputStream(nextElement);
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 24
}
catch (FileNotFoundException e) {
System.err.println("ListaPlikow:Nie można otworzyć " +
nextElement);
}
}
return in;
}
}
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 25
5) Strumienie filtruj
ą
ce
(FilterInputStream, FilterOutputStream,
FilterReader, FilterWriter
)
Strumie
ń
filtruj
ą
cy wymaga doł
ą
czenia do niego innego (obrabianego)
strumienia. Metoda
read
strumienia filtruj
ą
cego wczytuje dane z
podległego strumienia, filtruje je i przekazuje jako swój wynik.
Metoda
write
filtruje przekazywane jej dane i zapisuje tak przetworzone
dane do podległego strumienia.
W pakiecie
java.io
zdefiniowano bajtowe klasy bazowe
FilterInputStream
i
FilterOutputStream
i szereg ich klas pochodnych:
•
DataInputStream, DataOutputStream
•
BufferedInputStream , BufferedOutputStream
•
LineNumberInputStream
•
PushbackInputStream
•
PrintStream
(strumie
ń
wyj
ś
ciowy)
W pakiecie
java.io
wyst
ę
puje jedynie jedna klasa pochodna od klasy
znakowego strumienia
FilterReader
:
PushbackReader
.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 26
Utworzenie strumienia filtruj
ą
cego
Np. doł
ą
czenie standardowego strumienia wej
ś
ciowego do strumienia
filtruj
ą
cego i "opakowanie" klas
ą
BufferedReader
dla uzyskania metody
readLine()
:
BufferedReader d = new BufferedReader (new
DataInputStream(System.in));
String input;
while ((input = d.readLine()) != null) {
...
// wykonuje przetwarzanie
}
Przykład.
Wykorzystanie klas
DataInputStream
i
DataOutputStream
w
celu formatowania odczytu i zapisu danych w postaci kolumn
przedzielonych znakiem tabulacji. Kolumnami s
ą
: cena, liczba zamówie
ń
,
opis towaru - zapisane binarnie.
// (1) Najpierw obiekt typu
DataOutputStream
zostaje doł
ą
czony do
// obiektu klasy
FileOutputStream
, który słu
ż
y do zapisu do pliku
//
rachunek1.txt
:
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 27
DataOutputStream out = new DataOutputStream(
new FileOutputStream("rachunek1.txt"));
// (2) Program korzysta z metody
write
klasy
DataOutputStream
aby
// zapisa
ć
do pliku dane wyst
ę
puj
ą
ce w tablicach odpowiednio
// do ich typów:
for (int i = 0; i < prices.length; i ++) {
// Tablica
prices
zawiera dane
out.writeDouble(prices[i]); //
out.writeChar('\t');
out.writeInt(units[i]);
out.writeChar('\t');
out.writeChars(descs[i]);
out.writeChar('\n');
}
out.close();
// (3) Teraz otworzymy strumie
ń
typu
DataInputStream
dla wła
ś
nie
// co zapisanego pliku:
DataInputStream in = new DataInputStream(
new FileInputStream("rachunek1.txt"));
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 28
// (4)
DataInputStream
te
ż
musi by
ć
doł
ą
czony do innego strumienia - w
// tym przypadku do
FileInputStream
- aby odczyta
ć
dane z pliku.
// (5)Teraz dane wczytywane s
ą
przez specjalizowane metody
readXxxx
// klasy
DataInputStream:
try {
while (true) {
//Teoretycznie bezwarunkowa p
ę
tla ko
ń
czona wyj
ą
tkiem
price = in.readDouble();
in.readChar();
// usuwa znak tabulacji
unit = in.readInt();
in.readChar();
// usuwa znak tabulacji
char chr;
desc = new StringBuffer(20);
char lineSep = System.getProperty("line.separator").charAt(0);
while ((chr = in.readChar() != lineSep) {
desc.append(chr);
}
System.out.println("Zamówiono " + unit + " jednostek "
+ desc + " po cenie $" + price);
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 29
total = total + unit * price;
}
}
catch (EOFException e) { }
System.out.println("SUMA: $" + total);
in.close();
Uwaga:
zwykle p
ę
tla odczytu danych wygl
ą
da nast
ę
puj
ą
co:
while ((input = in.read()) != null) {
. . .
}
Jest to mo
ż
liwe dla klas, które mog
ą
symbolem
null
przekaza
ć
osi
ą
gni
ę
cie ko
ń
ca pliku. Wiele metod klasy
DataInputStream
nie mo
ż
e
tak sygnalizowa
ć
ko
ń
ca pliku, gdy
ż
ka
ż
da warto
ść
mo
ż
e by
ć
poprawn
ą
dan
ą
, np. -1. Dlatego te
ż
metody
read
klasy
DataInputStream
zgłaszaj
ą
w tym celu wyj
ą
tek
EOFException
.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 30
Definiowanie własnych strumieni filtruj
ą
cych
1. Utworzy
ć
klasy pochodne od
FilterInputStream
i
FilterOutputStream
-
zwykle współpraca z plikami ma dualny charakter.
2. Nadpisa
ć
metody
read
i
write
w razie potrzeby.
Przykład.
Zdefiniowanie pary klas pochodnych od
FilterInputStream
i
FilterOutputStream
korzystaj
ą
cych z klasy sprawdzaj
ą
cej sum
ę
kontroln
ą
odczytywanych i zapisywanych danych.
Program
CheckedIODemo
obejmuje 4 klasy i 1 interfejs:
•
klasy
pochodne
strumieni
filtruj
ą
cych
-
CheckedOutputStream,
CheckedInputStream
•
interfejs
Checksum
i klasa
Adler32
dla obliczania sumy kontrolnej,
•
klasa główna programu
CheckedIODemo
z metod
ą
main
.
// Konstruktor klasy
CheckedOutputStream
:
public CheckedOutputStream(OutputStream out, Checksum cksum) {
super(out);
// Zainicjalizuj podobiekt dziedziczony
this.cksum = cksum;
// Zapisz sum
ę
kontroln
ą
}
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 31
Argumentami
konstruktora
s
ą
:
obiekt
strumienia
wyj
ś
ciowego
OutputStream
podlegaj
ą
cy filtrowaniu i obiekt klasy
Checksum
, który jest w
stanie obliczy
ć
sum
ę
kontroln
ą
.
W klasie
CheckedOutputStream
nadpisano wszystkie trzy wersje metody
write
klasy
FilterOutputStream
:
public void write(int b) throws IOException {
out.write(b);
cksum.update(b);
}
public void write(byte[] b) throws IOException {
out.write(b, 0, b.length);
cksum.update(b, 0, b.length);
}
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
cksum.update(b, off, len);
}
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 32
Celem metod
write
jest zapis danych do strumienia wyj
ś
ciowego i
modyfikacja sumy kontrolnej.
Klasa
CheckedInputStream
jest podobna do klasy
CheckedOutputStream
.
Jej jedyny konstruktor to:
public CheckedInputStream(InputStream in, Checksum cksum) {
super(in);
this.cksum = cksum;
}
W klasie
CheckedInputStream
nadpisano wszystkie trzy wersje metody
read
klasy bazowej
FilterInputStream
:
public int read() throws IOException {
int b = in.read();
if (b != -1) {
cksum.update(b);
}
return b;
}
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 33
public int read(byte[] b) throws IOException {
int len;
len = in.read(b, 0, b.length);
if (len != -1) { cksum.update(b, 0, b.length); }
return len;
}
public int read(byte[] b, int off, int len) throws IOException {
len = in.read(b, off, len);
if (len != -1) { cksum.update(b, off, len); }
return len;
}
Metody
read
wczytuj
ą
dane ze strumienia wej
ś
ciowego i je
ś
li wczytano
dan
ą
to nast
ą
pi modyfikacja sumy kontrolnej.
Interfejs
Checksum
obejmuje m.in. metody
getValue
i
update
przeznaczone dla pobrania i modyfikowania warto
ś
ci sumy kontrolnej.
Suma jest obliczana krok po kroku dla danych całego strumienia. Np.
klasa
Adler32
implementuje ten interfejs.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 34
Klasa główna programu
CheckedIODemo
:
import java.io.*;
public class CheckedIODemo {
public static void main(String[] args) throws IOException {
Adler32 inChecker = new Adler32();
Adler32 outChecker = new Adler32();
CheckedInputStream in = null;
CheckedOutputStream out = null;
try { in = new CheckedInputStream(
new FileInputStream("plikWE.txt"), inChecker);
out = new CheckedOutputStream(
new FileOutputStream("plikWY.txt"), outChecker);
} catch (FileNotFoundException e) {
System.err.println("CheckedIODemo: " + e);
System.exit(-1);
} catch (IOException e) {
System.err.println("CheckedIODemo: " + e);
System.exit(-1);
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 35
}
int c;
while ((c = in.read()) != -1)
out.write(c);
System.out.println("Suma kontrolna WE: " + inChecker.getValue());
System.out.println("Suma kontrolna WY: "+ outChecker.getValue());
in.close();
out.close();
}
}
W metodzie
main
utworzono dwa obiekty klasy
Adler32
dla obliczania
sumy kontrolnej dwóch obiektów klas
CheckedOutputStream
i
CheckedInputStream
, które otwarte zostaj
ą
dla dwóch plików. Pierwszy
obiekt wczytuje dane z 1-szego pliku a drugi zapisuje je w drugim pliku -
oba wykorzystuj
ą
przy tym swoje obiekty klasy
Adler32
w celu
modyfikacji sumy kontrolnej.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 36
5.3
Serializacja
obiektu
(klasy
ObjectInputStream,
ObjectOutputStream
)
Klasy bajtowych strumieni
ObjectInputStream
i
ObjectOutputStream
umo
ż
liwiaj
ą
zapis i odczyt trwałego obiektu, w tym serializacj
ę
i
deserializacj
ę
obiektu.
Zastosowanie serializacji
:
Dla
"Remote Method Invocation"
(RMI) - komunikacja obiektów poprzez
gniazdka - przekazywanie obiektu pomi
ę
dzy klientem i serwerem.
Trwało
ść
obiektu w aplikacji - archiwizacja obiektu umo
ż
liwi jego
odtworzenie po ponownym wywołaniu programu.
Klasa obiektu podlegaj
ą
cego serializacji musi implementowa
ć
interfejs
Serializable
.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 37
1) Mechanizmy serializacji obiektów
Zapis obiektu do strumienia (
ObjectOutputStream)
Np.
FileOutputStream out = new FileOutputStream("Czas.txt");
ObjectOutputStream s = new ObjectOutputStream(out);
//Nasz obiekt
s.writeObject("Dzisiaj");
// Serializacja napisu
s.writeObject( new Date());
// Serializacja obiektu klasy
s.flush();
Obiekt klasy
ObjectOutputStream
tworzony jest na obiekcie innej klasy
strumieniowej, np.
FileOutputStream
(dla pliku "Czas.txt" w powy
ż
szym
przykładzie). Metoda
writeObject
zapisuje do pliku zarówno napis jak i
obiekt klasy
Date
.
ObjectOutputStream
implementuje interfejs
DataOutput
- s
ą
tu metody
dla zapisu elementarnych typów danych:
writeInt, writeFloat, writeUTF
.
Metoda
writeObject
zgłasza wyj
ą
tek
NotSerializableException
je
ś
li nie
mo
ż
e dokona
ć
serializacji przekazanego jej obiektu.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 38
Odczyt obiektu ze strumienia (
ObjectInputStream)
Np.
FileInputStream in = new FileInputStream("Czas");
ObjectInputStream s = new ObjectInputStream(in);
// Nasz obiekt
String today = (String)s.readObject();
// Deserializuj napis
Date date = (Date)s.readObject();
// Deserializauj obiekt
Obiekt klasy
ObjectInputStream
konstruowany jest na obiekcie innej
klasie strumieniowej - w podanym przypadku jest ni
ą
FileInputStream
.
Metoda
readObject
pozwala na wczytanie obiektu, np. typu
String
i
Date
z
pliku.
Zwraca
ona
referencj
ę
typu
Object
,
która
musi
by
ć
przekonwertowana na wła
ś
ciwy typ referencji.
ObjectInputStream
implementuje interfejs
DataInput
wyznaczaj
ą
cy
metody dla odczytu elementarnych typów danych, jak np.
readInt,
readFloat, readUTF
.
Deserializacj
ę
obiektu zapewnia metoda
defaultReadObject
w klasie
ObjectInputStream
.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 39
2) Zapewnienie standardowej serializacji obiektu w naszej klasie
Obiekt mo
ż
e zosta
ć
serializowany tylko wtedy, gdy jego klasa
implementuje interfejs
Serializable
(ale jest to PUSTY interfejs):
package java.io;
public interface Serializable {
// Jest to pusty interfejs przeznaczony dla
// zaznaczania klas, dla których zdefiniowano serializacj
ę
.
};
Wykorzystanie - deklarowanie implementowania interfejsu:
public class MojaSerializowalnaKlasa implements Serializable {
...
}
ale bez konieczno
ś
ci implementacji jakie
ś
metody.
Zadanie
serializacji
obiektów
takiej
klasy
przejmie
metoda
defaultWriteObject
w klasie
ObjectOutputStream
. Wypisuje ona
automatycznie nast
ę
puj
ą
ce dane potrzebne do odtworzenia obiektu:
•
klas
ę
obiektu, sygnatur
ę
klasy, warto
ś
ci wszystkich nie przechodnich i
niestatycznych pól, w tym warto
ś
ci referencji do innych obiektów.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 40
3) Indywidualny sposób serializacji obiektu
•
Własna obsługa serializacji i deserializacji obiektu jest mo
ż
liwa po
zdefiniowaniu metod
writeObject
i
readObject
. Ich rola polega na
przekazaniu dodatkowej informacji o obiekcie do lub ze strumienia.
•
Metoda
writeObject
musi woła
ć
domy
ś
ln
ą
metod
ę
serializacji
defaultWriteObject
danego strumienia swoj
ą
pierwsz
ą
instrukcj
ą
:
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
// Własny kod serializacji ...
}
•
Metoda
readObject
powinna wczytywa
ć
wszystko to co zapisano
metod
ą
writeObject
. Jej struktura:
private void readObject(ObjectInputStream s) throws IOException {
s.defaultReadObject();
// Własna deserializacja obiektu ...
...
// Ewentualnie wykorzystanie informacji dla modyfikacji obiektu
...
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 41
}
Implementacja interfejsu
Externalizable
Aby uzyska
ć
wył
ą
czn
ą
odpowiedzialno
ść
naszej klasy za serializacj
ę
jej
obiektów nale
ż
y zaimplementowa
ć
interfejs
Externalizable
.
Dla obiektów typu
Externalizable
automatycznie zapisywana jest jedynie
identyfikacja klasy
. Klasa odpowiada za zapis i odczyt zawarto
ś
ci jej obiektu i
za koordynacj
ę
w tej sprawie ze swoj
ą
klas
ą
bazow
ą
.
package java.io;
public interface Externalizable extends Serializable {
public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in) throws IOException,
java.lang.ClassNotFoundException;
}
Klasa typu
Externalizable
musi zaimplementowa
ć
metody publiczne
writeExternal
i
readExternal
.
Wi
ąż
e si
ę
to z ryzykiem uzyskania bezpo
ś
redniego dost
ę
pu przez program
klienta do zasobów obiektu tej klasy. Dlatego mo
ż
na tak post
ą
pi
ć
jedynie z
klasami nie zawieraj
ą
cymi informacji zabezpieczonej lub krytycznej dla
zachowania si
ę
programu.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 42
Ochrona wra
ż
liwej informacji
1) Pola zawieraj
ą
ce
wra
ż
liwe
dla systemu dane oznaczane s
ą
jako
private transient
.
Pola typu
transient
i
static
nie podlegaj
ą
serializacji / deserializacji.
2)
Cało
ś
ciowo "wra
ż
liwe" klasy
w ogóle nie powinny by
ć
serializowane
- nie nale
ż
y implementowa
ć
Serializable
lub
Externalizable
.
3) Mo
ż
na w metodach
writeObject
i
readObject
weryfikowa
ć
poprawno
ść
stanu obiektu
i w razie bł
ę
dnego stanu
zgłosi
ć
wyj
ą
tek
NotSerializableException
, co uchroni program przed korzystaniem z
obiektu.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 43
5.4 Współpraca z plikami o dost
ę
pie swobodnym
Strumienie znakowe i bajtowe posiadaj
ą
sekwencyjny dost
ę
p.
Inaczej jest z klas
ą
RandomAccessFile
, która umo
ż
liwia swobodny dost
ę
p
do elementów pliku.
Mo
ż
liwe jest te
ż
zdefiniowanie nad tym strumieniem strumieni
filtruj
ą
cych, tzn. implementuj
ą
cych interfejsy
DataInput
i
DataOutput
.
Np.
ZIP jest to format archiwizacji plików. Dost
ę
p sekwencyjny do takiego
pliku wymaga przeczytania du
ż
ej cz
ęś
ci pliku zanim dotrzemy do
katalogu zawieraj
ą
cego informacj
ę
o interesuj
ą
cych nas plikach.
W przypadku
swobodnego dost
ę
pu
wykorzystamy metod
ę
seek
w celu
przeszukania pliku (bez konieczno
ś
ci odczytu) i dotarcia do katalogu.
Utworzenie obiektu
tej klasy
RandomAccessFile
wymaga przekazania
nazwy pliku lub obiektu klasy
File
oraz sposobu współpracy z plikiem.
Np.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 44
new RandomAccessFile("plikWE.txt", "r");
// Otwarcie dla odczytu
new RandomAccessFile("plikWEWY.txt", "rw");
// Odczyt i zapis
Klasa
RandomAccessFile
słu
ż
y zarówno do odczytu jak i zapisu do pliku.
•
Nie dziedziczy
ona z
InputStream
ani z
OutputStream
.
•
Jednak
implementuje
interfejsy
DataInput
i
DataOutput
co umo
ż
liwia
zastosowanie do niej strumieni filtruj
ą
cych.
Operowanie na pliku odbywa si
ę
za pomoc
ą
metod wyznaczonych przez
interfejsy
DataInput
i
DataOutput
.
Wska
ź
nik pliku
- wskazuje aktualn
ą
pozycj
ę
w pliku, od 0 do liczby
bajtów. Wska
ź
nik przesuwany jest odpowiednio do liczby bajtów dla
operacji odczytu lub zapisu.
S
ą
te
ż
metody jawnej modyfikacji wska
ź
nika pliku:
int skipBytes(int)
- przesu
ń
wska
ź
nik wprzód o okre
ś
lon
ą
liczb
ę
bajtów.
void seek(long)
- umie
ść
wska
ź
nik bezpo
ś
rednio przez zadanym bajtem.
long getFilePointer()
- zwró
ć
poło
ż
enie wska
ź
nika wyra
ż
one w bajtach.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 45
Tworzenie strumieni filtruj
ą
cych dla plików o swobodnym dost
ę
pie
Przykład.
Powrócimy do przykładu z
CheckedIODemo
.
Jednak teraz
utworzymy strumienie filtruj
ą
ce dla klasy
RandomAccessFile
.
(1) Teraz klasa
CheckedDataOutput
jest modyfikacj
ą
poprzedniej klasy
CheckedOutputStream
- oblicza sum
ę
kontroln
ą
danych zapisywanych do
strumienia. Jednak operuje na obiektach implementuj
ą
cych
DataOutput
a
nie na obiektach klasy
OutputStream
.
(2) Klasa
CheckedDataInput
jest modyfikacj
ą
klasy
CheckedInputStream
,
pracuj
ą
c
ą
na obiektach
DataInput
zamiast
InputStream
.
(3) Ró
ż
nice mi
ę
dzy
CheckedDataOutpu
t
a
CheckedOutputStream:
(I)
CheckedDataOutput
nie dziedziczy po
FilterOutputStream
a implementuje
interfejs
DataOutput
:
public class CheckedDataOutput implements DataOutput { ...
(II) W
CheckedDataOutput
zadeklarowano prywatne pole - obiekt typu
DataOutput
,
do którego zapisywane b
ę
d
ą
dane.
private DataOutput out;
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 46
(III) Konstruktor klasy
CheckedDataOutput
tworzy obiekt "osadzony" na
obiekcie
DataOutput
a nie na
OutputStream
.
public CheckedDataOutput(DataOutput out, Checksum cksum) {
this.cksum = cksum;
this.out = out;
}
(IV) Konstruktor nie woła
super(out)
jak w
CheckedOutputStream
,
gdy
ż
klasa dziedziczy z klasy
Object
a nie klasy strumieniowej.
(4) Podobnych zmian dokonano w
CheckedDataInput
:
(I)
CheckedDataInput
implementuje
DataInput
.
(II) W
CheckedDataInput
zadeklarowano prywatne pole typu
DataInput
na którym osadzono obiekt tej klasy.
(III) Konstruktor w
CheckedDataInput
wymaga obiektu
DataInput
.
(5) Równie
ż
metody
readXxxx
w klasie
CheckedDataInput
posiadaj
ą
zmienione nazwy i sygnatury:
public byte readByte() throws IOException {
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 47
byte b = in.readByte();
cksum.update(b);
return b;
}
public void readFully(byte[] b) throws IOException {
in.readFully(b, 0, b.length);
cksum.update(b, 0, b.length);
}
public void readFully(byte[] b, int off, int len) throws IOException {
in.readFully(b, off, len);
cksum.update(b, off, len);
}
(6) Mamy teraz dwa programy testuj
ą
ce:
poprzedni
CheckedDIODemo
, sprawdzał działanie filtrów na plikach o
sekwencyjnym dost
ę
pie (
DataInputStream
i
DataOutputStream
).
nowy
CheckedRAFDemo
, sprawdza filtry dla plików o swobodnym
dost
ę
pie (
RandomAccessFile
)
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 48
Jedyna ró
ż
nica mi
ę
dzy nimi polega na typie otwieranych obiektów.
W
CheckedDIODemo
:
in = new
CheckedDataInput
(new DataInputStream(
new FileInputStream("plikWE.txt")), inChecker);
out = new
CheckedDataOutput
(new DataOutputStream(
new FileOutputStream("plikWY.txt")), outChecker);
W
CheckedRAFDemo
:
in = new
CheckedDataInput
(
new RandomAccessFile("plikWE.txt", "r"), inChecker);
out = new
CheckedDataOutput
(
new RandomAccessFile("plikWY.txt", "rw"), outChecker);
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 49
5.5 Pozostałe klasy w java.io
Poza omawianymi klasami
w pakiecie
java.io
wyst
ę
puj
ą
klasy i interfejsy:
File
Reprezentuje plik nale
żą
cy do rodzimego systemu plików. Mo
ż
na
utworzy
ć
obiekt klasy
File
dla pliku.
FileDescriptor
Reprezentuje uchwyt (lub deskryptor) do otwartego pliku lub gniazdka.
Ta klasa zwykle nie jest u
ż
ywana przez programist
ę
u
ż
ytkowego.
StreamTokenizer
Rozdziela strumie
ń
na "token-y" - elementarne jednostki dla parsera
tekstu (słowo, symbol) .
FilenameFilter
Korzysta z niej metoda
list
w klasie
File
dla okre
ś
lenia, które pliki w
kartotece maj
ą
by
ć
wybrane.
5. Strumienie wej
ś
cia - wyj
ś
cia
W. Kasprzak: Programowanie zdarzeniowe
5 - 50
Strumienie WE-WY w pakiecie
java.util.zip
CheckedInputStream , CheckedOutputStream
Para
strumieni
realizuj
ą
ca
sprawdzanie
sumy
kontrolnej
dla
wczytywanych lub wysyłanych danych.
DeflaterOutputStream, InflaterInputStream
Para strumieni do kompresji i dekompresji danych.
GZIPInputStream, GZIPOutputStream
Dla odczytu i zapisu komprymowanych danych w formacie GZIP.
ZipInputStream, ZipOutputStream
Dla odczytu i zapisu komprymowanych danych w formacie ZIP.