Cześć! Dzisiejszy wykład będzie podzielony na dwie części. Powtórzymy niektóre stare tematy, które już poruszaliśmy i przyjrzymy się kilku nowym funkcjom :) Zacznijmy od pierwszego. Powtarzanie jest matką nauki :) Korzystałeś już z takich zajęć jak
BufferedReader
. Mam nadzieję, że nie zapomniałeś jeszcze tego polecenia:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Zanim zaczniesz czytać dalej, postaraj się zapamiętać System.in, InputStreamReader, BufferedReader
za co odpowiada każdy komponent ( ) i do czego jest potrzebny. Stało się? Jeśli nie, nie martw się :) Jeśli w tym momencie o czymś zapomniałeś, przeczytaj jeszcze raz ten wykład dedykowany czytelnikom. Przypomnijmy krótko, co każdy z nich może zrobić. System.in
to wątek do odbierania danych z klawiatury. W zasadzie do realizacji logiki czytania tekstu wystarczyłby nam jeden. Ale, jak pamiętasz, System.in
może czytać tylko bajty, a nie znaki:
public class Main {
public static void main(String[] args) throws IOException {
while (true) {
int x = System.in.read();
System.out.println(x);
}
}
}
Jeśli uruchomimy ten kod i wpiszemy literę „Y” w konsoli, wynik będzie następujący:
Й
208
153
10
Znaki cyrylicy zajmują 2 bajty w pamięci, które są wyświetlane na ekranie (a liczba 10 to bajtowa reprezentacja podziału wiersza, czyli naciśnięcia Enter). Czytanie bajtów to wielka przyjemność, więc używanie go System.in
w czystej postaci będzie niewygodne. Aby odczytać litery cyrylicy (i nie tylko) zrozumiałe dla każdego, używamy InputStreamReader
jako opakowania:
public class Main {
public static void main(String[] args) throws IOException {
InputStreamReader reader = new InputStreamReader(System.in);
while (true) {
int x = reader.read();
System.out.println(x);
}
}
}
Jeśli w konsoli wpiszemy tę samą literę „Y”, wynik tym razem będzie inny:
Й
1049
10
InputStreamReader
skonwertował dwa odczytane bajty (208, 153) na pojedynczą liczbę 1049. Jest to czytanie znakowe. 1049 odpowiada literze „Y”, co można łatwo zweryfikować:
public class Main {
public static void main(String[] args) throws IOException {
char x = 1049;
System.out.println(x);
}
}
Wyjście konsoli:
Й
Cóż, jeśli chodzi o BufferedReader
'a (i ogólnie - BufferedAnything), klasy buforowane służą do optymalizacji wydajności. Dostęp do źródła danych (pliku, konsoli, zasobu w Internecie) jest operacją dość kosztowną pod względem wydajności. Dlatego też, aby ograniczyć ilość takich połączeń, BufferedReader
odczytuje i gromadzi dane w specjalnym buforze, skąd możemy je później odebrać. W rezultacie liczba wywołań do źródła danych zmniejsza się kilkukrotnie, a nawet dziesięciokrotnie! Kolejną dodatkową cechą BufferedReader
„a i jego przewagą nad zwykłym InputStreamReader
” jest niezwykle przydatna metoda readLine()
, która odczytuje dane jako całe ciągi znaków, a nie pojedyncze liczby. To oczywiście znacznie zwiększa wygodę przy implementowaniu np. dużego tekstu. Tak wyglądałoby czytanie wiersza:
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s = reader.readLine();
System.out.println("Пользователь ввел следующий текст:");
System.out.println(s);
reader.close();
}
}
BufferedReader+InputStreamReader работает быстрее, чем просто InputStreamReader
Пользователь ввел следующий текст:
BufferedReader+InputStreamReader работает быстрее, чем просто InputStreamReader
Oczywiście BufferedReader
jest to bardzo elastyczny mechanizm i pozwala na pracę nie tylko za pomocą klawiatury. Dane możesz odczytać np. bezpośrednio z Internetu, po prostu przekazując do czytnika wymagany adres URL:
public class URLReader {
public static void main(String[] args) throws Exception {
URL oracle = new URL("https://www.oracle.com/index.html");
BufferedReader in = new BufferedReader(
new InputStreamReader(oracle.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
}
Możesz odczytać dane z pliku, przekazując do niego ścieżkę:
public class Main {
public static void main(String[] args) throws Exception {
FileInputStream fileInputStream = new FileInputStream("testFile.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream));
String str;
while ((str = reader.readLine()) != null) {
System.out.println (str);
}
reader.close();
}
}
Podstawienie System.out
Przyjrzyjmy się teraz jednej interesującej możliwości, o której wcześniej nie wspomnieliśmy. Jak zapewne pamiętasz,System
w klasie znajdują się dwa pola statyczne - System.in
i System.out
. Ci bracia bliźniacy są obiektami klasy wątków. System.in
- klasa abstrakcyjna InputStream
. Klasa . System.out
_ PrintStream
Teraz porozmawiamy konkretnie o System.out
. Jeśli wejdziemy w kod źródłowy klasy System
, zobaczymy to:
public final class System {
……………...
public final static PrintStream out = null;
…………
}
A więc System.out
zwykła zmienna klasy statycznejSystem
. Nie ma w tym żadnej magii :) Zmienna out
należy do klasy PrintStream
. Oto interesujące pytanie: dlaczego podczas wykonywania kodu System.out.println()
dane wyjściowe pojawiają się w konsoli, a nie gdzie indziej? I czy da się to jakoś zmienić? Przykładowo chcemy odczytać dane z konsoli i zapisać je do pliku tekstowego. Czy można w jakiś sposób zaimplementować taką logikę bez użycia dodatkowych klas czytników i zapisów, ale po prostu używając System.out
? Jeszcze jak najbardziej :) I chociaż zmienna System.out
jest oznaczona modyfikatorem final
, to i tak możemy to zrobić! Czego więc potrzebujemy do tego? Po pierwsze , potrzebujemy nowego obiektu klasy PrintStream
zamiast obecnego. Aktualny obiekt zainstalowany w klasie System
domyślnie nam nie odpowiada: wskazuje na konsolę. Musimy stworzyć nowy, który będzie wskazywał plik tekstowy jako „miejsce docelowe” naszych danych. Po drugie , musisz zrozumieć, jak przypisać nową wartość do zmiennej System.out
. Nie można tego tak po prostu zrobić, bo jest to oznaczone final
. Zacznijmy od końca. Klasa System
zawiera dokładnie taką metodę, jakiej potrzebujemy - setOut()
. Przyjmuje obiekt jako wejście PrintStream
i ustawia go jako punkt wyjściowy. Właśnie tego potrzebujemy! Pozostaje tylko stworzyć obiekt PrintStream
. Jest to również łatwe do zrobienia:
PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
Cały kod będzie wyglądał następująco:
public class SystemRedirectService {
public static void main(String arr[]) throws FileNotFoundException
{
PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
/*Сохраним текущее oznaczający System.out в отдельную переменную, чтобы потом
можно было переключиться обратно на вывод в консоль*/
PrintStream console = System.out;
// Присваиваем System.out новое oznaczający
System.setOut(filePrintStream);
System.out.println("Эта строка будет записана в текстовый файл");
// Возвращаем System.out старое oznaczający
System.setOut(console);
System.out.println("А эта строка - в консоль!");
}
}
W rezultacie pierwsza linia zostanie zapisana do pliku tekstowego, a druga zostanie wysłana na konsolę :) Możesz skopiować ten kod do swojego IDE i uruchomić go. Otwierając plik tekstowy, zobaczysz, że żądana linia została tam pomyślnie wpisana :) Na tym kończy się wykład. Dziś przypomnieliśmy sobie jak pracować ze strumieniami i czytnikami, przypomnieliśmy czym się od siebie różnią i dowiedzieliśmy się o nowych funkcjach System.out
, z których korzystaliśmy niemal na każdej lekcji :) Do zobaczenia na kolejnych wykładach!
GO TO FULL VERSION