Главная страница

Ревью 5. Потоки Байтовые потоки InputStream и OutputStream. Символьные потоки Reader и Writer


Скачать 33.41 Kb.
НазваниеПотоки Байтовые потоки InputStream и OutputStream. Символьные потоки Reader и Writer
Дата18.10.2022
Размер33.41 Kb.
Формат файлаdocx
Имя файлаРевью 5.docx
ТипДокументы
#739585

**Потоки:


Байтовые потоки: InputStream и OutputStream.

Символьные потоки: Reader и Writer

1.1. Что такое InputStream и OutputStream.


В основе всех классов, управляющих потоками байтов, находятся два абстрактных класса: InputStream (представляющий потоки ввода) и OutputStream (представляющий потоки вывода).

Объект, из которого можно считать данные, называется потоком ввода, а объект, в который можно записывать данные, - потоком вывода.

Абстрактные классы InputStream и OutputStream нужны для того, чтобы абстрагировать различные способы ввода и вывода, независимо от того, является ли поток файлом, веб- страницей или картинкой.

1.2. Что такое Reader и Writer.


Символьные потоки имеют два основных абстрактных класса Reader и Writer, управляющие потоками символов Unicode.

Класс Reader - абстрактный класс, определяющий символьный потоковый ввод.

Класс Writer - абстрактный класс, определяющий символьный потоковый вывод. В случае ошибок все методы класса передают исключение IOException.

2. Почему важно закрывать потоки.


При закрытии потока освобождаются все выделенные для него ресурсы, например, файл. В случае, если поток окажется не закрыт, может произойти утечка памяти.

Закрытие потока также гарантирует, что данные будут сброшены через поток, если остались какие-либо данные для отправки.

После окончания блока try .. catch наш FileWriter автоматически закроется. Обратите внимание — если файл не закрыть, то не гарантируется, что он корректно будет записан.

3. Какие потоки можно не закрывать (не вызывать метод close()).


В JDK 7 метод close() определяется интерфейсом AutoCloseable и можно явно не закрывать поток, а использовать оператор try-with-resources, в котором в неявно созданном finally будет вызываться метод close().

4. Что делает метод available().


Метод возвращает количество байт, которое еще осталось в потоке.

5. Что такое flush().


Сбрасывает этот выходной поток и заставляет записывать все буферизованные выходные байты. Общий контракт flush заключается в том, что его вызов является указанием на то, что если какие-либо ранее записанные байты были буферизованные реализацией выходного потока, то такие байты должны быть немедленно записаны по назначению.

6. Можно ли использовать flush() для небуферизированного потока и что будет.


Метод flush() для небуферизированного потока использовать можно, т.к исходя из документации, "среда хоста может выполнять свою собственную буферизацию, неизвестную Java. В этом случае запись можешь кэшироваться в буферах ОС".

7. Гарантируется ли запись данных в файл при вызове flush().


Если целевым назначением этого потока является абстракция, предоставляемая базовой операционной системой, например файл, то очистка потока гарантирует только то, что байты, ранее записанные в поток, передаются операционной системе для записи; это не гарантирует, что они действительно записаны на физическое устройство, такое как компьютер.дисковод.

8. На каком паттерне основана иерархия потоков ввода/вывода.


Объекты классов Java, которые используются для ввода/вывода, для обеспечения необходимой функциональности наслаиваются друг на друга.

Такая модель взаимодействия объектов возможна в паттерне "Декоратор". В этом паттерне, при создании потока, нужно использовать несколько объектов.

сед9. Что делает метод read. Почему он возвращает int а не byte. Почему он не может возвращать byte.


Метод read() используется для чтения символов.

Данный метод возвращает кол-во только что прочитанных символов (целое число от 0 до 65635) или -1, если достиг конца stream(потока).

Он возвращает int, потому что диапазон byte в Java лежит от -127 до 127, а возвращаемое значение метода read() лежит в диапазоне от 0 до 255.

Чтобы получит представление byte в int, в методе read() используется побитовое "И" с числом 255, т.е убираются лидирующие единицы.

10. Что вернет метод read(), если он считывает файл и ему встречается байт равный -1.


Если методу read() встречается байт равный -1, то он вернет значение, к которому применяется побитовое "И" с числом 255. Т.е вернёт 255.

11.??? Что возвращает перегруженный read. Какое максимальное значение вернет.


Перегруженный read вернет -1 (65635 байта) , а максимальное значение 65634 байта.

**Scanner - Простой текстовый сканер, который может анализировать примитивные типы и строки с помощью регулярных выражений.

1. Для чего нужен Scanner.


