From 134ffc6c59365beb443b5e2d9fc7ce75dbf9dec0 Mon Sep 17 00:00:00 2001 From: egorzhurov Date: Fri, 10 May 2024 22:56:09 +0300 Subject: [PATCH] Move 31 article to markdown format --- .../031/Resource classes. IO Streams.md | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 lessons/java-core/031/Resource classes. IO Streams.md diff --git a/lessons/java-core/031/Resource classes. IO Streams.md b/lessons/java-core/031/Resource classes. IO Streams.md new file mode 100644 index 0000000..8dfec55 --- /dev/null +++ b/lessons/java-core/031/Resource classes. IO Streams.md @@ -0,0 +1,151 @@ +# Классы ресурсов. I/O Streams + +Сегодня мы приступаем к достаточно обширной теме, связанной с ресурсами в Java. **Ресурсами (внешними ресурсами)** +называют некие хранилища информации вне JVM. Это могут быть файлы, базы данных и пр. + +Также вы можете встретить употребление **"ресурс"** по отношению к классу (или его объектам), предназначенному для +работы с внешними ресурсами. + +**Внутренние ресурсы** - это объекты, хранящиеся в памяти JVM (отсюда и разделение на внутренние и внешние ресурсы), но +вряд ли вам когда-нибудь понадобится это информация. + +## Ресурсы. Классы для работы с ресурсами + +Для работы с ресурсами в Java (а также сторонних **библиотеках** и **фреймворках**) существует огромное количество +различных классов. Их объединяет две вещи: + +1. Все они предназначены для взаимодействия с ресурсами (неожиданно); +2. Все они являются наследниками интерфейса `AutoCloseable` (или его наследника `Closeable`). + +`AutoCloseable` имеет лишь один метод: `close()`. Он необходим для закрытия ресурса. + +На самом деле, мы опосредовано знакомы с некоторыми классами ресурсов: + +- `InputStream`. Статическое поле `in` у класса `System` имеет именно такой тип; +- `PrintStream`. Одна из реализаций класса `OutputStream`. `PrintStream` – тип статического поля `out` в + классе `System`; +- `Scanner`. Строго говоря, он является не ресурсом, а оберткой над ресурсом, но как класс, расширяющий `Closeable`, он + относится к классам для работы с ресурсами. Метод `close()` будет вызывать `close()` у ресурса, который был указан в + конструкторе _Scanner'а_. + +## Try-with-resource + +Знать, что используемый вами класс, является ресурсом, важно по одной простой причине: ресурсы необходимо закрывать, +чтобы избежать **утечек памяти**. + +Скажем, если вы создали `InputStream`, читающие данные из файла – в памяти Java будет храниться содержимое файла, с +которым работает ваш `InputStream`. Это может быть не критично в маленьких программах, имеющих избыток оперативной +памяти. Но в больших приложениях это приведет как минимум к замедлению работы. + +Но если вы создали `OutputStream`, пишущий в файл - никто не сможет писать в этот же файл, пока ваш `OutputStream` не +будет закрыт. Опять же, в небольших однопоточных (в контексте многопоточности, а не потоков ввода-вывода) приложениях +это может быть не ощутимо, но в больших приложениях приведет к проблемам доступа, замедлению работы или более серьезным +проблемам. + +С завершением программы, ресурс будет закрыт силами операционной системы. Но если используемый вами ресурс имеет +специфическую логику при закрытии (а это вполне возможно), она не отработает, что может привести к дальнейшим ошибкам. + +Таким образом, для любых объектов классов, расширяющих `AutoCloseable` или `Closeable` требуется вызывать +метод `close()` сразу после того, как работа с ресурсом завершена. + +До Java 7 такую логику обычно оборачивали в `try-catch`: + +```java + InputStream in = null;//создание объекта может выбрасывать исключение, первично присваиваем переменной значение null + + try { + in = … // Инициализация переменной in + + // Работа с переменной in + } catch (IOException e) { + // IOException – ошибка ввода/вывода. + // В большинстве случаев методы ресурсов будут throws IOException (или его наследников) + // Логика обработки ошибки + } finally { + in.close(); //при условии, что метод, где это происходит, является throws Exception (или IOException) + } +``` + +Закрытие производится в `finally`, т.к. при выпадении ошибки код в `try` может быть выполнен не до конца и нет гарантии, +что метод `close()` будет вызван, если расположить его там. Но сам `close()` тоже может выбросить исключение ( +В `AutoCloseable` он помечен как `throws Exception`, в `Cloaseable` – `throws IOException`). + +Поэтому метод, работающий ресурсами должен был либо иметь блок `throws`, либо `finally` из примера выше становился более +раздутым: + +```java + finally { + try { + if (in != null) { // первично переменная была инициализирована null, + // мы не имеем гарантии, что дальнейшая инициализация была успешна + in.close(); + } + } catch (IOException ex) { + // Логика обработки ошибки + } + } +``` + +Однако такой код был настолько объемным и настолько однотипным, что в Java 7 появилась отдельная синтаксическая +конструкция, инкапсулирующая в себе работу с классами ресурсов – **try-with-resource**. Теперь код выше можно переписать +так: + +```java + try (InputStream in = ... /* инициализация переменной in */) { + // Работа с переменной in + } catch (IOException e) { + // Логика обработки ошибки + } +``` + +Таким образом, Java берет на себя закрытие ресурса, который создается в скобках после `try`. Также в `()` может быть +помещено несколько ресурсов, в таком случае необходимо их указать через точку с запятой: + +```java + try (InputStream in1 = …; InputStream in2 = …) +``` + +Если в рамках этого подраздела что-то осталось непонятным – могу предложить статью на +[metanit](https://metanit.com/java/tutorial/6.2.php). + +В целом, там примерно то же самое, но, возможно, кому-то будет легче осознать на более живых примерах. + +## I/O Streams. Reader и Writer + +В рамках этого подраздела мы также обратимся к метаниту. + +Статья, посвященная знакомству с потоками ввода и вывода не слишком сложная. Но сразу замечу, что заучивать все иерархию +наследования _Input-_ и _Output- Stream_ нет необходимости. То же актуально и для _Reader_ и _Writer_. + +Кроме того, советую сформировать в голове ассоциацию: + +`InputStream` и `Reader` – классы, которые читают ресурс. Т.е. передают информацию из ресурса в Java, ее можно будет +записать в переменные. + +`OutputStream` и `Writer` – классы, пишущие в ресурс. Они позволяют значение Java-переменных записать в ресурс. + +На этом переходим к [статье](https://metanit.com/java/tutorial/6.1.php) + +В ближайших уроках мы познакомимся с обработкой файлов, на данном этапе можете взглянуть на синтаксис, позволяющий +работать с ними через `InputStream` (есть в примерах статьи выше). + +#### С теорией на сегодня все! + +![img.png](../../../commonmedia/defaultFooter.jpg) + +Я не вижу смысла давать полноценные задания по этой теме, I/O Streams и Reader /Writer достаточно низкоуровневая логика, +работа с ней нужна не часто, в большинстве случаев, она сводится к вызову `readAllBytes()` у `InputStream`. + +Однако для закрепления материала предлагаю попытаться считать какие-нибудь значения из консоли через `System.in` и +поприводить их к переменным разных примитивных типов или строкам. Это может быть достаточно увлекательно, хоть и не +сложно. + +Подсказка: у `String` есть конструктор, принимающий `byte[]`. Как удалить из строки лишние символы вы тоже уже знаете:) + +> Если что-то непонятно или не получается – welcome в комменты к посту или в лс:) +> +> Канал: https://t.me/ViamSupervadetVadens +> +> Мой тг: https://t.me/ironicMotherfucker +> +> **Дорогу осилит идущий!** \ No newline at end of file