java 5 модуль. 5модуль кора. Что такое потоки ввода и вывода. Как это реализовано в Java
Скачать 99.16 Kb.
|
Что такое потоки ввода и вывода. Как это реализовано в Java? Разделяют 2 вида потоков байтовые и системные. Байтовые: java.io.InputStream,java.io.OutputStream; Символьные: java.io.Reader, java.io.Writer; ------------------------------------------------------------------------------------ Что такое InputStream и OutputStream? Абстрактные классы InputStream и OutputStream нужны для того, чтобы абстрагировать различные способы ввода и вывода, независимо от того, является ли поток файлом, веб- страницей или картинкой. InputStream используется для считывания информации. OutputStream используется для записи информации. Что такое InputStream и OutputStream. Это абстрактные классы отвечающие за считывание и запись потоков байтов. Отличие InputStream от Reader 1) InputStream/OutputStream - это потоки байтов (byte). Reader/Writer - потоки символов (char) 2) Reader/Writer используют I/OStream для чтения/записи байтов, а затем расшифровывают байты как символы. ------------------------------------------------------------------------------------------------ Отличие пакетов IO и NIO, InputStream от Reader. Основное отличие IO от NIO в том, что IO , а NIO буферо-ориентированным Потокоориентированный ввод/вывод подразумевает чтение/запись из потока/в поток одного или нескольких байт в единицу времени поочередно. Подход, на котором основан Java NIO немного отличается. Данные считываются в буфер для последующей обработки. Отличие пакетов IO и NIO IO - Потокоориентированный - Блокирующий (синхронный) ввод/вывод NIO - Буфер-ориентированный - Неблокирующий (асинхронный) ввод/вывод - Селекторы (?) InputStream и Reader InputStream - это сырой метод получения информации из ресурса. Он захватывает данные байт за байтом, не выполняя никакого перевода. Если вы читаете данные изображения или любой двоичный файл, этот поток следует использовать. Reader предназначен для потоков символов. Если информация, которую вы читаете, полностью состоит из текста, то Reader позаботится о декодировании символов и предоставит символы юникода из необработанного входного потока. ------------------------------------------------------------------------------------------------ Блокирующий и неблокирующий ввод/вывод Потоки ввода/вывода (streams) в Java IO являются блокирующими. Это значит, что когда в потоке выполнения (tread) вызывается read() или write() метод любого класса из пакета java.io.*, происходит блокировка до тех пор, пока данные не будут считаны или записаны. Поток выполнения в данный момент не может делать ничего другого. Неблокирующий режим Java NIO позволяет запрашивать считанные данные из канала (channel) и получать только то, что доступно на данный момент, или вообще ничего, если доступных данных пока нет. Вместо того, чтобы оставаться заблокированным пока данные не станут доступными для считывания, поток выполнения может заняться чем-то другим. ----------------------------------------------------------------------------------------------------------- Какие интерфейсы реализует InputStream/ OutputStream/ Reader/ Writer? InputStream реализует интерфейсы: Closeable и AutoCloseable OutputStream реализует интерфейсы: Closeable, Flushable, AutoCloseable Reader реализует интерфейсы: Closeable, AutoCloseable, Readable Writer реализует интерфейсы: Closeable, Flushable, Appendable, AutoCloseable ----------------------------------------------------------------------------------------------------------- Проблемы класса java.io.File Проблемы класса java.io.File: 1) Нет метода copy() для копирования файла/каталога. 2) Методы в основном возвращают boolean, а не бросают исключение. Сложно искать ошибки. 3) Нельзя создать символическую ссылку. 4) Неэффективно обрабатывает файлы/каталоги, проблема с масштабированием. 5) Ограниченный набор атрибутов файлов. (права доступа, владелец) ---------------------------------------------------------------------------------------------------------- Что такое «символьная ссылка»? Символьная (символическая) ссылка (также «симлинк», Symbolic link) — специальный файл в файловой системе, в котором, вместо пользовательских данных, содержится путь к файлу, который должен быть открыт при попытке обратиться к данной ссылке (файлу). Целью ссылки может быть любой объект: например, другая ссылка, файл, каталог или даже несуществующий файл (в последнем случае, при попытке открыть его, должно выдаваться сообщение об отсутствии файла). Символьные ссылки используются для более удобной организации структуры файлов на компьютере, так как: позволяют для одного файла или каталога иметь несколько имён и различных атрибутов; свободны от некоторых ограничений, присущих жёстким ссылкам (последние действуют только в пределах одной файловой системы (одного раздела) и не могут ссылаться на каталоги). ----------------------------------------------------------------------------------------------------------- Абсолютный и относительный путь Абсолютный путь всегда содержит корневой элемент и полный список каталогов, для нахождения файла. Например: "c:\\windows\\projects\\note.txt" — это абсолютный путь. Для проверки, является ли путь абсолютным, есть метод isAbsolute(). Относительный путь необходимо соединить с другим путем, чтобы получить доступ к файлу. Например, joe/foo — это относительный путь. Без дополнительной информации, программа не сможет достоверно определить местоположение каталога joe/foo в файловой системе. ------------------------------------------------------------------------------------------------------------- Что такое паттерн адаптер и паттерн декоратор Паттерн проектирования адаптер — это такой шаблон проектирования, который позволяет обернуть несовместимые объекты в адаптер, чтобы сделать их совместимыми друг с другом. Паттерн проектирования декоратор — шаблон декоратор позволяет вам динамически изменять поведение объекта во время работы, оборачивая их в объект класса декоратора. Представим, что у вас есть свой автосервис. Как вы будете рассчитывать сумму в счете за услуги? Вы выбираете одну услугу и динамически добавляете к ней цены на предоставляемые услуги, пока не получите окончательную стоимость. Здесь каждый тип сервиса является декоратором. На каком паттерне основана иерархия потоков ввода/вывода? Объекты классов Java, которые используются для ввода/вывода, для обеспечения необходимой функциональности наслаиваются друг на друга. Такая модель взаимодействия объектов возможна в паттерне "Декоратор". В этом паттерне, при создании потока, нужно использовать несколько объектов. Что такое декоратор в IO Декоратор - это класс который сохраняет тип, но добавляет какую то функциональность Reader/Writer - BufferedReader/Writer InputStream/Output - BufferedInputStream/Output GZIPInputStream/Output Что такое Адаптер Адаптер — это структурный паттерн проектирования, который позволяет объектам с несовместимыми интерфейсами работать вместе. InputStreamReader, FileReader, FileInputStream ------------------------------------------------------------------------------------------------------------- Как преобразовать считанные байты в символы? Какой класс для этого используется? OutputStreamWriter — «мост» между классом OutputStream и классом Writer. Символы, записанные в поток, преобразовываются в байты. InputStreamReader — аналог для чтения. При помощи методов класса Reader читаются байты из потока InputStream и далее преобразуются в символы. ------------------------------------------------------------------------------------------------------------ Какие классы позволяют ускорить чтение/запись за счет использования буфера? BufferedInputStream(InputStream in)/BufferedInputStream(InputStream in, int size), BufferedOutputStream(OutputStream out)/BufferedOutputStream(OutputStream out, int size), BufferedReader(Reader r)/BufferedReader(Reader in, int sz), BufferedWriter(Writer out)/BufferedWriter(Writer out, int sz) ------------------------------------------------------------------------------------------------------------ Конструкторы и методы класса File. - File(String path) - указывается путь к файлу - File(File dir, String name) - указывается объекта класса File (каталог) и имя файла - File(String dirPath, Sring name) - указывается путь к файлу и имя файла - File(URI uri) - указывается объекта URI, описывающий файл. (Универсальный Идентификатор ресурса (URI) ссылка.) Методы: - exists() - isFile() - isDirectory() - length() - getParent() - getName() - getPath(), getAbsolutePath() - list() - string - listFile() - filepath - delete() - renameTo() Методы класса File createNewFile() - создает новый файл по пути, который передан в конструкторе; delete() - удаляет каталог или файл по пути, который передан в конструкторе; exists() - проверяет, существует ли по указанному в конструкторе пути файл или каталог; getAbsolutePath() - возвращает абсолютный путь для пути, который передан в конструктор getName() - возвращает краткое имя файла или каталога length() - возвращает размер файла в байтах mkdir() - создает новый каталог и при удачном создании возвращает значение true --------------------------------------------------------------------------------------------------- Что вернет метод read(), если он считывает файл и ему встречается байт равный -1? Если методу read() встречается байт равный -1, то он вернет значение, к которому применилось побитовое "И" с числом 255. Т.е вернёт 255. Что делает метод read. Почему он возвращает int а не byte. Почему он не может возвращать byte? Метод read() используется для чтения символов. Данный метод возвращает кол-во только что прочитанных символов (целое число от 0 до 65635) или -1, если достиг конца stream(потока). Он возвращает int, потому что диапазон byte в Java лежит от -127 до 127, а возвращаемое значение метода read() лежит в диапазоне от 0 до 255. Чтобы получит представление byte в int, в методе read() используется побитовое "И" с числом 255, т.е убираются лидирующие единицы. -------------------------------------------------------------------------------------------------------- Что возвращает перегруженный метод read() с массивом? read() метод имеет перегруженная версия который может считывать байты данных заданной длины из входного потока в массив байтов. Мы можем использовать этот метод для одновременного чтения всего файла в массив байтов. Если файл слишком велик для чтения за одну операцию чтения, мы можем открыть входной поток байтов, используя InputStream и повторно читать байты данных из него в массив байтов, используя другой перегруженный метод read(byte[], int, int) пока не будет достигнут конец файла. Возвращает следующий байт входного потока типа int int - чтобы обозначить конец потока -1 и использовать числа от 128 до 256 вернет 255 Вернет int - количество реально прочитанных байт ------------------------------------------------------------------------------------------------------------- Что делает метод available()? Метод available() проверяет, есть ли данные в файле и возвращает примерное количество доступных для чтения байтов. ----------------------------------------------------------------------------------------------------- что делает flush? выполнится ли flush если мы сделаем close? Flush очищает буфер и скидывает содержимое в поток Да, выполнится В соответствии с документами java, вызывая close() на любом потоке java.io, автоматически вызывает flush(). Но я видел много примеров, даже в производственных кодексах разработчики явно использовали flush() непосредственно перед close() --------------------------------------------------------------------------------------------------------------- Можно ли использовать flush() для небуферизированного потока и что будет? Метод flush() для небуферезированного потока использовать можно, т.к исходя из документации, "среда хоста может выполнять свою собственную буферизацию, неизвестную Java. В этом случае запись можешь кэшироваться в буферах ОС". ------------------------------------------------------------------------------------------------------------------ Почему важно закрывать потоки? При закрытии потока освобождаются все выделенные для него ресурсы, например, файл. В случае, если поток окажется не закрыт, может произойти утечка памяти. ---------------------------------------------------------------------------------------------------------------- Какие потоки можно не закрывать (не вызывать метод close())? В JDK 7 метод close() определяется интерфейсом AutoCloseable и можно явно не закрывать поток, а использовать оператор try-with-resources, в котором в неявно созданном finally будет вызываться метод close(). --------------------------------------------------------------------------------------------------------- Как создать файл на компьютере с помощью Java? Для создания нового файла, во время инициализации объекта File, мы должны предоставить ему имя файла, а затем создать сам новый файл вызовом метода createNewFile(). Создать файл в Java с помощью объекта File можно тремя способами: 1. Передав в объект абсолютный путь 2. Указать только имя файла 3. Указать относительный путь Как создать файл? Создать экземпляр класса io.File или nio.Files 1) java.io: (File) file.createNewFile() 2) java.nio: Files.createFile() ---------------------------------------------------------------------------------------------------------- Разница между mkdir и mkdirs javadocs для mkdirs() : Создает каталог с именем этого абстрактного пути, включая все необходимые, но несуществующие родительские каталоги. Обратите внимание, что в случае сбоя этой операции, возможно, удалось создать некоторые из необходимых родительских каталогов. javadocs для mkdir() : Создает каталог с именем этого абстрактного пути. ---------------------------------------------------------------------------------------------------- Как удалить директорию с файлами. Что если в ней есть вложенные директории? Чтобы удалить директорию с файлами, сначала необходимо удалить сами файлы в папке, а затем и саму папку. Как удалить директорию с файлами? Что если в ней есть вложенные директории? Необходимо воспользоваться методом delete(Path path) класса Files. Пример кода: Files.delete(Path.of(“c:\\desktop); Директория будет удалена только если она пустая. Вложенные директории удаляются с помощью рекурсии. Как удалить директорию с файлами file.delete() Нужно прописать вручную рекурсивный обход для удаления всех файлов в директории и поддиректорий для класса nio.Files использовать метод walkFileTree() ----------------------------------------------------------------------------------------------------- В чём отличие File от Path File - класс, самая старая версия работы с папками и файлами Его методы возвращают тру/фолс Path - интерфейс, гораздо новый, его методы выбрасывают исключение в случае проблем ------------------------------------------------------------------------------------------------------------ Что вы знаете о RandomAccessFile (java.io) RandomAccessFile в Java предоставляет возможность читать с файла и записывать данные в файл. RandomAccessFile работает с файлом как с большим массивом байтов. Он использует курсор с помощью которого мы можем переместить указатель файла в определенную позицию. При создании экземпляра RandomAccessFile, мы должны выбрать режим файла, например "r", если вы хотите прочитать данные с файла или "rw" — если вы хотите читать с файла и писать в файл. С помощью указателя файла мы можем читать из файла или записывать данные в файл в любом месте. Чтобы получить текущий указатель файла, используется метод getFilePointer(). Чтобы установить индекс указателя файла используется метод seek(int i). Следует отметить, что если мы пишем в файл по индексу, где данные уже присутствуют, то они будут заменены. RandomAccessFile имеет такие специфические методы как: getFilePointer() для определения текущего местоположения в файле; seek() для перемещения на новую позицию в файле; length() для выяснения размера файла; setLength() для установки размера файла; skipBytes() для того, чтобы попытаться пропустить определённое число байт; getChannel() для работы с уникальным файловым каналом, ассоциированным с заданным файлом; методы для выполнения обычного и форматированного вывода из файла (read(), readInt(), readLine(), readUTF() и т.п.); методы для обычной или форматированной записи в файл с прямым доступом (write(), writeBoolean(), writeByte() и т.п.). ------------------------------------------------------------------------------------------------------------------- Что вы знаете об интерфейсе FilenameFilter? Интерфейс java.io.FilenameFilter может быть реализован для фильтрации имен файлов в определенной папке. Интерфейс FilenameFilter содержит метод boolean accept(File dir, String name). Класс должен реализовывать этот метод, а каждый тестируемый файл должен быть включен в общий список файлов. Метод accept как пример, проверяет имя файла на наличие определенного расширения. ------------------------------------------------------------------------------------------------------------------- Какие классы позволяют архивировать объекты? Для этого в пакете java.util.zip определены два класса - ZipInputStream и ZipOutputStream. Для создания архива используется класс ZipOutputStream. Для создания объекта ZipOutputStream в его конструктор передается поток вывода: ZipOutputStream(OutputStream out). Для чтения архивов применяется класс ZipInputStream. В конструкторе он принимает поток, указывающий на zip-архив --------------------------------------------------------------------------------------------------------------- Для чего нужен Scanner? Сканер нужен для считывания данных из источника, который мы для него указываем. Например, из строки, файла или консоли. Далее он распознает эту информацию и обрабатывает нужным образом. Сканер выполняет поиск токенов во входной строке. ---------------------------------------------------------------------------------------------------------- Какие методы класса Scanner вы помните? 1) (has)next() 2) (has)nextByte (Short, Int, Long, Double, 3) (has)nextBigInteger (BigDecimal) 4) (has)nextLine() 6) reset() 7) locale() 7) close() - вызывает также close() у обернутого потока. ------------------------------------------------------------------------------------------------------------------------ Что такое токен в Scanner? Токен — это серия цифровых или буквенно-цифровых символов, которые заканчиваются разделителем. ---------------------------------------------------------------------------------------------------------- Отличие Scanner'a от BufferedReader'a BufferedReader имеет значительно большую буферную память (8192 символа), чем . Используйте BufferedReader, если необходимо просто получить длинные строки из потока, и используйте , если хотите разобрать определенный тип токенов из потока. BufferedReader является синхронным, а — нет. Используйте , если хотите работать с несколькими потоками. Scanner скрывает IOException, в то время как бросает его немедленно. Отличие Scanner'a от BufferedReader'a? - BufferedReader только считывает данные, Scanner также анализирует их определяя тип (int, long или float). - Размер буфера BufferedReader велик(8 КБ) по сравнению с 1 КБ Scanner. - BufferedReader больше подходит для чтения файла с длинной строкой , в то время как сканер больше подходит для чтения небольшого пользовательского ввода из командной строки. - BufferedReader синхронизируется, Scanner - нет - BufferedReader быстрее сканера, потому что он не тратит время на синтаксический анализ ---------------------------------------------------------------------------------------- Есть ли у сканера буфер? У сканера есть буфер, но он довольно маленький — всего 1024 символа. -------------------------------------------------------------------------------------------------- Что такое System.in, что такое System.out? System.in — это "стандартный" поток ввода и объект InputStream. Этот поток уже открыт и готов к подаче входных данных. Обычно этот поток соответствует вводу с клавиатуры или другому источнику ввода, заданному окружением хоста или пользователем. System.out — это "стандартный" поток вывода и объект PrintStream. Этот поток уже открыт и готов к приему выходных данных. Обычно этот поток соответствует выводу на дисплей или другому месту вывода, указанному окружением хоста или пользователем. ------------------------------------------------------------------------------------------------------------ Что такое клонирование. Как реализовано клонирование в Java? Иногда нужно на основе существующего объекта создать второй такой же, то есть - создать его клон. Этот процесс в Java называется клонированием. Клонировать объект в Java можно тремя способами: 1. Переопределение метода clone() и реализация интерфейса Cloneable; 2. Использование конструктора копирования; В классе описывается конструктор, который принимает объект этого же класса и инициализирует значениями его полей поля нового объекта. 3. Использовать для клонирования механизм сериализации; Если нет особой необходимости обработки полей во время клонирования объектов, то сериализация является наиболее предпочтительным вариантом для этих целей. -------------------------------------------------------------------------------------------------------- Клонирование объекта. Глубокое и поверхностное. Поверхностное копирование копирует настолько маленькую часть, насколько это возможно. По умолчанию, копирование в Java является поверхностным, т.е Object class не знает о структуре класса, которого он копирует. Метод clone() возвращает экземпляр объекта с копированными полями-примитивами и ссылками. И получается что у оригинала и его клона поля-ссылки указывают на одни и те же объекты. Глубокое копирование дублирует всё. Глубокое копирование — это две коллекции, в одну из которых дублируются все элементы оригинальной коллекции. Мы хотим сделать копию, при которой внесение любых изменений в копию не затронет оригинальную коллекцию. Клонирование объекта. Глубокое и поверхностное. Клонирование объекта - это процесс создания идентичной копии объекта. Метод clone() По умолчанию клонирование - поверхностное. Поверхностное - копирование полей примитивных типов и ссылок клонируемого объекта; Глубокое копирование дублирует все. Глубокое копирование — это две коллекции, в одну из которых дублируются все элементы оригинальной коллекции. Внесение изменений в любой элемент копии не затронет оригинальную коллекцию. ----------------------------------------------------------------------------------------------------------------- Чем отличается копирование от клонирования? При копировании объектов, мы создаем не новый объект, а объект, который ссылается на копируемый. Т.е изменения, которые мы произведем во втором объекте, будут так же отражены и в первом. При клонировании, мы создаем отдельную копию первого объекта, но со своей собственной ссылкой. При этом каждый объект будет иметь свою собственную ссылку. ------------------------------------------------------------------------------------------------------------------ Можно ли клонировать String, массив String? Класс String представляет неизменяемую строку. Нет смысла клонировать String. Если вы чувствуете, что вам нужно клонировать его, вы можете просто повторно использовать ту же ссылку и добиться того же эффекта. Даже если бы вы могли clone s1 как s2, тогда s1 != s2 все равно был бы true. Они по-прежнему будут ссылками на отдельные объекты. Массив String клонировать можно, т.к он реализует интерфейс Cloneable ---------------------------------------------------------------------------------------------------------------- Что такое «каналы»? Каналы (channels) – это логические (не физические) порталы, абстракции объектов более низкого уровня файловой системы (например, отображенные в памяти файлы и блокировки файлов), через которые осуществляется ввод/вывод данных, а буферы являются источниками или приёмниками этих переданных данных. При организации вывода, данные, которые необходимо отправить, помещаются в буфер, который затем передается в канал. При вводе, данные из канала помещаются в заранее предоставленный буфер. --------------------------------------------------------------------------------------------------------------- Что такое сериализация и десериализация? Сериализация — процесс сохранения состояния объекта в последовательность байт. Десериализация — процесс восстановления объекта из этих байт. Любой Java объект можно преобразовать в последовательность байт. Одно уточнение — чтобы появилась возможность сериализовать объект, он должен наследовать интерфейс Serializable. Данный интерфейс не имеет каких-либо методов, а просто дает понять системе, что объект, который реализует его, может быть сериализован. Для сериализации объектов в поток используется класс ObjectOutputStream. Он записывает данные в поток. Для десериализации используется класс ObjetInputStream Сериализация и десериализация Сериализация — это процесс сохранения состояния объекта в последовательность байт. Десериализация — это процесс восстановления объекта из этих байт При использовании Serializable десериализация происходит так: под объект выделяется память, после чего его поля заполняются значениями из потока. Конструктор объекта при этом не вызывается. При десериализации вызывается конструктор без параметров родительского НЕсериализуемого класса. И если такого конструктора не будет – при десериализации возникнет ошибка. Конструктор же дочернего объекта, того, который мы десериализуем, не вызывается -------------------------------------------------------------------------------------------------------------- Назовите несколько форматов сериализации Основные форматы сериализации: JSON YAML XML BSON Назовите несколько форматов сериализации. - JSON. JavaScript Object Notation - BSON (binary JSON) - YAML. Yet Another Markup Lang. YAML Ain't ML - XML. ------------------------------------------------------------------------------------------------------------------ Как сериализовать объект класса? Чтобы сериализовать объект класса, мы сначала создаем файл, в который будем записывать объект, затем преобразуем объект в байты и с помощью метода writeObject() сохраняем. Как сериализовать объект класса 1) Класс объекта должен реализовывать интерфейс Serializable 2) Создать поток ObjectOutputStream (oos), который записывает объект в переданный OutputStream. 3) Записать в поток: oos.writeObject(Object); 4) Сделать oos.flush() и oos.close()- нет !!! try with resurses!!! --------------------------------------------------------------------------------------------------------- Что делать, если одно из полей сериализовывать не нужно? Чтобы сериализовать определенные поля, можно воспользоваться одним из двух методов: 1. Использовать ключевое слово transient 2. Вместо реализации Seriazable использовать его расширение — интерфейс Externalizable При использовании слова transient мы явно указываем, какое поле или поля не нужно сериализовывать. При использовании интерфейса Externalizable нам необходимо переопределить два метода writeExternal() и readExternal(). В методе writeExternal() мы указываем, какие поля будут сериализованы, а в readExternal() как прочитать эти поля. Что делать, если одно из полей сериализовывать не нужно. Использовать модификатор transient для этого поля Или сделать поле статичным -------------------------------------------------------------------------------------------------------------- Как сериализовать статическое поле? При стандартной сериализации, поля с модификатором static не сериализуются. Соответственно, после десериализации это поле значение не поменяет. При использовании реализации Externalizable сериализовать и десериализовать статическое поле можно, но не рекомендуется этого делать, т.к. это может сопровождаться трудноуловимыми ошибками. Как сериализовать статическое поле. Статические члены связаны с классом, а не с экземплярами, поэтому нет смысла включать их при сериализации экземпляра. Первое решение - сделать эти элементы не статичными. Или, если эти элементы одинаковы в исходном классе и целевом классе (один и тот же класс, но, возможно, разные среды выполнения), не сериализуйте их вообще. Правильный ответ на это заключается в использовании экстернализуемого, а не сериализуемого интерфейса. Нужно в классе, который мы сериализуем создать вручную статические методы для сериализации и десериализации ------------------------------------------------------------------------------------------------------------- Externalizable vs Serializable При записи Serializable класса весь контроль над сериализацией достается JVM. Интерфейс Externalizable расширяет Serializable и добавляет методы записи и чтения writeExternal и readExternal. Этот интерфейс позволяет реализовать полностью свой механизм сериализации, стандартно запишется только идентификатор класса. Externalizable vs Serializable 1) Externalizable extends Serializable 2) Интерфейс Serializable полностью реализует виртуальная машина JVM. Интерфейс Externalizable имеет методы writeExternal() и readExternal(), которые позволяют вручную указать - какие поля записывать. Это может в том числе повысит производительность. 3) Externalizable нужно создавать пустой конструктор при использовании Serializable стандартная сериализация - класс разбирается как набор полей, каждое из которых пишется в выходной поток. При использовании Serializable десериализация происходит так: под объект выделяется память, после чего его поля заполняются значениями из потока. Конструктор объекта при этом не вызывается. При десериализации вызывается конструктор без параметров родительского НЕсериализуемого класса. И если такого конструктора не будет – при десериализации возникнет ошибка. Конструктор же дочернего объекта, того, который мы десериализуем, не вызывается +++При стандартной сериализации поля, имеющие модификатор static, не сериализуются. Соответственно, после десериализации это поле значения не меняет. +++В каждый класс, реализующий интерфейс Serializable, на стадии компиляции добавляется еще одно поле – private static final long serialVersionUID. Это поле содержит уникальный идентификатор версии сериализованного класса. Оно вычисляется по содержимому класса – полям, их порядку объявления, методам, их порядку объявления. Соответственно, при любом изменении в классе это поле поменяет свое значение. Это поле записывается в поток при сериализации класса. Кстати, это, пожалуй, единственный известный мне случай, когда static-поле сериализуется. При десериализации значение этого поля сравнивается с имеющимся у класса в виртуальной машине. Если значения не совпадают – инициируется исключение если набор полей класса и их порядок уже определен, а методы класса могут меняться - вручную в классе определить поле private static final long serialVersionUID. В принципе, значение этого поля может быть абсолютно любым. Некоторые предпочитают ставить его равным дате модификации кода. Некоторые вообще используют 1L. Для получения стандартного значения (того, которое вычисляется внутренним механизмом) можно использовать утилиту serialver, входящую в поставку SDK. После такого определения значение поля будет фиксировано, следовательно, десериализация всегда будет разрешена. При использовании Externalizable ++Сначала вызывается конструктор без параметров, а потом уже на созданном объекте вызывается метод readExternal, который и вычитывает, собственно, все свои данные. Потому – любой реализующий интерфейс Externalizable класс обязан иметь public конструктор без параметров! Более того, поскольку все наследники такого класса тоже будут считаться реализующими интерфейс Externalizable, у них тоже должен быть конструктор без параметров! ++transient - никто не мешает сериализовать это поле, равно как и вычитать его. Если поле объявлено transient, то при десериализации объекта оно принимает значение по умолчанию. ++сериализовать и десериализовать static поле никто не мешает, однако я крайне не рекомендую этого делать, т.к. это может привести к трудноуловимым ошибкам. ++Поля с модификатором final сериализуются как и обычные. За одним исключением – их невозможно десериализовать при использовании Externalizable. есть класс, унаследованный от несериализуемого родителя. - сериализовать экземпляр класса B(наследник) можно. Что для этого нужно? А нужно, чтобы у класса A (родитель) был конструктор без параметров, public либо protected. Тогда при десериализации все переменные класса A будут инициализированы с помощью этого конструктора. Переменные класса B будут инициализированы значениями из потока сериализованных данных. Правило 1. После десериализации объекта необходимо проверить его внутреннее состояние (инварианты) на правильность, точно так же, как и при создании с помощью конструктора. Если объект не прошел такую проверку, необходимо инициировать исключение java.io.InvalidObjectException. Правило 2. Если в составе класса A присутствуют объекты, которые не должны быть доступными для изменения извне, то при десериализации экземпляра класса A необходимо вместо этих объектов создать и сохранить их копии. ----------------------------------------------------------------------------------------------------------------------- Что будет с работой интерфейсов Serizalizable и Externalizable в случае, если переменная - final? 1) Интерфейс Serializable отработает без проблем - он использует рефлексию для создания объекта. При этом используются методы ObjectInputStream.defaultReadObject() и ObjectOutputStream.defaultWriteObject(). 2) Однако, если мы создадим свои методы readObject() и writeObject(), то Serizalizable не сможет присвоить значение переменной: будет жаловаться, что она final. 3) У интерфейса Externalizable будет проблема в методе readExternal(). Он не сможет выполнить this.field = in.readValue(). Потому что создает объект конструктором, а затем меняет значение. Особенность сериализации поля final Поля с модификатором final сереализуются, как обычные. За одним исключением — их невозможно десериализовать при использовании Externalizable. --------------------------------------------------------------------------------------------------------------- Что если при десериализации поменять тип? Если при десериализации поменять тип поля, то можно получить исключение ClassCastException. Например, если при десериализации я захочу изменить тип данных со строки на список, то получу исключение, потому что он не может преобразовать строку в список. ----------------------------------------------------------------------------------------------------------------- Что будет при сериализации объекта у которого есть поле и оно не Serializable? Если это поле находится в классе, от которого наследуется класс, над которым будет проходить сериализация, то это поле должно быть доступно напрямую или через геттеры и сеттеры классу, над которым будет проходить сериализация. В этом классе придется реализовать пользовательскую сериализацию. Ещё один из приемлемых вариантов — это пометить поле transient, чтобы оно игнорировалось во время процессов сериализации и десериализации. ----------------------------------------------------------------------------------------------------------------- |