– Cześć, Amigo! Opowiem Ci teraz o dwóch interfejsach: InputStream i OutputStream. Zostały one zadeklarowane jako klasy abstrakcyjne, ale jeśli im się przyjrzysz, to zobaczysz, że zasadniczo są to interfejsy. Prawie wszystkie ich metody, poza kilkoma mniej znaczącymi, są abstrakcyjne. Bliżej im do „ochroniarza”, o którym mówiliśmy wcześniej.

To bardzo ciekawe interfejsy. W tej chwili celowo nazywam je interfejsami, żebyś zrozumiał, dlaczego ich potrzebujemy. A później porozmawiamy o tym, dlaczego są to jednak klasy abstrakcyjne.

– Dobrze. Czym są zatem te interfejsy?

– Powiem to bez owijania w bawełnę.

Mamy w Javie pewną interesującą rzecz, która nazywana jest «strumieniem». Strumień to bardzo prosta koncepcja. Jego prostota stanowi klucz do niezwykle skutecznego sposobu wymiany danych. Istnieją dwa rodzaje strumieni: strumienie do wczytywania i zapisywania danych.

Jak zapewne się domyślasz dane możesz zapisywać w strumieniu do zapisywania. Ma on do tego specjalną metodę write. Z kolei dane możesz odczytywać ze strumienia do odczytu. Ma on do tego specjalną metodę read().

InputStream jest interfejsem dla strumienia, który obsługuje odczyt. Definiuje on następującą funkcjonalność: «można ze mnie odczytywać bajty».

Podobnie OutputStream – jest on interfejsem dla strumienia, który obsługuje zapisywanie. Definiuje on następującą funkcjonalność: «można we mnie zapisywać bajty».

– To wszystko?

– Mniej więcej. Tak naprawdę chodzi głównie o to, że w Javie jest mnóstwo klas, które mogą pracować z InputStream i OutputStream. Na przykład, chcesz wczytać plik z dysku i wyświetlić jego zawartość na ekranie. Nic prostszego.

Aby wczytać dane z pliku na dysku, używamy specjalnej klasy FileInputStream, która implementuje interfejs InputStream. A może chcesz zapisać te dane w innym pliku? Nie ma problemu, mamy przecież klasę FileOutputStream, która implementuje interfejs OutputStream. Poniższy kod pokazuje, co należy zrobić, aby skopiować dane z jednego pliku do drugiego.

Kod
public static void main(String[] args) throws IOException
{
 InputStream inStream = new FileInputStream("c:/source.txt");
 OutputStream outStream = new FileOutputStream("c:/result.txt");

 while (inStream.available() > 0)
 {
  int data = inStream.read(); //czyta jeden bajt ze strumienia wejściowego
  outStream.write(data); //zapisuje ten bajt do innego strumienia.
 }

 inStream.close(); //zamyka strumienie
 outStream.close();
}

Załóżmy, że napisaliśmy klasę i dodaliśmy do niej funkcjonalności InputStream i OutputStream.

Jeśli zaimplementowaliśmy te interfejsy poprawnie, instancje naszej klasy mogą być teraz zapisywane lub odczytywane z pliku. Odbywa się to przez odczytywanie ich zawartości przy użyciu metody read. Mogą one także zostać załadowane z pliku poprzez utworzenie obiektu i użycie metody write do zapisania zawartości pliku.

– Może dasz jakiś przykład?

– Jasne.

Kod Opis
class MyClass
{
private ArrayList<Integer> list;
}
Dla uproszczenia wyobraź sobie, że nasza klasa zawiera jeden obiekt – ArrayList, który przechowuje typy Integer.

A teraz dodamy do niego metody read i write

Kod Opis
class MyClass
{
private ArrayList<Integer> list;
public void write(int data)
{
list.add(data);
}
public int read()
{
int first = list.get(0);
list.remove(0);
return first;
}

public int available()
{
return list.size();
}
}
Teraz nasza klasa implementuje metodę read, która umożliwia odczytywanie całej zawartości listy według określonej kolejności.

Oraz metodę write, która pozwala Ci zapisywać wartości do naszej listy.

Oczywiście, nie jest to implementacja interfejsów InputStream i OutputStream, ale jest to bardzo podobne.

– Jasne, rozumiem. Ale jak zapisać zawartość takiego obiektu do pliku?

– Pozwól, że pokażę to na przykładzie:

Zapisz obiekt MyClass do pliku
public static void main(String[] args)
{
 MyClass myObject = new MyClass();
    OutputStream outStream = new FileOutputStream ("c:/my-object-data.txt");

 while (myObject.available() > 0)
 {
  int data = myObject.read(); //czyta jeden int ze strumienia wejściowego
  outStream.write(data); //zapisuje ten int do innego strumienia.
 }

 outStream.close();
}
Wczytaj obiekt MyClass z pliku
public static void main(String[] args)
{
 InputStream inStream = new FileInputStream("c:/my-object-data.txt");
 MyClass myObject = new MyClass();

 while (inStream.available() > 0)
 {
  int data = inStream.read(); //czyta jeden int ze strumienia wejściowego
  myObject.write(data); //zapisuje ten int w innym strumieniu.
 }

 inStream.close(); //zamyka strumienie
}

– Kurka wodna! To rzeczywiście jest podobne do pracy z InputStream/OutputStream. Te strumienie są urocze!

– W rzeczy samej!