Сканер нужен для считывания данных из источника, который мы для него указываем. Например, из строки, файла или консоли. Далее он распознает эту информацию и обрабатывает нужным образом.

Сканер выполняет поиск токенов во входной строке.

2. Что такое токен в Scanner.


Токен — это серия цифровых или буквенно-цифровых символов, которые заканчиваются разделителем. Стандартными разделителями являются: пробел ' ', символ табуляции '\t', перевода строки '\n' и возврата каретки '\r', который должен иметь тип String.

3. Отличие Scanner от BufferedReader.


3.1. BufferedReader работает синхронно, а Scanner — нет. BufferedReader следует использовать, если мы работаем с несколькими потоками.

3.2. BufferedReader имеет значительно большую буферную память, чем Scanner.

3.3. Сканер имеет небольшой буфер (1KB буфер символов или 1024 байта) в отличие от BufferedReader (8KB байтовый буфер или 8192 байта), но этого более чем достаточно.

3.4. BufferedReader немного быстрее по сравнению со сканером, потому что сканер выполняет синтаксический анализ входных данных, а BufferedReader просто читает последовательность символов.

**Файл

1. Абсолютный и относительный путь.


Пути бывают двух типов: абсолютные и относительные. Абсолютный путь начинается с корневой директории. Для Windows это может быть папка c:\, для Linux — директория /

Относительный путь считается относительно какой-то директории. Т.е. это как бы конец пути, но только без начала. Относительный путь можно превратить в абсолютный и наоборот.

Метод boolean isAbsolute() - Метод проверяет, является ли текущий путь абсолютным.

2. Методы File


createNewFile() - создает новый файл по пути, который передан в конструкторе;

delete() - удаляет каталог или файл по пути, который передан в конструкторе. Директория или файл должны быть пустыми;

exists() - проверяет, существует ли по указанному в конструкторе пути файл или каталог;

getAbsolutePath() - возвращает абсолютный путь для пути, который передан в конструктор

getName() - возвращает краткое имя файла или каталога

length() - возвращает размер файла в байтах

mkdir() - создает новый каталог и при удачном создании возвращает значение true

3. Как создать файл на компьютере с помощью java.


Для создания нового файла, во время инициализации объекта File, мы должны предоставить ему имя файла, а затем создать сам новый файл вызовом метода createNewFile().

Создать файл в Java с помощью объекта File можно тремя способами:

1. Передав в объект абсолютный путь

2. Указать только имя файла

3. Указать относительный путь

4. Как удалить директорию с файлами. Что если в ней есть вложенные директории.


Чтобы удалить директорию с файлами, сначала необходимо удалить сами файлы в папке, а затем и саму папку.

5. В чём отличие File от Path.


File - класс, самая старая версия работы с папками и файлами

Его методы возвращают тру/фолс

Path - интерфейс, гораздо новый, его методы выбрасывают исключение в случае проблем.

**Клонирование

1. Что такое клонирование. Как реализовано клонирование в Java.


Точной копией оригинала является его клон. В Java это означает возможность создавать объект с аналогичной структурой, как и у исходного объекта. Метод clone() обеспечивает эту функциональность.

Клонировать объект в Java можно тремя способами:

1. Переопределение метода clone() и реализация интерфейса Cloneable; (Поверхностное клонирование)

2. Использование конструктора копирования; (Глубокое клонирование)

3. Использовать для клонирования механизм сериализации;

2. Клонирование объекта. Глубокое и поверхностное.


Java поддерживает два типа клонирования — поверхностное клонирование и глубокое клонирование.

2.1. Первый способ подразумевает, что вы будете использовать механизм так называемого «поверхностного клонирования» и сами позаботитесь о клонировании полей-объектов. Метод clone() в родительском классе Object является protected, поэтому требуется переопределение его с объявлением как public. Он возвращает экземпляр объекта с копированными полями-примитивами и ссылками. И получается что у оригинала и его клона поля-ссылки указывают на одни и те же объекты.

2.2. Следующий способ заключается в использовании конструктора копирования: В классе описывается конструктор, который принимает объект этого же класса и инициализирует значениями его полей поля нового объекта. О реализации инициализации полей полностью должен позаботиться разработчик класса.

2.3. Но оба вышеуказанных способа полны потенциальных ошибок и по сути создают копию объекта. Наиболее удобным и гибким способом клонирования является механизм сериализации. Если нет особой необходимости обработки полей во время клонирования объектов, то сериализация является наиболее предпочтительным вариантом для этих целей.

Поверхностное клонирование применяется только с примитивными полями.

Глубокое с примитивными и ссылочными.

3. Чем отличается копирование от клонирования.


