1. Strumienie danych
Każdy program rzadko istnieje samodzielnie. Zwykle w jakiś sposób wchodzi w interakcje ze „światem zewnętrznym”. Może to być odczytywanie danych z klawiatury, wysyłanie wiadomości, pobieranie stron z Internetu lub odwrotnie, przesyłanie plików na zdalny serwer.
Wszystko to możemy nazwać jednym słowem – proces wymiany danych między programem a światem zewnętrznym. Nie jest to jednak jedno słowo.
Sam proces wymiany danych można podzielić na dwa typy: odbieranie danych i wysyłanie danych. Na przykład odczytujesz dane z klawiatury za pomocą obiektu Scanner
- to jest odbieranie danych. I wyświetl dane na ekranie za pomocą polecenia System.out.println()
- to jest wysyłanie danych.
Termin wątek jest używany do opisania procesu wymiany danych w programowaniu. Skąd taka nazwa?
W prawdziwym życiu może to być strumień wody lub strumień ludzi (strumień ludzki). W programowaniu przepływ odnosi się do przepływu danych.
Nici to wszechstronne narzędzie. Pozwalają programowi odbierać dane z dowolnego miejsca (strumienie przychodzące) i wysyłać dane w dowolne miejsce (strumienie wychodzące). Dzielą się one na dwa rodzaje:
- Strumień przychodzący (wejście): używany do odbierania danych
- Strumień wychodzący (Output): używany do wysyłania danych
Aby wątki były „dotykalne”, programiści Java napisali dwie klasy: InputStream
i OutputStream
.
Klasa InputStream
posiada metodę read()
, która pozwala na odczytywanie z niej danych. A klasa OutputStream
ma metodę write()
, która pozwala zapisywać do niej dane. Mają inne metody, ale o tym później.
Strumienie bajtów
Co to za dane iw jakiej formie można je odczytać? Innymi słowy, jakie typy danych są obsługiwane przez te klasy?
Och, to są klasy ogólne i jako takie obsługują najpopularniejszy typ danych, byte
. OutputStream
Bajty (i tablice bajtów) mogą być zapisywane do , a InputStream
bajty (lub tablice bajtów) mogą być odczytywane z obiektu. Wszystkie — nie obsługują żadnych innych typów danych.
Dlatego takie strumienie są również nazywane strumieniami bajtów .
Cechą strumieni jest to, że dane z nich można odczytywać (zapisywać) tylko sekwencyjnie. Nie można odczytać danych ze środka strumienia bez odczytania wszystkich danych przed nim.
Oto jak czytanie z klawiatury działa w całej klasie Scanner
: odczytujesz dane z klawiatury sekwencyjnie: linia po linii. Przeczytaj wiersz, przeczytaj następny wiersz, przeczytaj następny wiersz i tak dalej. Dlatego nazywa się metodę czytania linii nextLine()
(dosłownie - „następna linia”).
Zapisywanie danych do strumienia OutputStream
również odbywa się sekwencyjnie. Dobrym przykładem jest wyjście ekranowe. Wyprowadzasz linię, a następnie kolejną i kolejną. To jest wyjście szeregowe. Nie możesz wypisać pierwszej linii, potem dziesiątej, a potem drugiej. Wszystkie dane są zapisywane w strumieniu wyjściowym tylko sekwencyjnie.
Strumienie znaków
Niedawno dowiedziałeś się, że łańcuchy są drugim najpopularniejszym typem danych i rzeczywiście tak jest. Wiele informacji przekazywanych jest w postaci znaków i całych linii. Komputer byłby świetny w przesyłaniu wszystkiego jako bajtów, ale ludzie nie są tak doskonali.
Programiści Javy wzięli to pod uwagę i napisali jeszcze dwie klasy: Reader
i Writer
. Klasa Reader
jest odpowiednikiem klasy InputStream
, tylko jej metoda read()
odczytuje nie bajty, a znaki - char
. Klasa Writer
odpowiada klasie OutputStream
i podobnie jak klasa Reader
, działa ze znakami ( char
), a nie bajtami.
Jeśli porównamy te cztery klasy, otrzymamy następujący obraz:
Bajty (bajty) | Symbole (znaki) | |
---|---|---|
Odczytywanie danych |
|
|
Rejestracja danych |
|
|
Praktyczne użycie
InputStream
Klasy , OutputStream
, Reader
i same w sobie Writer
nie są używane jawnie: nie są dołączone do żadnych zewnętrznych obiektów, z których można odczytywać dane (lub do których można zapisywać dane). Jednak te cztery klasy mają wiele następców, które mogą wiele zdziałać.
2. KlasaInputStream
Klasa InputStream
jest interesująca, ponieważ jest klasą nadrzędną dla setek klas podrzędnych. Sam w sobie nie ma żadnych danych, ale ma metody, które mają wszystkie jego klasy pochodne.
Obiekty strumieniowe na ogół rzadko przechowują dane same w sobie. Strumień jest narzędziem do odczytu/zapisu danych, ale nie do przechowywania. Chociaż są wyjątki.
Metody klasy InputStream
i wszystkich jej klas potomnych:
Metody | Opis |
---|---|
|
Odczytuje jeden bajt ze strumienia |
|
Odczytuje tablicę bajtów ze strumienia |
|
Odczytuje wszystkie bajty ze strumienia |
|
Przekazuje n bajt w strumieniu (odczytuje i odrzuca) |
|
Sprawdza, ile bajtów pozostało w strumieniu |
|
Zamyka strumień |
Omówmy pokrótce te metody:
metodaread()
Metoda read()
odczytuje jeden bajt ze strumienia i zwraca go. Możesz być zdezorientowany typem wyniku - int
, ale zrobiono to, ponieważ typ int
jest standardem dla wszystkich liczb całkowitych. Pierwsze trzy bajty typu int
będą miały wartość zero.
metodaread(byte[] buffer)
Jest to druga modyfikacja metody read()
. Pozwala InputStream
na jednoczesny odczyt z tablicy bajtów. Tablica do przechowywania bajtów musi być przekazana jako parametr. Metoda zwraca liczbę — liczbę faktycznie odczytanych bajtów.
Załóżmy, że masz 10-kilobajtowy bufor i odczytujesz dane z pliku przy użyciu rozszerzenia FileInputStream
. Jeśli plik zawiera tylko 2 kilobajty, wszystkie dane zostaną umieszczone w tablicy buforów, a metoda zwróci liczbę 2048 (2 kilobajty).
metodareadAllBytes()
Bardzo dobra metoda. Po prostu odczytuje wszystkie dane od InputStream
momentu ich wyczerpania i zwraca je jako tablicę jednobajtową. Bardzo przydatny do czytania małych plików. Duże pliki mogą fizycznie nie mieścić się w pamięci, a metoda zgłosi wyjątek.
metodaskip(long n)
Ta metoda pozwala pominąć pierwsze n bajtów z pliku InputStream
. Ponieważ dane są odczytywane ściśle sekwencyjnie, ta metoda po prostu odczytuje pierwsze n bajtów ze strumienia i odrzuca je.
Zwraca liczbę bajtów, które faktycznie zostały pominięte (jeśli strumień zakończył się, zanim n
bajt został przewinięty).
metodaint available()
Metoda zwraca liczbę bajtów, które pozostały jeszcze w strumieniu
metodavoid close()
Metoda close()
zamyka strumień danych i zwalnia powiązane z nim zasoby zewnętrzne. Po zamknięciu strumienia nie można już z niego odczytać żadnych danych.
Napiszmy przykładowy program, który kopiuje bardzo duży plik. Nie można go wczytać w całości do pamięci za pomocą metody readAllBytes()
. Przykład:
Kod | Notatka |
---|---|
|
InputStream do odczytu z pliku OutputStream do zapisu do pliku Bufor do którego odczytamy dane Dopóki w strumieniu są dane Wczytaj dane do bufora Zapisz dane z bufora do drugiego strumienia |
W tym przykładzie użyliśmy dwóch klas: FileInputStream
- dziedziczącej InputStream
do odczytu danych z pliku oraz klasy FileOutputStream
- dziedziczącej OutputStream
do zapisu danych do pliku. O drugiej klasie porozmawiamy nieco później.
Innym interesującym punktem jest zmienna real
. Kiedy ostatni blok danych zostanie odczytany z pliku, łatwo może się okazać, że jego długość jest mniejsza niż 64Kb. Dlatego nie cały bufor musi być również zapisywany na wyjściu, ale tylko jego część: pierwsze real
bajty. To jest dokładnie to, co jest zrobione w metodzie write()
.
3. KlasaReader
Klasa Reader
jest kompletnym odpowiednikiem klasy InputStream
, z jedną tylko różnicą: działa ze znakami - char
a nie z bajtami. Klasa Reader
, podobnie jak sama klasa InputStream
, nie jest nigdzie używana: jest klasą nadrzędną dla setek klas potomnych i ustala dla nich wspólne metody.
Metody klasy Reader
(i wszystkie jej klasy potomne):
Metody | Opis |
---|---|
|
Odczytuje jeden char ze strumienia |
|
Odczytuje tablicę char 's ze strumienia |
|
Pomija n char elementy w strumieniu (czyta i odrzuca) |
|
Sprawdza, czy w strumieniu nadal coś zostało |
|
Zamyka strumień |
Metody są bardzo podobne do metod klasowych InputStream
, chociaż istnieją niewielkie różnice.
metodaint read()
Ta metoda odczytuje ze strumienia pierwszego char
i zwraca go. Typ char
rozwija się do type int
, ale pierwsze dwa bajty wyniku są zawsze równe zero.
metodaint read(char[] buffer)
Jest to druga modyfikacja metody read()
. Pozwala czytać z Reader
tablicy znaków jednocześnie. Tablica znaków musi być przekazana jako parametr. Metoda zwraca liczbę — liczbę faktycznie odczytanych znaków.
metodaskip(long n)
Ta metoda pozwala pominąć n
pierwsze znaki z pliku Reader
. Działa dokładnie tak samo jak podobna metoda klasy InputStream
. Zwraca liczbę znaków, które faktycznie zostały pominięte.
metodaboolean ready()
Zwraca, true
jeśli w strumieniu znajdują się bajty, które nie zostały jeszcze odczytane.
metodavoid close()
Metoda close()
zamyka strumień danych i zwalnia powiązane z nim zasoby zewnętrzne. Po zamknięciu strumienia nie można już z niego odczytać żadnych danych.
Napiszmy program, który kopiuje plik tekstowy do porównania:
Kod | Notatka |
---|---|
|
Reader do odczytu z pliku Writer do zapisu do pliku Bufor do którego odczytamy dane Dopóki w strumieniu są dane Wczytaj dane do bufora Zapisz dane z bufora do drugiego strumienia |
GO TO FULL VERSION