– Cześć, Amigo! Dziś po raz kolejny zajmiemy się tym, jak działają InputStream i OutputStream. Wstępne wyjaśnienie było tak naprawdę trochę uproszczone. To nie są interfejsy. To klasy abstrakcyjne, które mają nawet kilka zaimplementowanych metod. Spójrzmy na metody, które posiadają:

Metody InputStream Co robi ta metoda
int read(byte[] buff);
Metoda ta natychmiast wczytuje blok bajtów do bufora (tablicy bajtów), dopóki bufor nie będzie pełny lub dopóki źródło nie będzie miało więcej bajtów do wczytania.
Metoda zwraca liczbę faktycznie wczytanych bajtów (która może być mniejsza niż długość tablicy).
int read();
Metoda ta wczytuje jeden bajt i go zwraca. Wynik jest rozszerzany do int ze względów estetycznych. Jeżeli nie ma więcej bajtów do wczytania, to metoda zwraca -1.
int available();
Metoda ta zwraca liczbę niewczytanych (dostępnych) bajtów.
void close();
Ta metoda «zamyka» strumień. Wywołuje się ją po zakończeniu pracy ze strumieniem.
Następnie obiekt wykonuje operacje porządkowe potrzebne do zamknięcia pliku itd.
W tym momencie nie można odczytać więcej danych ze strumienia.

– Więc możemy wczytać nie tylko pojedyncze bajty, ale i całe bloki?

– Dokładnie.

– Czy możemy też zapisać całe bloki?

– Tak, sprawdź to:

Metody OutputStream Co robi ta metoda
void write(int c);
Metoda ta zapisuje jeden bajt. Typ int jest zawężany do jednego bajtu. Pozostała część jest zwyczajnie odrzucana.
void write(byte[] buff);
Metoda ta zapisuje blok bajtów.
void write(byte[] buff, int from, int count);
Metoda ta zapisuje część bloku bajtów. Używana jest w przypadkach, gdy tablica bajtów mogła nie zostać wypełniona w całości.
void flush();
Jeśli strumień przechowuje w sobie jakiekolwiek dane, które nie zostały jeszcze zapisane, to metoda ta wymusza ich zapis.
void close();
Ta metoda «zamyka» strumień. Wywołuje się ją po zakończeniu pracy ze strumieniem.
Następnie obiekt wykonuje operacje porządkowe potrzebne do zamknięcia pliku itd. Nie możesz już zapisywać danych w strumieniu, a polecenie flush jest wywoływane automatycznie.

– Jak wyglądałby kod do kopiowania pliku, gdybyśmy wczytywali całe bloki na raz, a nie tylko pojedyncze bajty?

– Hmm. Wyglądałoby to mniej więcej tak:

Skopiuj plik na dysk
public static void main(String[] args) throws Exception
{
 //Utwórz strumień do odczytu bajtów z pliku
 FileInputStream inputStream = new FileInputStream("c:/data.txt");
 //Utwórz strumień do zapisu bajtów do pliku
 FileOutputStream outputStream = new FileOutputStream("c:/result.txt");

  byte[] buffer = new byte[1000];
 while (inputStream.available() > 0) //tak długo, jak występują nieodczytane bajty
 {
  //Wczytaj następny blok bajtów do bufora i zapisz w count rzeczywistą liczbę wczytanych bajtów.
  int count = inputStream.read(buffer);
  outputStream.write(buffer, 0, count); //Zapisz blok (część bloku) do drugiego strumienia
 }

 inputStream.close(); //Zamknij oba strumienie. Nie są już nam potrzebne.
 outputStream.close();
}

– Rozumiem wszystko, co dotyczy bufora, ale co to jest ta zmienna count?

– Kiedy wczytamy ostatni blok danych z pliku, możemy otrzymać, powiedzmy, 328 bajtów zamiast 1000. Więc kiedy zapisujemy dane, musimy zaznaczyć, że nie zapisujemy całego bloku - tylko pierwsze 328 bajtów.

Metoda read zwróci wartość równą liczbie faktycznie odczytanych bajtów. Dla wszystkich odczytów wynosi ona 1000, a dla ostatniego bloku - 328.

Tak więc przy zapisie bloku wskazujemy, że nie wszystkie bajty w buforze powinny zostać zapisane, tylko 328 (czyli wartość zapisana w zmiennej count).

– Teraz już wszystko jasne. Dzięki, Baśka.