При копировании объектов, мы создаем не новый объект, а объект, который ссылается на копируемый. Т.е изменения, которые мы произведем во втором объекте, будут также отражены и в первом.

При клонировании, мы создаем отдельную копию первого объекта, но со своей собственной ссылкой. При этом каждый объект будет иметь свою собственную ссылку.

4. Можно ли клонировать String, массив String.


Класс String представляет неизменяемую строку. Нет смысла клонировать String. Если вы чувствуете, что вам нужно клонировать его, вы можете просто повторно использовать ту же ссылку и добиться того же эффекта.

Даже если бы вы могли clone s1 как s2, тогда s1 != s2 все равно был бы true. Они по-прежнему будут ссылками на отдельные объекты.

Массив String клонировать можно, т.к он реализует интерфейс Cloneable.
**Сериализация

1. Что такое сериализация и десериализация.


Сериализация — процесс сохранения состояния объекта в последовательность байт.

Десериализация — процесс восстановления объекта из этих байт.

Любой Java объект можно преобразовать в последовательность байт.

Одно уточнение — чтобы появилась возможность сериализовать объект, он должен наследовать интерфейс Serializable.

Данный интерфейс не имеет каких-либо методов, а просто дает понять системе, что объект, который реализует его, может быть сериализован.

Для сериализации объектов в поток используется класс ObjectOutputStream. Он записывает данные в поток.

Для десериализации используется класс ObjectInputStream

2. Назовите несколько форматов сериализации.


Основные форматы сериализации:

2.1. JavaScript Object Notation (JSON)

2.2. Yet Another Markup Language (YAML)

2.4. XML

2.3. BSON (binary JSON)

3. Как сериализовать объект класса.


Чтобы сериализовать объект класса, мы сначала создаем файл, в который будем записывать объект, затем преобразуем объект в байты и с помощью метода writeObject() сохраняем.

4. Что делать, если одно из полей сериализовывать не нужно.


Чтобы сериализовать определенные поля, можно воспользоваться одним из двух методов:

1. Использовать ключевое слово transient

2. Вместо реализации Seriazable использовать его расширение — интерфейс Externalizable

При использовании слова transient мы явно указываем, какое поле или поля не нужно сериализовывать.

При использовании интерфейса Externalizable нам необходимо переопределить два метода writeExternal() и readExternal(). В методе writeExternal() мы указываем, какие поля будут сериализованы, а в readExternal() как прочитать эти поля.

5.1./// Какие поля не сериализуются.


При стандартной сериализации, поля с модификатором static не сериализуются. Соответственно, после десериализации это поле значение не поменяет, т.к. static поля принадлежат классу, а не объекту.

И transient поля. А поля final, невозможно десериализовать при использовании Externalizable.

5. Как сериализовать статическое поле.


При стандартной сериализации, поля с модификатором static не сериализуются. Соответственно, после десериализации это поле значение не поменяет.

При использовании реализации Externalizable сериализовать и десериализовать статическое поле можно, но не рекомендуется этого делать, т.к. это может сопровождаться трудно уловимыми ошибками.

6. Особенность сериализации поля final.


Поля с модификатором final сериализуются, как обычные. За одним исключением — их невозможно десериализовать при использовании Externalizable. Все final-поля инициализируются при вызове конструктора по умолчанию, и после этого их значение уже невозможно изменить. Поэтому для сериализации объектов, содержащих final-поля, необходимо использовать стандартную сериализацию через Serializable.

7. Externalizable vs Serializable


При записи Serializable класса весь контроль над сериализацией достается JVM.

Интерфейс Externalizable расширяет Serializable и добавляет методы записи и чтения writeExternal и readExternal. Этот интерфейс позволяет реализовать полностью свой механизм сериализации, стандартно запишется только идентификатор класса. У него есть свои плюсы:

1) Производительность

2) Гибкость. Serializable вообще не позволяет управлять процессом, кроме исключения полей или добавление (через свою реализацию методов writeObject/readObject).

3) Безопасность. Иногда необходимо шифрование данных.

Любой класс, имплементирующий интерфейс Externalizable и его наследники, обязан иметь конструктор по умолчанию!

Расширенный алгоритм сериализации (Externalizable) может быть использован, если данные содержат «конфиденциальную» информацию. В этом случае имеет смысл шифровать сериализуемые данные и дешифровать их при десериализации, что требует реализацию собственного алгоритма.

8. Что если при десериализации поменять тип.


Если при десериализации поменять тип поля, то можно получить исключение ClassCastException.

Например, если при десериализации я захочу изменить тип данных со строки на список, то получу исключение, потому что он не может преобразовать строку в список.

9. Что будет если перед десериализацией изменится класс, почему будет выброшено исключение.


Изменится поле private static final long serialVersionUID. А затем будет выброшено исключение java.io.InvalidClassException. Если же поле создать явно и присвоить значение, то дальше уже всё зависит от того какие изменения произошли в классе, совместимые или несовместимые для сериализации.

10. Что будет при сериализации объекта у которого есть поле и оно не Serializable.


Код скомпилируется и выкинет исключение: Exception in thread "main" java.io.NotSerializableException!

Если это поле находится в классе, от которого наследуется класс, над которым будет проходить сериализация, то это поле должно быть доступно напрямую или через геттеры и сеттеры классу, над которым будет проходить сериализация. В этом классе придется реализовать пользовательскую сериализацию.

Общие вопросы

1. Отличие пакетов IO и NIO, InputStream от Reader.


Основное отличие IO от NIO в том, что IO потокоориентированный , а NIO буфера-ориентированным.

Потокоориентированный ввод/вывод подразумевает чтение/запись из потока/в поток одного или нескольких байт в единицу времени поочередно.

Подход, на котором основан Java NIO немного отличается. Данные считываются в буфер для последующей обработки.

1.2. InputStream и Reader


InputStream - это сырой метод получения информации из ресурса. Он захватывает данные байт за байтом, не выполняя никакого перевода. Если вы читаете данные изображения или любой двоичный файл, этот поток следует использовать.

Reader предназначен для потоков символов. Если информация, которую вы читаете, полностью состоит из текста, то Reader позаботится о декодировании символов и предоставит символы юникода из необработанного входного потока.

2. Что такое System.in, что такое System.out.


System.in — это "стандартный" поток ввода и объект InputStream. Этот поток уже открыт и готов к подаче входных данных. Обычно этот поток соответствует вводу с клавиатуры или другому источнику ввода, заданному окружением хоста или пользователем.

System.out — это "стандартный" поток вывода и объект PrintStream. Этот поток уже открыт и готов к приему выходных данных. Обычно этот поток соответствует выводу на дисплей или другому месту вывода, указанному окружением хоста или пользователем.

3. Какиеинтерфейсыреализует InputStream/ OutputStream/ Reader/ Writer.


InputStream реализует интерфейсы: Closeable и AutoCloseable

OutputStream реализует интерфейсы: Closeable, Flushable, AutoCloseable

Reader реализует интерфейсы: Closeable, AutoCloseable, Readable

Writer реализует интерфейсы: Closeable, Flushable, Appendable, AutoCloseable

4. Что такое паттерн адаптер.


Паттерн проектирования адаптер — это такой шаблон проектирования, который позволяет обернуть несовместимые объекты в адаптер, чтобы сделать их совместимыми друг с другом.

5. Пример адаптера и декоратора из IO.


Декоратор имеет альтернативное название — обёртка. Оно более точно описывает суть паттерна: вы помещаете целевой объект в другой объект-обёртку, который запускает базовое поведение объекта, а затем добавляет к результату что-то своё.

Адаптер меняет интерфейс существующего объекта. Декоратор улучшает другой объект без изменения его интерфейса. Причём Декоратор поддерживает рекурсивную вложенность, чего не скажешь об Адаптере.

Адаптер предоставляет классу альтернативный интерфейс, а Декоратор предоставляет расширенный интерфейс.

6. Разница между mkdir и mkdirs


В классе File имеются еще два полезных служебных метода: mkdir () и

mkdirs (). В частности, метод mkdir () создает каталог, возвращая логическое

значение true при удачном исходе операции и логическое значение false при

неудачном ее исходе. Операция создания каталога может завершиться неудачно

по разным причинам. Например, путь, указанный в объекте типа F i l е, уже существует, или каталог не может быть создан, потому что полный путь к нему еще не существует. Чтобы создать каталог, путь к которому еще не создан, следует вызвать

метод mkdirs ().Он создаст как сам каталог, так и все его родительские каталоги.

7. Блокирующий и неблокирующий ввод/вывод


Потоки ввода/вывода (streams) в Java IO являются блокирующими.

Это значит, что когда в потоке выполнения (tread) вызывается read() или write() метод любого класса из пакета java.io.*, происходит блокировка до тех пор, пока данные не будут считаны или записаны. Поток выполнения в данный момент не может делать ничего другого.

Неблокирующий режим Java NIO позволяет запрашивать считанные данные из канала (channel) и получать только то, что доступно на данный момент, или вообще ничего, если доступных данных пока нет. Вместо того, чтобы оставаться заблокированным пока данные не станут доступными для считывания, поток выполнения может заняться чем-то другим



Начало формы

 

Конец формы


написать администратору сайта