more scenarios done, started methodics for clean
This commit is contained in:
parent
45e15d385c
commit
399bbd9feb
Binary file not shown.
|
@ -117,7 +117,7 @@ JVM самостоятельно осуществляет сборку так н
|
|||
\begin{figure}[H]
|
||||
\centering
|
||||
\def\svgwidth{150mm}
|
||||
\input{pics/jc-01-jvm-struct.pdf_tex}
|
||||
\includesvg{pics/jc-01-jvm-struct.svg}
|
||||
\label{jvm:structure}
|
||||
\caption{принцип работы JVM}
|
||||
\end{figure}
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
% Вот и настало время разобраться в тонкастях работы. На этом уроке мы начнём с файловой системы, как с ней взаимодействовать в целом, с файлами и каталогами. Мы узнаем как как управлять содержимым файлов, затем перейдём к разбору класса String, а так же разберёмся с сериализаторами и десериализаторами в Java.
|
||||
|
||||
% Что такое файловая система.
|
||||
% Перед тем, как приступить к изучению классов, которые работают с файловой системой, предлагаю разобраться вообще с понятием файловой системы, что она из себя представляет и как с ней взаимодействовать.
|
||||
% Что же такое файловая система (File System) - FS, ФС – один из ключевых компонентов всех операционных систем. В ее обязанности входит структуризация, чтение, хранение, запись файловой документации. Она напрямую влияет на физическое и логическое строение данных, особенности их формирования, управления, допустимый объем файла, количество символов в его названии и пр. В качестве примеров выступают две операционные системы Linux и Windows.
|
||||
% В ядре ОС Линукс предусмотрен огромный набор заблаговременно установленных файловых систем. Их задача – помогать пользователю в решении той или иной поставленной задачи. Для определенного раздела можно выбирать свою систему, ориентируясь на предстоящие потребности: обеспечение быстродействия, гарантированное восстановление информации, повышенная производительность. Речь идет как о стандартных, так и о специализированные либо же виртуальных файловых системах.
|
||||
% В ОС Линукс еще на этапе установки пользователю предоставляется на выбор большое количество ФС, вмонтированных в ее ядро. Пользователь самостоятельно выбирает вариант, который будет соответствовать его запросам и проблемам, требующим решения в рабочем процессе. Обратите внимание: подобное актуально только для операционной системы Linux и Windows NT (с файловой системой новой технологии NTFS). Для обычного Windows не предусмотрено возможности выбора вида файловых систем. Отличными здесь будут и строение каталога, и иерархия самих ФС.
|
||||
% ОС Линукс также предоставляет возможность разбивки жесткого диска персонального компьютера на отдельные разделы. Пользователи могут определить их границы по так называемым таблицам разделов – GPT, MBR. Здесь указывается метка и номер директории, адрес его реального размещения: точки старта и финиша.
|
||||
% Чуток подробнее рассмотрим данные форматы:
|
||||
% Итак, допустим, вы получили новый SSD, подключили и зашли в «Управление дисками», чтобы его инициализировать.
|
||||
% Утилита управления дисками спросит, хотите ли вы использовать стиль разделов MBR или GPT?
|
||||
% Основная загрузочная запись (MBR) и таблица разделов GUID (GPT) — это два стиля формата разделов, которые позволяют вашему компьютеру загружать операционную систему с жесткого диска, а также индексировать и упорядочивать данные.
|
||||
% Для большинства людей предпочтительным стилем разделов должен быть GPT — более новый из двух.
|
||||
% Однако не всегда все бывает просто.
|
||||
|
||||
% Основная загрузочная запись (MBR)
|
||||
% Основная загрузочная запись (MBR) — это устаревшая форма разделения загрузочного сектора. Это первый сектор диска, который содержит информацию о том, как разбит диск. Он также содержит загрузчик, который сообщает вашей машине, как загрузить ОС.
|
||||
% MBR состоит из трех частей:
|
||||
|
||||
% Основной загрузчик;
|
||||
% Таблица разделов диска;
|
||||
% Конечная подпись.
|
||||
|
||||
|
||||
% .tg {border-collapse:collapse;border-spacing:0;}
|
||||
% .tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
|
||||
% overflow:hidden;padding:10px 5px;word-break:normal;}
|
||||
% .tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
|
||||
% font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}
|
||||
% .tg .tg-0pky{border-color:inherit;text-align:center;vertical-align:center}
|
||||
|
||||
|
||||
|
||||
|
||||
% Смещение
|
||||
% Длина, байт
|
||||
% Описание
|
||||
|
||||
|
||||
|
||||
|
||||
% 0000h
|
||||
% 446
|
||||
% Код загрузчика
|
||||
|
||||
|
||||
% 01BEh
|
||||
% 16
|
||||
% Раздел 1
|
||||
% Таблица разделов
|
||||
|
||||
|
||||
% 01CEh
|
||||
% 16
|
||||
% Раздел 2
|
||||
|
||||
|
||||
% 01DEh
|
||||
% 16
|
||||
% Раздел 3
|
||||
|
||||
|
||||
% 01EEh
|
||||
% 16
|
||||
% Раздел 4
|
||||
|
||||
|
||||
% 01FEh
|
||||
% 2
|
||||
% Сигнатура (55h AAh)
|
||||
|
||||
|
||||
|
||||
|
||||
% Основной загрузчик
|
||||
|
||||
% MBR резервирует первые 446 байт дискового пространства для основного загрузчика.
|
||||
% Windows размещает здесь очень упрощенный загрузчик, в то время как другие ОС могут размещать более сложные многоступенчатые загрузчики.
|
||||
|
||||
% Таблица разделов диска
|
||||
|
||||
% Таблица разделов диска находится в цилиндре 0, головке 0 и секторе 1 жесткого диска. Она хранит информацию о том, как разбит диск.
|
||||
% MBR выделяет 16 байт данных для каждой записи раздела и может выделить всего 64 байта. Таким образом, MBR может адресовать не более 4 основных разделов или 3 основных раздела и 1 расширенный раздел. Расширенный раздел используется для создания нескольких логических разделов. Это полезно, когда пользователю нужно создать более 4 разделов. Однако операционная система может быть установлена только в основных разделах, а не в логических разделах.
|
||||
|
||||
% Конечная подпись
|
||||
|
||||
% 2-байтовая подпись отмечает конец MBR.
|
||||
% Всегда устанавливается в шестнадцатеричное значение 0x55AA.
|
||||
% Некоторые особенности основной загрузочной записи:
|
||||
|
||||
% Возможность инициализировать загрузчик в устаревшем режиме BIOS;
|
||||
% Может адресовать до 2 ТБ дискового пространства;
|
||||
% Может иметь 4 основных раздела или 3 основных раздела и 1 дополнительный раздел;
|
||||
% Возможность загрузки Windows 7 и более ранних версий Windows.
|
||||
|
||||
% Плюсы:
|
||||
|
||||
% Совместимость со всеми версиями Windows, включая Windows 7 и более ранние версии;
|
||||
% Требуется для обеспечения совместимости со старым 32-разрядным оборудованием;
|
||||
% Использует 32-битные значения, поэтому имеет меньшие накладные расходы, чем GPT.
|
||||
|
||||
% Минусы:
|
||||
|
||||
% Максимальная емкость раздела составляет 2 ТБ;
|
||||
% Ограничено 4 основными разделами или 3 основными разделами и 1 расширенным разделом;
|
||||
% Не устойчив к повреждению MBR;
|
||||
% Не имеет встроенного исправления ошибок для защиты данных, поскольку использует BIOS.
|
||||
|
||||
|
||||
% Таблица разделов GUID (GPT)
|
||||
% Таблица разделов GUID (GPT) — это стиль формата раздела, который был представлен в рамках инициативы United Extensible Firmware Interface (UEFI). GPT был разработан для архитектурного решения некоторых ограничений MBR.
|
||||
% КАРТИНКА
|
||||
% Стиль GPT новее, гибче и надежнее, чем MBR. GPT использует логическую адресацию блоков (LBA) для указания блоков данных. Первый блок помечен как LBA0, затем LBA1, LBA2, … и так далее.
|
||||
% Каждый логический блок имеет размер 512 байт. GPT хранит защитную MBR в LBA0, основной заголовок GPT в LBA1 и записи разделов в LBA2 — LBA33.
|
||||
% Структура GPT состоит из:
|
||||
|
||||
% Защитная MBR;
|
||||
% Основной тег GPT;
|
||||
% Записи разделов;
|
||||
% Дополнительный GPT.
|
||||
|
||||
|
||||
% Защитная MBR
|
||||
|
||||
% Защитная MBR — это пространство, зарезервированное в GPT для устаревших целей. Оно находится в LBA0. Система, которая не распознает GPT, скорее всего, перезапишет диски GPT. Это обеспечивает обратную совместимость с системами, которые не распознают GPT. Защитная MBR охватывает либо весь диск, либо 2 ТБ, в зависимости от того, что меньше.
|
||||
|
||||
% Основной тег GPT
|
||||
|
||||
% Первичный GPT охватывает LBA1–LBA33 GPT.
|
||||
% LBA1 состоит из основного заголовка GPT, который содержит указатель на таблицу разделов. Он также определяет объем свободного места на диске. Соответствующие записи раздела расположены в LBA2 – LBA33. Каждая запись имеет длину 128 байт, и в одной LBA может храниться 4 записи. Теоретически GPT может иметь бесконечное количество разделов. Однако в Windows GPT может хранить информацию о 128 разделах (32 LBA x 4 записи раздела в каждом LBA).
|
||||
% Эти LBA хранят информацию о разделах диска и их расположении.
|
||||
|
||||
% Разделительные блоки
|
||||
|
||||
% Это используемые блоки диска, отформатированные в разделе в стиле GPT, где хранятся фактические данные.
|
||||
% На диске с 512-байтовыми секторами первый используемый блок находится в LBA34. Каждый блок разделов на диске в формате GPT представляет собой отдельный том. Таким образом, в соответствии с записями в Primary GPT, диск в формате GPT может иметь 128 томов. В отличие от MBR, каждый том в GPT может быть основным томом.
|
||||
% Таким образом, пользователь может иметь до 128 первичных томов, способных разместить 128 загрузчиков на диске в формате GPT.
|
||||
|
||||
% Дополнительный GPT
|
||||
|
||||
% Схема GPT требует, чтобы копия основного GPT хранилась в последних секторах диска.
|
||||
% Обычно они имеют маркировку LBA33 – LBA1.
|
||||
% Это обеспечивает избыточность схемы GPT, которую можно использовать в качестве резервной на случай повреждения или сбоя основного GPT.
|
||||
% Плюсы:
|
||||
|
||||
% Максимальная емкость раздела 9.4ZB (Зеттабайт);
|
||||
% Максимум 128 первичных разделов;
|
||||
% Устойчив к повреждению первичного GPT, поскольку он также имеет вторичный GPT;
|
||||
% Возможность использовать функции UEFI, такие как безопасная загрузка, быстрый запуск и т. д.
|
||||
|
||||
|
||||
% MBR против GPT: различия
|
||||
% Различия между MBR и GPT заключаются в следующем:
|
||||
% 1. Требования к прошивке.
|
||||
% Прошивка — это программное обеспечение, обеспечивающее низкоуровневое управление аппаратным устройством и встроенное в само устройство.
|
||||
% Базовая система ввода-вывода (BIOS) и унифицированный расширенный интерфейс встроенного ПО (UEFI) — это две встроенные программы, которые сегодня широко распространены в компьютерах.
|
||||
% BIOS является старым из двух и в настоящее время имеет устаревший статус.
|
||||
% Для работы MBR требуется устаревшая прошивка BIOS, в то время как GPT, как часть спецификации UEFI, требует загрузки прошивки EFI.
|
||||
% Теоретически возможна загрузка в некоторые ОС с дисками с разделами MBR в системах с прошивкой EFI. Однако Windows требует, чтобы диски были разбиты на разделы GPT для использования UEFI.
|
||||
% Если ваш диск разбит на разделы MBR, Windows предоставляет инструмент «diskpart» для преобразования его в GPT без потери данных. Точно так же теоретически также возможно загрузить некоторые ОС на компьютере с устаревшей версией BIOS и дисками с разделами GPT.
|
||||
% Однако Windows этого не позволяет.
|
||||
% 2. Поддержка Windows
|
||||
% Windows 7 и более ранние версии Windows, работающие на 32-разрядных компьютерах, совместимы только с дисками с разделами MBR.
|
||||
% Windows 8 и более поздние версии могут использовать диски с разделами GPT и MBR.
|
||||
% Это связано с тем, что Windows 7 и более ранние версии не поддерживают UEFI на 32-разрядных платформах и, следовательно, не разрешают загрузку с разделов GPT.
|
||||
% 64-разрядные версии более ранних версий Windows могут читать и записывать с дисков с разделами GPT, но не могут загружаться с них.
|
||||
% 3. Максимальная ёмкость раздела
|
||||
% Максимальный размер диска, который может адресовать раздел MBR, ограничен 2 ТБ.
|
||||
% Это связано с тем, что MBR хранит адреса и размеры блоков в таблице разделов с использованием 32-битных данных.
|
||||
% Размер одного сектора составляет 512 байт.
|
||||
% Следовательно, теоретический максимальный размер, который он может хранить, составляет (2^32-1) x 512 байт = 2 ТиБ (2,19 ТБ).
|
||||
% С другой стороны, таблица разделов GPT может использовать 64-битные.
|
||||
% Таким образом, теоретический максимальный размер диска с разделами GPT составляет (2^64-1) x 512 байт = 8 ZiB (9,44 ZB).
|
||||
% Следует отметить, что файловые системы Windows в настоящее время ограничены 256 ТБ каждая.
|
||||
% 4. Количество разделов
|
||||
% Как обсуждалось ранее, MBR выделяет 16 байт данных для каждой записи раздела и может выделить всего 64 байта. Таким образом, MBR может адресовать не более 4 основных разделов или 3 основных раздела и 1 расширенный раздел.
|
||||
% Вы можете иметь неограниченное количество логических разделов в расширенном разделе.
|
||||
% Однако вы можете установить ОС только в основной раздел. Раздел GPT, с другой стороны, теоретически может иметь неограниченное количество первичных разделов.
|
||||
% Однако его реализация в Windows ограничена только 128 разделами. Каждый из этих 128 разделов может быть основным разделом.
|
||||
% Таким образом, вы можете иметь 128 копий ОС, установленных на диске с форматированием GPT.
|
||||
% 5. Скорость загрузки
|
||||
% Хотя ни разделы MBR, ни разделы GPT не предназначены для работы быстрее друг друга, между ними может быть некоторая разница в скорости загрузки.
|
||||
% Это связано с тем, что MBR использует устаревший BIOS, а GPT использует UEFI. Устаревший BIOS проходит процесс Power On Self Test (POST), который проверяет, готово ли всё оборудование к нормальной работе. После этого он ищет MBR для запуска загрузчика, который вызывает ядро ОС, которое затем запускает Windows.
|
||||
% Напротив, UEFI вызывает загрузчик EFI при запуске, который вызывает ядро, которое затем запускает Windows. Быстрый запуск можно включить в режиме UEFI, который обходит POST для некритичных аппаратных компонентов.
|
||||
% 6. Безопасность данных
|
||||
% MBR — это простая схема таблицы разделов, которая объединяет загрузочные данные и разделы. Таким образом, разделы MBR имеют более высокую вероятность потери данных в случае повреждения раздела.
|
||||
% GPT разделяет таблицу разделов и блоки данных, что обеспечивает более надежную конфигурацию. Кроме того, схема GPT предоставляет дополнительный GPT, который можно использовать в качестве резервной копии в случае повреждения основного GPT. А также, GPT имеет функцию безопасной загрузки, которая предотвращает захват вашего процесса загрузки вредоносными программами. Он также включает проверку циклическим избыточным кодом (CRC) для проверки на предмет повреждения данных.
|
||||
% Хотя GPT был введен для устранения ограничений MBR и в конечном итоге заменил его, они имеют некоторые общие функции:
|
||||
|
||||
% Загрузчик хоста может вызвать ядро ОС, чтобы начать процесс запуска ОС;
|
||||
% Таблица разделов хоста, в которой хранится информация о размере и расположении разделов;
|
||||
% Теоретически может быть неограниченное количество разделов. В MBR это происходит из-за возможности назначить четвертый раздел в качестве расширенного раздела, который может содержать неограниченное количество логических разделов. GPT по своей конструкции может содержать неограниченное количество разделов (хотя в Windows это ограничение составляет 124);
|
||||
% Оба используют логическую адресацию блоков для указания блоков данных;
|
||||
% MBR хранит основной загрузочный код, а GPT хранит защитную MBR в секторе 1 диска.
|
||||
|
||||
% Итак, теперь мы знаем, что такое MBR и GPT. Настало время взглянуть на особенности организации файловой системы в ОС Linux, затем в Windows. Начнём.
|
||||
% ОС Линукс предоставляет возможность устанавливать в каждый отдельный блок свою файловую систему, которая и будет обеспечивать порядок поступающих и хранящихся данных, поможет с их организацией. Каждая ФС работает на наборе правил. Исходя из этого и определяется в каком месте и каким образом будет выполняться хранение информации. Эти правила лежат в основе иерархии системы, то есть всего корневого каталога. Выше предусмотрен технический блок, отвечающий за организацию хранения данных на определенном типе носителя. Но он уже будет подчиняться правилам файловой системы.
|
||||
% От того, насколько правильно человек выберет тип файловой системы для каждого раздела зависит ряд параметров:
|
||||
|
||||
% скорость обработки данных;
|
||||
% сбережение файлов;
|
||||
% оперативность записи;
|
||||
% допустимый размер блока;
|
||||
% возможность хранения информации в оперативной памяти;
|
||||
% способы корректировки пользователями ядра и пр.
|
||||
|
||||
% Обмен информацией может осуществляться двумя способами:
|
||||
|
||||
% Через VFS – виртуальную файловую систему. Здесь обеспечивается слаженное взаимодействие ядра ОС и аппаратного обеспечения, которое используется системой. Останавливая выбор на виртуальной ФС, пользователь сможет работать, не вникая в нюансы любого из видов файловой системы.
|
||||
% Через драйверы ФС. Это узлы, обеспечивающие стабильную взаимосвязь между аппаратным и программным обеспечением ОС.
|
||||
|
||||
% Подводя промежуточный итог, выделим, что файловая система всякой ОС, включая Linux – это некая архитектура хранения информации, размещенной на жестком диске и на оперативной памяти. С ее помощью пользователь получает доступ к структуре ядра системы. Он же отвечает за размещение файлов во всех разделах, поддерживая актуальную для него структуру, формирует правила для ее генерации, управляет блоками, исходя из особенностей определенного типа файловой системы.
|
||||
% Но все же файловая система Linux имеет и свои отличительные особенности. Так, здесь все пространство раздела разбито на узлы разного размера, исходя из кратности сектора: 1024, 2048, 4096, 8120 байт. Эти параметры известны пользователю уже изначально, то есть на момент установки операционной системы. Ограничения здесь идут по размеру файловой системы и требований, установленных в каждой директории.
|
||||
% Все файловые системы Linux, которые применяются сегодня можно разделить на 2 отдельные категории:
|
||||
|
||||
|
||||
% Журналируемые. Сохраняет историю манипуляций пользователя и позволяет ее посмотреть, выполнить диагностику системы в отдельном специальном файле. Отличается повышенной стойкостью к сбоям в функционировании, сохранностью целостности данных.
|
||||
|
||||
|
||||
% Не журналируемые. Здесь не предусмотрено сбережение логов, нет гарантий сохранности информации. Но зато в работе такие ФМ более быстрые.
|
||||
|
||||
|
||||
% При установке на ПК данной операционной системы, пользователь сможет остановить выбор на следующих разновидностях Linux:
|
||||
|
||||
|
||||
% Ext (extended) FS. Это расширенная файловая система, одна из первых. Была запущена в работу еще в 1992 году. В основе ее функциональности лежала ФС UNIX. Основная задача состояла в выходе за рамки конфигурации классической файловой системы MINIX, исключить ее ограничения и повысить эффективность администрирования. Сегодня она применяется крайне редко.
|
||||
|
||||
|
||||
% Ext2. Вторая, более расширенная вервия ФС, паявшаяся на рынке в 1993 году. По своей структуре продукт аналогичный Ext. Изменения коснулись интерфейса, конфигурации. Увеличился объем памяти, производительность. Максимально допустимый объем файлов для хранения (указывается в настройках) – 2 ТБ. Ввиду невысокой перспективности применяется на практике редко.
|
||||
|
||||
|
||||
% Ext3. Третье поколение Extended FS, введенное в использование в 2001 году. Уже относится к журналируемой. Позволяет хранить логи – изменения, обновления файлов данных записываются в отдельный журнал еще до того, как эти действия будут завершены. После перезагрузки ПК, такая ФС позволит восстановить файлы благодаря внедрению в систему специального алгоритма.
|
||||
|
||||
|
||||
% Ext4. Четвертое поколение Extended FS, запущенное в 2006 году. Здесь максимально убраны всевозможные ограничения, присутствующие в предыдущих версиях. Сегодня именно она по умолчанию входит в состав большей части дистрибутивов Линукс. Передовой ее нельзя назвать, но стабильность и надежность работы здесь в приоритете. В Unix системах применяется повсеместно.
|
||||
|
||||
|
||||
% Все эти типы имеют различные наборы команд, что и определяет их узкопрофильный функционал. Узнать вид NFS, использующейся на вашем компьютере можно через команду file -s.
|
||||
% Наряду с основными, предусмотрены и альтернативные типы File System:
|
||||
|
||||
|
||||
% JFS. Журналируемая система, первый аналог продуктам из основной группы, разработанная специалистами IBM под AIX UNIX. Отличается постоянством и незначительными требованиями к работе. Может использоваться на многопроцессорных ПК. Но в ее журнале сохраняются ссылки лишь на метаданные. Поэтому если произойдет сбой, автоматически подтянутся устаревшие версии данных.
|
||||
|
||||
|
||||
% ReiserFS. Создана Гансом Райзером исключительно под Линукс и названа в его честь. По своей структуре – это продукт, похожий на Ext3, но с более расширенными возможностями. Пользователи могут соединять небольшие файлы в более масштабные блоки, исключая фрагментацию, повышая эффективность функционирования. Но в случае непредвиденного отключения электроэнергии есть вероятность потерять данные, которые будут группироваться в этот момент.
|
||||
|
||||
|
||||
% XFS. Еще один представитель группы журналируемых файловых систем. Отличительная особенность: в логи программа будет записывать только изменения в метаданных. Из преимуществ выделяют быстроту работы с объемной информацией, способность выделять место для хранения в отложенном режиме. Позволяет увеличивать размеры разделов, а вот уменьшать, удалять часть – нельзя. Здесь также есть риск потери данных при отключении электроэнергии.
|
||||
|
||||
|
||||
% Btrfs. Отличается повышенной стойкостью к отказам и высокой производительностью. Удобная в работе, позволяет легко восстанавливать информацию, делать скриншоты. Размеры разделов можно менять в рабочем процессе. По умолчанию входит в OpenSUSE и SUSE Linux. Но обратная совместимость в ней нарушена, что усложняет поддержку.
|
||||
|
||||
|
||||
% F2FS. Разработка Самсунг. Уже входит в ядро Линукс. Предназначена для взаимодействия с хранилищем данных флеш-памяти. Имеет особую структуру: носитель разбивается на отдельные части, которые в свою очередь дополнительно еще делятся.
|
||||
|
||||
|
||||
% OpenZFS. Это ответ на вопрос какую файловую систему выбрать для Ubuntu – она автоматически включена в поддержку ОС уже более 6 лет назад. Отличается высоким уровнем защиты от повреждения информации, автоматическим восстановлением, поддержкой больших объемов данных.
|
||||
|
||||
|
||||
% В ядре требуются далеко не все ФС, также и виртуальные, - есть варианты, которые можно осуществить непосредственно в пользовательской среде. Сюда относят сетевые файловые системы и системы для шифрования:
|
||||
|
||||
|
||||
% EncFS. Шифрует данные и пересохраняет их в этом формате в указанную пользователем директорию. Надо примонтировать ФС чтобы обеспечить доступ в расшифрованной информации.
|
||||
|
||||
|
||||
% Aufs. С ее помощью отдельные File Systems можно группировать в один раздел.
|
||||
|
||||
|
||||
% NFS. Позволит через сеть примонтировать ФС удаленного устройства.
|
||||
|
||||
|
||||
% Также, есть и специальные файловые системы:
|
||||
|
||||
|
||||
% Tmpfs. Предусмотрена возможность размещения пользовательских файлов непосредственно в оперативной памяти ПК. Предполагает создание блочного узла определенного размера с последующим подключением к папке. При необходимости данные можно будет удалять.
|
||||
|
||||
|
||||
% Procfs. По умолчанию размещена в папке proc. Буде содержать полный набор данных относительно процессов, запущенных в системе и непосредственно в ядре в режиме реального времени.
|
||||
|
||||
|
||||
% Sysfs. Такая ФС позволит пользователю задавать и отменять параметры ядра во время выполнения задачи.
|
||||
|
||||
|
||||
% Сколько же много всяких возможных файловых систем, теперь бы рассмотреть какие типы файлов вообще существуют в Linux:
|
||||
|
||||
|
||||
% Regular File, они же обычные. Предназначены для хранения двоичной и символьной информации. Это жесткая ссылка, ведущая на фактическую информацию, размещенную в каталоге. Если этой ссылке присвоить уникальное имя, получаем Named Pipe, то есть именованный канал.
|
||||
|
||||
|
||||
% Device File – файлы для устройств, туннелей. Речь идет о физических устройствах, представленными в Линукс файлами. Могут классифицировать специальные символы и блоки. Обеспечивают мгновенный доступ к дисководам, принтерам, модемам, воспринимая их как файл данных.
|
||||
|
||||
|
||||
% Soft Link, а именно мягкая (символьная) ссылка. Отвечает за мгновенный доступ к файлам, размещенным на любых носителях информации. В процессе копирования, перемещения и прочего действия пользователя. Предполагающего указание ссылки, будет выполняться операция над документом, на который ссылаются.
|
||||
|
||||
|
||||
% Каталоги. Обеспечивают быстрый и удобный доступ к каталогам. Представляет собой файл с директориями и указателями на них. Это некого рода картотека: в папках размещаются файлы, а в директориях – дополнительные каталоги.
|
||||
|
||||
|
||||
% Блочные и символьные устройства. Выделяют интерфейс, необходимый для взаимодействия приложений с аппаратной составляющей.
|
||||
|
||||
|
||||
% Каналы и сокеты. Отвечают за взаимодействие внутренних процессов в операционной системе.
|
||||
|
||||
|
||||
|
||||
% Windows
|
||||
% Далее приступим к рассмотрению всей линейке файловых систем для Windows, чтобы понять, какую роль они играют в работе системы и как они развивались в процессе становления Windows плоть до Windows 8.
|
||||
% Преимущества NTFS касаются практически всего: производительности, надежности и эффективности работы с данными (файлами) на диске. Так, одной из основных целей создания NTFS было обеспечение скоростного выполнения операций над файлами (копирование, чтение, удаление, запись), а также предоставление дополнительных возможностей: сжатие данных, восстановление поврежденных файлов системы на больших дисках и т.д.
|
||||
% Другой основной целью создания NTFS была реализация повышенных требований безопасности, так как файловые системы FAT, FAT32 в этом отношении вообще никуда не годились. Именно в NTFS можно разрешить или запретить доступ к какому-либо файлу или папке (разграничить права доступа).
|
||||
% Сначала рассмотрим сравнительные характеристики файловых систем, а потом остановимся на каждой из них поподробнее. Сравнение, для большей наглядности, приведены в табличной форме.
|
||||
% Файловая система FAT для современных жестких дисков просто не подходит (ввиду ее ограниченных возможностей). Что касается FAT32, то ее еще можно использовать, но уже с натяжкой.
|
||||
% Если купить жесткий диск на 1000 ГБ, то вам придется разбивать его как минимум на несколько разделов. А если вы собираетесь заниматься видеомонтажом, то вам будет очень мешать ограничение в 4 Гб как максимально возможный размер файла.
|
||||
% Всех перечисленных недостатков лишена файловая система NTFS. Так что, даже не вдаваясь в детали и специальные возможности файловой системы NTFS, можно сделать выбор в ее пользу.
|
||||
|
||||
% .tg {border-collapse:collapse;border-spacing:0;}
|
||||
% .tg td{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
|
||||
% overflow:hidden;padding:10px 5px;word-break:normal;}
|
||||
% .tg th{border-color:black;border-style:solid;border-width:1px;font-family:Arial, sans-serif;font-size:14px;
|
||||
% font-weight:normal;overflow:hidden;padding:10px 5px;word-break:normal;}
|
||||
% .tg .tg-0pky{border-color:inherit;text-align:center;vertical-align:center}
|
||||
|
||||
|
||||
|
||||
|
||||
% Файловая система
|
||||
% Параметры
|
||||
|
||||
|
||||
% Размеры тома
|
||||
% Максимальный размер файла
|
||||
|
||||
|
||||
|
||||
|
||||
% FAT
|
||||
% От 1.44 МБ до 4 ГБ
|
||||
% 2ГБ
|
||||
|
||||
|
||||
% FAT32
|
||||
% Теоретически возможен размер тома от 512 МБ до 2 Тбайт. Сжатие не поддерживается на уровне файловой системы
|
||||
% 4ГБ
|
||||
|
||||
|
||||
% NTFS
|
||||
% Минимальный рекомендуемый размер составляет 1,44 МБ, а максимальный - 2 Тбайт. Поддержка сжатия на уровне файловой системы для файлов, каталогов и томов.
|
||||
% Максимальный размер ограничен лишь размером тома (Теоретически - 264 байт минус 1 килобайт. Практически - 244 байт минус 64 килобайта)
|
||||
|
||||
|
||||
|
||||
% Вообще использование FAT32 может быть оправдано лишь в тех случаях, когда у вас на компьютере установлено несколько операционных систем, а какая-либо из них не поддерживает NTFS. Но на сегодняшний день таких практически нет. Разве что вы захотите установить у себя антиквариат типа Windows 98.
|
||||
|
||||
% Файловая система FAT
|
||||
|
||||
% Файловая система FAT (обычно под ней понимается FAT 16) была разработана достаточно давно и предназначалась для работы с небольшими дисковыми и файловыми объемами, простой структурой каталогов. Аббревиатура FAT расшифровывается как File Allocation Table (с англ. таблица размещения файлов). Эта таблица размещается в начале тома, причем хранятся две ее копии (в целях обеспечения большей устойчивости).
|
||||
% Данная таблица используется операционной системой для поиска файла и определения его физического расположения на жестком диске. В случае повреждения таблицы (и ее копии) чтение файлов операционной системой становится невозможно. Она просто не может определить, где какой файл, где он начинается и где заканчивается. В таких случаях говорят, что файловая система «упала».
|
||||
% Файловая система FAT изначально разрабатывалась компанией Microsoft для дискет. Только потом они стали ее применять для жестких дисков. Сначала это была FAT12 (для дискет и жестких дисков до 16 МБ), а потом она переросла в FAT16, которая была введена в эксплуатацию с операционной системой MS-DOS 3.0.
|
||||
% Далее она поддерживается в Windows 3.x, Windows 95, Windows 98, Windows NT/2000 и т.д.
|
||||
|
||||
% Файловая система FAT32
|
||||
|
||||
% Начиная с Windows 95 OSR2, компания Microsoft начинает активно использовать в своих операционных системах FAT32 - тридцатидвухразрядную версию FAT. Что поделать, технический прогресс не стоит на месте и возможностей FAT 16 стало явно недостаточно.
|
||||
% По сравнению с ней FAT32 стала обеспечивать более оптимальный доступ к дискам, более высокую скорость выполнения операций ввода/вывода, а также поддержку больших файловых объемов (объем диска до 2 Тбайт).
|
||||
% В FAT32 реализовано более эффективное расходование дискового пространства (путем использования более мелких кластеров). Выгода по сравнению с FAT16 составляет порядка 10.15%. То есть при использовании FAT32 на один и тот же диск может быть записано информации на 10. 15% больше, чем при использовании FAT16.
|
||||
% Кроме того, необходимо отметить, что FAT32 обеспечивает более высокую надежность работы и более высокую скорость запуска программ.
|
||||
% Обусловлено это двумя существенными нововведениями:
|
||||
|
||||
% Возможностью перемещения корневого каталога и резервной копии FAT (если основная копия получила повреждения);
|
||||
% Возможностью хранения резервной копии системных данных.
|
||||
|
||||
|
||||
% Файловая система NTFS
|
||||
|
||||
% Ни одна из версий FAT не обеспечивает хоть сколько-нибудь приемлемого уровня безопасности. Это, а также необходимость в добавочных файловых механизмах (сжатия, шифрования) привело к необходимости создания принципиально новой файловой системы. И ею стала файловая система NT (NTFS)
|
||||
% NTFS — от англ. New Technology File System, файловая система новой технологии. Как уже упоминалось, основным ее достоинством является защищенность: для файлов и папок NTFS могут быть назначены права доступа (на чтение, на запись и т.д.). Благодаря этому существенно повысилась безопасность данных и устойчивость работы системы.
|
||||
% Назначение прав доступа позволяет запретить/разрешить каким-либо пользователям и программам проделывать какие-либо операции над файлами. Например, не обладая достаточными правами, посторонний пользователь не сможет изменить какой-либо файл. Или не обладая достаточными правами, вирус не сможет испортить файл.
|
||||
% Кроме того, NTFS, как было сказано выше, обеспечивает лучшую производительность и возможность работы с большими объемами данных.
|
||||
% Начиная с Windows 2000, используется версия NTFS 5.0, которая, помимо стандартных, позволяет реализовывать следующие возможности:
|
||||
% Шифрование данных - эта возможность реализуется специальной надстройкой NTFS, которая называется Encrypting File System(EFS) - шифрующая файловая система. Благодаря этому механизму шифрованные данные могут быть прочитаны только на компьютере, на котором произошла шифровка.
|
||||
% Дисковые квоты - стало возможно назначать пользователям определенный (ограниченный) размер на диске, который они могут использовать.
|
||||
% Хранение разреженных файлов. Встречаются файлы, в которых содержится большое количество последовательных пустых байтов. Файловая система NTFS позволяет оптимизировать их хранение.
|
||||
% Использование журнала изменений - позволяет регистрировать все операции доступа к файлам и томам.
|
||||
% И еще одно нововведение NTFS - точки монтирования. С помощью точек монтирования можно определить различные не связанные между собой папки и даже диски в системе, как один диск или папка. Это имеет большую важность для сбора в одном месте разнородной информации, находящейся в системе.
|
||||
% Напоследок необходимо иметь в виду, что если для файла под NTFS были установлены определенные права доступа, а потом вы его скопировали на раздел FAT, то все его права доступа и другие уникальные атрибуты, присущие NTFS, будут утеряны. Так что будьте бдительны.
|
||||
|
||||
% Устройство NTFS. Главная таблица файлов MFT
|
||||
|
||||
% Как и любая другая файловая система, NTFS делит все полезное место на кластеры - минимальные блоки данных, на которые разбиваются файлы. NTFS поддерживает почти любые размеры кластеров - от 512 байт до 64 Кбайт. Однако общепринятым стандартом считается кластер размером 4 Кбайт. Именно он используется по умолчанию. Принцип существования кластеров можно проиллюстрировать следующим примером.
|
||||
% Если у вас размер кластера составляет 4 Кбайт (что скорее всего), а нужно сохранить файл, размером 5 Кбайт, то реально под него будет выделено 8 Кбайт, так как в один кластер он не помещается, а под файл дисковое пространство выделяется только кластерами.
|
||||
% Для каждого NTFS-диска имеется специальный файл - MFT (Master Allocation Table - главная таблица файлов). В этом файле содержится централизованный каталог всех имеющихся на диске файлов. При создании файла NTFS создает и заполняет в MFT соответствующую запись, в которой содержится информация об атрибутах файла, содержимом файла, имя файла и т.п.
|
||||
% Помимо MFT, имеется еще 15 специальных файлов (вместе с MFT - 16), которые недоступны операционной системе и называются метафайлами. Имена всех метафайлов начинаются с символа $, но стандартными средствами операционной системы просмотреть их и вообще увидеть не представляется возможным. Далее для примера представлены основные метафайлы:
|
||||
|
||||
% SMFT - сам MFT.
|
||||
% $MFTmirr - копия первых 16 записей MFT, размещенная посередине диска (зеркало).
|
||||
% $LogFile - файл поддержки журналирования.
|
||||
% $Volume - служебная информация: метка тома, версия файловой системы, и т.д.
|
||||
% $AttrDef - список стандартных атрибутов файлов на томе.
|
||||
% $ - корневой каталог.
|
||||
% $Bitmap - карта свободного места тома.
|
||||
% $Boot - загрузочный сектор (если раздел загрузочный).
|
||||
% $Quota - файл, в котором записаны права пользователей на использование дискового пространства.
|
||||
% $Upcase - файл-таблица соответствия заглавных и прописных букв в именах файлов на текущем томе.
|
||||
% Нужен в основном потому, что в NTFS имена файлов записываются в кодировке Unicode, которую составляют 65 тысяч различных символов, искать большие и малые эквиваленты которых очень нетривиально.
|
||||
|
||||
% Что касается принципа организации данных на диске NTFS, то он условно делится на две части. Первые 12% диска отводятся под так называемую MFT-зону - пространство, в которое растет метафайл MFT.
|
||||
% Запись каких-либо пользовательских данных в эту область невозможна. MFT-зона всегда держится пустой. Это делается для того, чтобы самый главный служебный файл (MFT) не фрагментировался при своем росте. Остальные 88% диска представляют собой обычное пространство для хранения файлов.
|
||||
% Однако при нехватке дискового пространства MFT-зона может сама уменьшаться (если это возможно), так что никакого дискомфорта вы замечать не будете. При этом новые данные уже будут записываться в бывшую MFT-зону.
|
||||
% В случае последующего высвобождения дискового пространства MFT-зона снова будет увеличиваться, однако в дефрагментированном виде (то есть не единым блоком, а несколькими частями на диске). В этом нет ничего страшного, просто считается, что система более надежна, когда MFT-файл не дефрагментирован.
|
||||
% Кроме того, при не дефрагментированном MFT-файле вся файловая система работает быстрее. Соответственно чем более дефрагментированным является MFT-файл, тем медленней работает файловая система.
|
||||
% Что касается размера MFT-файла, то он примерно вычисляется, исходя из 1 МБ на 1000 файлов.
|
||||
|
Binary file not shown.
|
@ -7,7 +7,7 @@
|
|||
viewBox="0 0 210 297"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||
inkscape:version="1.2.1 (9c6d41e4, 2022-07-14)"
|
||||
sodipodi:docname="jc-01-jvm-struct.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
|
@ -26,14 +26,14 @@
|
|||
showgrid="true"
|
||||
showborder="false"
|
||||
borderlayer="false"
|
||||
inkscape:zoom="1.1929181"
|
||||
inkscape:cx="138.31629"
|
||||
inkscape:cy="520.15308"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1387"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="22"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:zoom="8.6634554"
|
||||
inkscape:cx="594.39332"
|
||||
inkscape:cy="469.78945"
|
||||
inkscape:window-width="1511"
|
||||
inkscape:window-height="832"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="25"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:pageshadow="2">
|
||||
<inkscape:grid
|
||||
|
@ -144,15 +144,15 @@
|
|||
y="96.395851">class loader</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.93888889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="65.326637"
|
||||
y="124.12859"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1"
|
||||
x="66"
|
||||
y="124"
|
||||
id="text2304"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan2302"
|
||||
style="stroke-width:0.265;-inkscape-font-specification:'IBM Plex Sans';font-family:'IBM Plex Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:4.93888889px"
|
||||
x="65.326637"
|
||||
y="124.12859">method area</tspan></text>
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';stroke-width:0.265"
|
||||
x="66"
|
||||
y="124">method area</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1"
|
||||
|
@ -177,15 +177,15 @@
|
|||
y="123.80221">stack</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.93888889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="150.76968"
|
||||
y="123.73399"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1"
|
||||
x="152"
|
||||
y="124"
|
||||
id="text3768"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3766"
|
||||
style="stroke-width:0.265;-inkscape-font-specification:'IBM Plex Sans';font-family:'IBM Plex Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:4.93888889px"
|
||||
x="150.76968"
|
||||
y="123.73399">java threads</tspan></text>
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';stroke-width:0.265"
|
||||
x="152"
|
||||
y="124">java threads</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.93888889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
|
@ -200,13 +200,13 @@
|
|||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1"
|
||||
x="64.981133"
|
||||
y="133.9402"
|
||||
x="66"
|
||||
y="134"
|
||||
id="text5230"><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';stroke-width:0.265"
|
||||
x="64.981133"
|
||||
y="133.9402"
|
||||
x="66"
|
||||
y="134"
|
||||
id="tspan5232">native internal threads</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
|
@ -232,15 +232,15 @@
|
|||
y="163.8349">JIT compiler</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.93888889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
x="90.339279"
|
||||
y="163.70062"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1"
|
||||
x="91"
|
||||
y="164"
|
||||
id="text7422"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan7420"
|
||||
style="stroke-width:0.265;-inkscape-font-specification:'IBM Plex Sans';font-family:'IBM Plex Sans';font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal;font-size:4.93888889px"
|
||||
x="90.339279"
|
||||
y="163.70062">garbage collector</tspan></text>
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';stroke-width:0.265"
|
||||
x="91"
|
||||
y="164">garbage collector</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.93888889px;font-family:'IBM Plex Sans';-inkscape-font-specification:'IBM Plex Sans';fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264999;stroke-opacity:1;font-weight:normal;font-style:normal;font-stretch:normal;font-variant:normal"
|
||||
|
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
Binary file not shown.
|
@ -35,7 +35,9 @@ Season current = Season.SPRING; System.out.println(current);
|
|||
|
||||
Season[] seasons = Season.values(); for (Season s : seasons) { System.out.printf("s ", s); }
|
||||
|
||||
Именно в этом примере, я использую цикл foreach для прохода по массиву, потому что у перечислений нет индексов, а показать их нужно. Если коротко, данный цикл возьмёт последовательно каждый элемент перечисления, присвоит ему имя s точно также, как мы это делали в примере на две строки выше, и сделает эту переменную С доступной в теле цикла в рамках одной итерации, на следующей итерации будет взят следующий элемент, и так далее \\ \hline
|
||||
Именно в этом примере, я использую цикл foreach для прохода по массиву, для лаконичности записи. Чуть подробнее о его особенностях мы поговорим на одной из следующих лекций. Если коротко, данный цикл возьмёт последовательно каждый элемент перечисления, присвоит ему имя s точно также, как мы это делали в примере на две строки выше, и сделает эту переменную С доступной в теле цикла в рамках одной итерации, на следующей итерации будет взят следующий элемент, и так далее \\ \hline
|
||||
|
||||
% +++ Комментатор_Ильнар. Строго говоря не очень корректная фраза "потому что у перечислений нет индексов". seasons - это уже обычный массив и можно его обойти обычным способом с индексами, скорее мы просто тут показали foreach цикл.
|
||||
|
||||
лайвкод 04-порядковый номер & Также в перечисления встроен метод ordinal() возвращающий порядковый номер определенной константы (нумерация начинается с 0).
|
||||
|
||||
|
@ -64,6 +66,28 @@ enum Color {
|
|||
|
||||
for (Color c : Color.values()) { System.out.printf("s(s) ", c, c.getCode()); } \\ \hline
|
||||
|
||||
Вопросы для самопроверки &
|
||||
\begin{enumerate}
|
||||
\item Перечисления нужны, чтобы: 3
|
||||
\begin{enumerate}
|
||||
\item вести учёт созданных в программе объектов;
|
||||
\item вести учёт классов в программе;
|
||||
\item вести учёт схожих по смыслу явлений в программе;
|
||||
\end{enumerate}
|
||||
\item Перечисление - это: 2
|
||||
\begin{enumerate}
|
||||
\item массив
|
||||
\item класс
|
||||
\item объект
|
||||
\end{enumerate}
|
||||
\item каждый объект в перечислении - это: 3
|
||||
\begin{enumerate}
|
||||
\item статическое поле
|
||||
\item статический метод
|
||||
\item статический объект
|
||||
\end{enumerate}
|
||||
\end{enumerate} \\ \hline
|
||||
|
||||
отбивка Вложенные и внутренние (Nested) классы & Взглянем на чуть более, скажем так, комплексный момент - вложенные классы. На самом деле мы очень хорошо подготовились к этой теме и сейчас должно быть не так уж и сложно. \\ \hline
|
||||
|
||||
03-вложенные-классы & В Java есть возможность создавать классы внутри других классов и их разделяют на два вида: Non-static nested classes — нестатические вложенные классы. По-другому их еще называют inner classes — внутренние классы; Static nested classes — статические вложенные классы. Непосредственно внутренние классы подразделяются ещё на два подвида. Помимо того, что внутренний класс может быть просто внутренним классом, он еще бывает: локальным классом (local class); анонимным классом (anonymous class). Анонимные классы мы пока что не будем рассматривать, отложим на несколько лекций. \\ \hline
|
||||
|
@ -103,7 +127,7 @@ Orange orange = new Orange(); orange.squeezeJuice();
|
|||
|
||||
И кстати вполне возможно что в будущем нам это пригодится такая связь объектов и классов называется композиции есть ещё ассоциация и агрегация а именно эта композиция. \\ \hline
|
||||
|
||||
На одном слайде 04-апельсин и 04-технологичный-апельсин & Если класс полезен только для одного другого класса, то вполне логично встроить его в этот класс и хранить их вместе. Использование внутренних классов увеличивает инкапсуляцию. Оба примера достаточно отличаются реализацией. Мой пример подразумевает "более сильную" инкапсуляцию, так как извне ко внутренниму классу доступ получить нельзя, поэтому создание объекта внутреннего класса происходит в конструкторе основного класса - в апельсине. Вы можете создавать объект сока где вам это нужно, не обязательно в конструкторе. С другой стороны, у примера из документации есть доступ извне ко внутреннему классу сок но всё равно только через основной класс апельсина. Как и собственно создать объект сока можно только через объект апельсина. \\ \hline
|
||||
На одном слайде 04-апельсин и 04-технологичный-апельсин & Если класс полезен только для одного другого класса, то вполне логично встроить его в этот класс и хранить их вместе. Использование внутренних классов увеличивает инкапсуляцию. Оба примера достаточно отличаются реализацией. Мой пример подразумевает "более сильную" инкапсуляцию, так как извне ко внутреннему классу доступ получить нельзя, поэтому создание объекта внутреннего класса происходит в конструкторе основного класса - в апельсине. Вы можете создавать объект сока где вам это нужно, не обязательно в конструкторе. С другой стороны, у примера из документации есть доступ извне ко внутреннему классу сок но всё равно только через основной класс апельсина. Как и собственно создать объект сока можно только через объект апельсина. \\ \hline
|
||||
|
||||
Особенности внутренних классов (последовательное появление элементов перечисления)
|
||||
\begin{itemize}
|
||||
|
@ -133,6 +157,28 @@ Orange orange = new Orange(); orange.squeezeJuice();
|
|||
|
||||
Также, внутренние классы имеют право наследовать другие классы, реализовывать интерфейсы и выступать в роли объектов наследования. Это уже более сложная тема, которую вы можете рассмотреть самостоятельно при желании. \\ \hline
|
||||
|
||||
Вопросы для самопроверки &
|
||||
\begin{enumerate}
|
||||
\item Внутренний класс: 1
|
||||
\begin{enumerate}
|
||||
\item реализует композицию;
|
||||
\item это служебный класс;
|
||||
\item не требует объекта внешнего класса;
|
||||
\end{enumerate}
|
||||
\item Инкапсуляция с использованием внутренних классов: 2
|
||||
\begin{enumerate}
|
||||
\item остаётся неизменной
|
||||
\item увеличивается
|
||||
\item уменьшается
|
||||
\end{enumerate}
|
||||
\item Статические поля внутренних классов: 2
|
||||
\begin{enumerate}
|
||||
\item могут существовать
|
||||
\item могут существовать только константными
|
||||
\item не могут существовать
|
||||
\end{enumerate}
|
||||
\end{enumerate} \\ \hline
|
||||
|
||||
03-вложенные-классы & Как мы помним классы это новый тип данных для нашей программы поэтому стоит ли упоминать что мы можем создавать классы а также их описывать например внутри методов это довольно редко используется но синтаксически язык позволяет это сделать. Первое, что нужно вспомнить перед изучением — их место в структуре вложенных классов. Исходя из схемы мы можем понять, что локальные классы — это подвид внутренних классов. Однако, у локальных классов есть ряд важных особенностей и отличий от внутренних классов. Главное заключается в их объявлении. \\ \hline
|
||||
|
||||
лайвкод 04-локальный-внутренний-класс & Локальный класс объявляется только в блоке кода. Чаще всего — внутри какого-то метода внешнего класса. Например, это может выглядеть так:
|
||||
|
@ -167,6 +213,8 @@ animal.performBehavior(true);
|
|||
|
||||
экземпляры локальных классов, как и экземпляры внутренних классов, имеют окружающий экземпляр, ссылка на который неявно передается всем конструкторам локальных классов. В результате определение внутренних классов можно описать так: подобно полям и методам экземпляра, каждый экземпляр внутреннего класса связан с экземпляром класса, внутри которого он определен (то есть каждый экземпляр внутреннего класса связан с экземпляром его окружающего класса). Вы не можете создать экземпляр внутреннего класса без привязки к экземпляру внешнего класса. То есть сперва должен быть создан экземпляр внешнего класса, а только затем уже вы можете создать экземпляр внутреннего класса. \\ \hline
|
||||
|
||||
% Комментатор_Ильнар. Тут у народа мозги закипят конечно, вся надежда на презу, которая это оживит и сделает более понятным) С вложенными и внутренними классами всегда есть проблема при изучении. Возможно надо будет побольше задачек им на семинары дать, которые покажут явно разницу между ними и смысл их использования
|
||||
|
||||
отбивка Статические вложенные классы & Мы поговорили о нестатических внутренних классах (non-static nested classes) или, проще, внутренних классах. Рассмотрим статические вложенные классы (static nested classes). Чем они отличаются от остальных? \\ \hline
|
||||
|
||||
лайвкод 04-статический-класс & При объявлении такого класса мы используем уже знакомое нам ключевое слово static. Возьмём класс нашего котика и заменим метод voice() на статический класс. Объясняется это, как вы уже слышали на прошлом уроке, что допустим, мы находимся дома и у нас отрыто окно, мы слышим разные звуки, которые доносятся из окна. Из этих звуков мы можем разобрать звук мурчания котика. И тут мы понимаем, что котика мы не видим, а при этом слышим.
|
||||
|
@ -201,6 +249,22 @@ Cat.Voice voice = new Cat.Voice(100 + i); voice.sayMur();
|
|||
|
||||
Не следует путать объекты с переменными. Если мы говорим о статических переменных — да, статическая переменная класса существует в единственном экземпляре. Но применительно ко вложенному классу static означает лишь то, что его объекты не содержат ссылок на объекты внешнего класса. В случае примера с котиком - мы слышим мурчание с разной громкостью и непонятно одного и того же котика или это другой. А самих объектов мы можем создать сколько угодно \\ \hline
|
||||
|
||||
Вопросы для самопроверки &
|
||||
\begin{enumerate}
|
||||
\item Вложенный класс: 1
|
||||
\begin{enumerate}
|
||||
\item реализует композицию;
|
||||
\item это локальный класс;
|
||||
\item всегда публичный;
|
||||
\end{enumerate}
|
||||
\item Статический вложенный классо бладает теми же свойствами, что: 2
|
||||
\begin{enumerate}
|
||||
\item константный метод
|
||||
\item внутренний класс
|
||||
\item статическое поле
|
||||
\end{enumerate}
|
||||
\end{enumerate} \\ \hline
|
||||
|
||||
отбивка Механизм исключительных ситуаций & Наконец-то, тема, которая почему-то вызывает у новичков отторжение недоумение и полное непонимание. \\ \hline
|
||||
|
||||
Исключение - это отступление от общего правила, несоответствие обычному порядку вещей & Думаю, тут надо начать с такой, немного философской части. Посмотрите пока что на этот вступительный слайд, он хорошая и в нём нет ничего сложного. Мы изучаем программирование, а что такое язык программирования? Это в первую очередь набор инструментов. Смотрите, например, есть строитель или вот лучше - художник. У художника есть набор всевозможных красок, кистей, холстов, карандашей, мольберт, ластик и куча-много-чего-ещё. Это всё его инструменты, с их помощью он делает свои важные художественные штуки. Тоже самое для программиста, у программиста есть язык программирования, который предоставляет ему инструменты: циклы, условия, классы, функции, методы, ООП, фрейморки, библиотеки... Исключения - это один из инструментов. Смотрите на исключения как на ещё один инструмент для работы программиста. Работает он достаточно специфично, и является достаточно высокоуровневым, исключения представляют из себя некую подсистему языка, которая является неотъемлемой частью любого мало-мальски серьёзного проекта.\\ \hline
|
||||
|
@ -280,11 +344,13 @@ throw e;
|
|||
|
||||
04-пробуй-лови & Давайте наше исключение ловить. Первое, и самое важное, что надо понять - это почему что-то упало, поэтому не пытайтесь что то ловить, пока не поймёте что именно произошло, от этого понимания будет зависеть способ ловли. Исключение ловится двухсекционным оператором try-catch, а именно, его первой секцией try. Это секция, в которой предполагается возникновение исключения, и предполагается, что мы можем его поймать. А в секции catch пишем имя класса исключения, которое мы ловим, и имя объекта, в который мы положим экземпляр нашего исключения. Секция catch ловит указанное исключение и всех его наследников. Это важно. Рекомендуется писать максимально узко направленные секции catch, потому что надо стараться досконально знать как работает ваша программа, и какие исключения она может выбрасывать. Ну и ещё потому что разные исключения могут по-разному обрабатываться, конечно-же. Секций catch может быть сколько угодно много. Как только мы обработали объект исключения, он уничтожается, дальше он не поднимается, и в следующие catch не попадает. Мы, конечно, можем его же насильно пульнуть выше, ключевым словом throw. \\ \hline
|
||||
|
||||
? & Так вот, когда какой-то наш метод выбрасывает исключение вы обязаны либо вынести объявление этого исключения в сигнатуру метода, что будет говорить тем, кто его вызывает о том, что в методе может возникнуть исключение, либо мы это исключение должны непосредственно в методе обработать, иначе у вас ничего не скомпилируется. Примером одного из таких исключений служит ИО это сокращение от инпут-аутпут и генерируется, когда, вы не поверите, возникает ошибка ввода-вывода. То есть при выполнении программы что-то пошло не так и она, программа не может произвести ввод-вывод в штатном режиме. На деле много чего может пойти не так, операционка заглючила, флешку выдернули, устройство телепортировалось в микроволновку, и всё, случился ИОЭксепшн, не смогла программа прочитать или написать что то в потоке ввода-вывода. Соответственно, уже от этого ИОЭ возникают какие-то другие, вроде FileNotFoundException, которое мы тоже обязаны обработать. Например, мы хотим чтобы наша программа что то там прочитала из файла, а файла на нужном месте не оказалось, и метод чтения генерирует исключение. \\ \hline
|
||||
04-варианты ? & Так вот, когда какой-то наш метод выбрасывает исключение у нас есть два основных пути: вы обязаны либо вынести объявление этого исключения в сигнатуру метода, что будет говорить тем, кто его вызывает о том, что в методе может возникнуть исключение, либо мы это исключение должны непосредственно в методе обработать, иначе у вас ничего не скомпилируется. Примером одного из таких исключений служит ИО это сокращение от инпут-аутпут и генерируется, когда, вы не поверите, возникает ошибка ввода-вывода. То есть при выполнении программы что-то пошло не так и она, программа не может произвести ввод-вывод в штатном режиме. На деле много чего может пойти не так, операционка заглючила, флешку выдернули, устройство телепортировалось в микроволновку, и всё, случился ИОЭксепшн, не смогла программа прочитать или написать что то в потоке ввода-вывода. Соответственно, уже от этого ИОЭ возникают какие-то другие, вроде FileNotFoundException, которое мы тоже обязаны обработать. Например, мы хотим чтобы наша программа что то там прочитала из файла, а файла на нужном месте не оказалось, и метод чтения генерирует исключение. \\ \hline
|
||||
|
||||
? & В случае, если мы выносим объявление исключения в сигнатуру, вызывающий метод должен обработать это исключение точно таким-же образом - либо в вызове, либо вынести в сигнатуру. Исключением из этого правила является класс RuntimeException. Все наследники от него, включая его самого, обрабатывать не обязательно. Туда входит деление на ноль, индексаутофбаунд экзепшн например. то есть те ошибки, которые компилятор пропускает, а возникают они уже в среде исполнения. Обычно уже по названию понятно что случилось, ну и помимо говорящих названий, там ещё содержится много инфы, хотя-бы даже номер строки, вызвавшей исключительную ситуацию. Общее правило работы с исключениями одно - если исключение штатное - его надо сразу обработать, если нет - надо дождаться, пока программа упадёт. Повторю, все исключения надо обрабатывать, примите это как расплату за использование языка Java.\\ \hline
|
||||
% +++ Комментатор_Ильнар. Мы вроде нигде про вынос в сигнатуру до этого не говорили, лучше до этих слов сказать что есть второй способ, а то читается так будто мы об этом уже сказали.
|
||||
|
||||
лайвкод ? & Вернёся к нашему учебному классу и Усложним ещё. в методеА вызовем методБ. а в методеБ выкинем то что нам обязательно надо обработать, например IOE. Вот тут то и начинается веселье. потому что IOE это не наследник рантайм эксепшена, и мы обязаны обрабатывать на этапе компиляции. Тут у нас есть две опции. многим уже известный try-catch, синтаксис его не совсем очевидный, так что тут надо просто сделать усилие и запомнить. пишем
|
||||
04-ответственность & В случае, если мы выносим объявление исключения в сигнатуру, вызывающий метод должен обработать это исключение точно таким-же образом - либо в вызове, либо вынести в сигнатуру. Исключением из этого правила является класс RuntimeException. Все наследники от него, включая его самого, обрабатывать не обязательно. Туда входит деление на ноль, индексаутофбаунд экзепшн например. то есть те ошибки, которые компилятор пропускает, а возникают они уже в среде исполнения. Обычно уже по названию понятно что случилось, ну и помимо говорящих названий, там ещё содержится много инфы, хотя-бы даже номер строки, вызвавшей исключительную ситуацию. Общее правило работы с исключениями одно - если исключение штатное - его надо сразу обработать, если нет - надо дождаться, пока программа упадёт. Повторю, все исключения надо обрабатывать, примите это как расплату за использование языка Java.\\ \hline
|
||||
|
||||
лайвкод 04-обработка-вариант-1 & Вернёся к нашему учебному классу и Усложним ещё. в методеА вызовем методБ. а в методеБ выкинем то что нам обязательно надо обработать, например IOE. Вот тут то и начинается веселье. потому что IOE это не наследник рантайм эксепшена, и мы обязаны обрабатывать на этапе компиляции. Тут у нас есть две опции. многим уже известный try-catch, синтаксис его не совсем очевидный, так что тут надо просто сделать усилие и запомнить. пишем
|
||||
|
||||
try { methodB() } catch (имя класса исключения которое хотим поймать и идентификатор экземпляра) { }.
|
||||
|
||||
|
@ -296,7 +362,7 @@ sout(e.getMessage()).
|
|||
|
||||
catch throw new runtimeexception(e); \\ \hline
|
||||
|
||||
лайвкод ? & Второй вариант обработки исключений - мы в сигнатуре метода пишем
|
||||
лайвкод 04-обработка-вариант-2 & Второй вариант обработки исключений - мы в сигнатуре метода пишем
|
||||
|
||||
throws IOE,
|
||||
|
||||
|
@ -310,7 +376,7 @@ throws IOE,
|
|||
|
||||
вовочка перед печкой «и так сойдёт» & Важный момент. Задачи бывают разные. Исключения - это инструмент, который нетривиально работает. Важно при написании кода понять, возникающая исключительная ситуация - штатная, или нештатная. В большинстве случаев - ситуации нештатные, поэтому надо уронить приложение и разбираться с тем, что произошло. Допустим для вашего приложения вот стопроцентно какой-то файл должен быть, без него дальше нет смысла продолжать. Что делать, если его нет? Явно не пытаться в него что то писать, правда? Самое плохое, что можно сделать - ничего не делать. Это самое страшное, когда программа повела себя как-то не так, а мы об этом даже не узнали. Допустим мы хотим прочитать файл, вывести в консоль, но мы глотаем исключение, выведя стектрейс куда-то-там какому-то разработчику и наши супер-важные действия не выполнились. Надо ронять. Как ронять? да throw new RuntimeException(e). Крайне редко случаются ситуации, когда исключение надо проглотить. \\ \hline
|
||||
|
||||
лайвкод ? & Давайте обрабатывать дальше. Все помнят, что потоки надо закрывать? Даже если не знали, теперь знаете. Вот, допустим, у нас в нашем модном потоке открылся файл, что то из него прочиталось, потом метод упал с исключением, а файл остался незакрытым, ресурсы заняты. Давайте даже допишем свой класс TestStream пусть его конструктор выбрасывает IOE, метод read выбрасывает IOE и будет метод close, везде будем логировать успешность в read пока оставляем исключение throw new IOE("reading error") в close "closed". Штатно возвращаем из read единичку и логируем, что всё прочитали в мейне. Как всё будет происходить штатно?
|
||||
лайвкод 04-файналли & Давайте обрабатывать дальше. Все помнят, что потоки надо закрывать? Даже если не знали, теперь знаете. Вот, допустим, у нас в нашем модном потоке открылся файл, что то из него прочиталось, потом метод упал с исключением, а файл остался незакрытым, ресурсы заняты. Давайте даже допишем свой класс TestStream пусть его конструктор выбрасывает IOE, метод read выбрасывает IOE и будет метод close, везде будем логировать успешность в read пока оставляем исключение throw new IOE("reading error") в close "closed". Штатно возвращаем из read единичку и логируем, что всё прочитали в мейне. Как всё будет происходить штатно?
|
||||
|
||||
TestStream stream = new TestStream();
|
||||
int a = stream.read()
|
||||
|
@ -318,9 +384,9 @@ throws IOE,
|
|||
|
||||
Далее представим что в методе read что то пошло не так, выбрасываем исключение, и что видим в консоли? создали поток, исключение, конец программы. Что же делать? а делать секцию finally. Секция finally будет выполнена в любом случае, будет исключение, не будет исключения, не важно. Но тут тоже большое спасибо разработчикам джавы, мы не видим наш поток, то есть его надо вынести наружу, а ещё он оказывается не инициализирован, значит надо написать что то типа TestStream stream = null. Теперь всё должно отработать. \\ \hline
|
||||
|
||||
лайвкод ? & Теперь немного неприятностей. Написали мы блок finally, вроде даже избавились от проблемы с закрытием потока. А как быть, если исключение возникло при создании этого потока? Тогда получается, у нас метод закрытия будет пытаться выполниться от ссылки на null. Нехорошо, знаете-ли, получается. Давайте всё сломаем, прям в конструкторе нашего потока выкинем IOE. Получили NPE в блоке finally. Очевидное решение - ставим в секции finally условие, и если поток не равен нулю, закрываем. Вроде клёво. Меняем тактику. Конструктор отрабатывает нормально. Метод чтения всё ещё генерирует исключение, но бац и в методе закрытия что-то пошло не так, и вылетело исключение. Ну вот так не повезло в жизни. Что делаем? Оборачиваем в try-catch. Вроде снова всё классно. Но и тут мы можем наткнуться на неприятность. Допустим, что нам надо в любом случае ронять приложение (в кэтч допишем throw new Runtime...). Тогда если у нас try поймал исключение, и выкинул его, потом finally всё равно выполнится, и второе исключение затрёт первое, мы его не увидим. Ну а поскольку первое для нас важнее, то второе - максимум что мы можем сделать - это залогировать исключение в консольку. Так дела обстояли в седьмой джаве. \\ \hline
|
||||
лайвкод 04-проблема & Теперь немного неприятностей. Написали мы блок finally, вроде даже избавились от проблемы с закрытием потока. А как быть, если исключение возникло при создании этого потока? Тогда получается, у нас метод закрытия будет пытаться выполниться от ссылки на null. Нехорошо, знаете-ли, получается. Давайте всё сломаем, прям в конструкторе нашего потока выкинем IOE. Получили NPE в блоке finally. Очевидное решение - ставим в секции finally условие, и если поток не равен нулю, закрываем. Вроде клёво. Меняем тактику. Конструктор отрабатывает нормально. Метод чтения всё ещё генерирует исключение, но бац и в методе закрытия что-то пошло не так, и вылетело исключение. Ну вот так не повезло в жизни. Что делаем? Оборачиваем в try-catch. Вроде снова всё классно. Но и тут мы можем наткнуться на неприятность. Допустим, что нам надо в любом случае ронять приложение (в кэтч допишем throw new Runtime...). Тогда если у нас try поймал исключение, и выкинул его, потом finally всё равно выполнится, и второе исключение затрёт первое, мы его не увидим. Ну а поскольку первое для нас важнее, то второе - максимум что мы можем сделать - это залогировать исключение в консольку. Так дела обстояли в седьмой джаве. \\ \hline
|
||||
|
||||
лайвкод ? & Что предлагает нам восьмая джава? try-с-ресурсами. Поток - это ресурс, абстрактное понятие. Как с этим работать? Сейчас будет небольшое забегание вперёд, пока что предлагаю просто запомнить магию. Выражаясь строго формально, мы должны реализовать интерфейс Closeable. А что это за интерфейс? (Ctrl+click) там содержится всего один метод close(), который умеет бросать IOE. Напишем везде пока что штатное поведение в нашем тестовом потоке. Залогируем. Далее синтаксис try-с-ресурсами. Все потоки начиная с восьмой джавы реализуют интерфейс Closeable. Стираем все ужасы, которые мы написали, пишем
|
||||
лайвкод 04-с-ресурсами & Что предлагает нам восьмая джава? try-с-ресурсами. Поток - это ресурс, абстрактное понятие. Как с этим работать? Сейчас будет небольшое забегание вперёд, пока что предлагаю просто запомнить магию. Выражаясь строго формально, мы должны реализовать интерфейс Closeable. А что это за интерфейс? (Ctrl+click) там содержится всего один метод close(), который умеет бросать IOE. Напишем везде пока что штатное поведение в нашем тестовом потоке. Залогируем. Далее синтаксис try-с-ресурсами. Все потоки начиная с восьмой джавы реализуют интерфейс Closeable. Стираем все ужасы, которые мы написали, пишем
|
||||
|
||||
try(TestStream stream = new TestStream())
|
||||
int a = stream.read();
|
||||
|
@ -329,7 +395,19 @@ throws IOE,
|
|||
|
||||
И всё, мы поток не закрываем. За это у нас ответит сама джава. Запустим и проверим. Никаких close() и finally, всё хорошо. Самое классное - если мы ломаем метод read() то трай с ресурсами всё равно наш поток корректно закроет. Иногда вы можете видеть вывод в консоль и стектрейс вразнобой, ничего страшного, просто исключения вылетают в поток error а вывод в консоль в стандартный вывод. Ну и у них иногда случаются асинхронности. А теперь вообще самый смак - ломаем метод закрытия, и джава очень правильно поступит, она выкатит наверх основное исключение, но и выведет "подавленное" исключение, вторичное в стектрейс. По-человечески, красиво, информативно, глаз радуется. Рекомендуется по возможности использовать вот такую конструкцию. Но научиться пользоваться надо и тем и тем, естественно. \\ \hline
|
||||
|
||||
& Последнее на сегодня про исключения это наследования и Полиморфизм исключения тема не очень большая и в целом не сложная потому что вы уже знаете что такое класса объекты как классы могут наследоваться и что такое Полиморфизм. Особенно застрять внимание на объектно-ориентированном программирования исключениях скорее всего не нужно потому что было неоднократно сказано что исключение это тоже классы и есть какие-то наследники исключений генерируются и выбрасываю объекты исключений единственное что важно упомянуть это то что под система исключений работает немного не тривиально впрочем это вы могли заметить и сами но мы можем создавать собственные исключения с собственными смыслами и сообщениями и точно также их выбрасывать вместо стандартных наследоваться мы можем от любых исключений единственное что важно это то что не рекомендуется наследоваться от классов throwable и error когда описываете исключение механика checked и unchecked исключений сохраняется при наследование поэтому создав наследник RuntimeException вы получаете не проверяемые на этапе написания кода исключение \\ \hline
|
||||
% Комментатор_Ильнар. Смотри, многие тут потеряются походу этой лекции про вложенные классы и исключения. Понятно что про ресурс надо объяснить почему без него костылить многие вещи сложно, но многие в этом могут запутаться. Добавь пожалуйста резюме на пальцах после таких блоков. Мол, если уже успели запутаться и потеряться, то вот вам простая инструкция - исключения либо прокидываем, либо обрабатываем. Прокидываем так, обрабатываем в try catch finaly. Есть для обработки прекрасная штука try с ресурсами. для чего все это нужно - можно переслушать то что я рассказал после того как поиграете со всем этим на семинаре. Переслушивать можно медленно и конспектируя несколько раз пока не дойдет, не стесняйтесь это делать. Такое же резюме можно по вложенным классам сделать, чтобы их немного успокоить
|
||||
|
||||
03-наследование & Последнее на сегодня про исключения это наследования и Полиморфизм исключения тема не очень большая и в целом не сложная потому что вы уже знаете что такое класса объекты как классы могут наследоваться и что такое Полиморфизм. Особенно застрять внимание на объектно-ориентированном программирования исключениях скорее всего не нужно потому что было неоднократно сказано что исключение это тоже классы и есть какие-то наследники исключений генерируются и выбрасываю объекты исключений единственное что важно упомянуть это то что под система исключений работает немного не тривиально впрочем это вы могли заметить и сами но мы можем создавать собственные исключения с собственными смыслами и сообщениями и точно также их выбрасывать вместо стандартных наследоваться мы можем от любых исключений единственное что важно это то что не рекомендуется наследоваться от классов throwable и error когда описываете исключение механика checked и unchecked исключений сохраняется при наследование поэтому создав наследник RuntimeException вы получаете не проверяемые на этапе написания кода исключение \\ \hline
|
||||
|
||||
На этом уроке & На этой лекции в дополнение к предыдущей, разобрали такие понятия как внутренние и вложенные классы, было непросто, но мы, кажется, справились; процессы создания, использования и расширения перечислений. Детально разобрали уже знакомое вам понятие исключений и их тесную связь с многопоточностью в джава. Всё время смотрели на исключения с точки зрения ООП, обработали немного исключений, а также раз и навсегда разделили понятия штатных и нештатных ситуаций. \\ \hline
|
||||
|
||||
ДЗ & В качестве домашнего задания
|
||||
\begin{enumerate}
|
||||
\item напишите два наследника класса Exception: ошибка преобразования строки и ошибка преобразования столбца
|
||||
\item разработайте исключения-наследники так, чтобы они информировали пользователя в формате ожидание/реальность
|
||||
\item для проверки напишите программу, преобразующую квадратный массив целых чисел 5х5 в сумму чисел в этом массиве, при этом, программа должна выбросить исключение, если строк или столбцов в исходном массиве окажется не 5.
|
||||
\end{enumerate}
|
||||
\\ \hline
|
||||
|
||||
Не стыдись учиться в зрелом возрасте: лучше научиться поздно, чем никогда. Эзоп & На этом вроде бы мы закончили рассмотрение основных аспектов ООП и исключений, дальше нас ждёт выход за пределы одной программы и ещё больше механизмов языка. Не теряйте внимания, будьте умничками. \\ \hline
|
||||
\end{longtable}
|
||||
|
|
|
@ -0,0 +1,514 @@
|
|||
\documentclass[../j-spec.tex]{subfiles}
|
||||
|
||||
\begin{document}
|
||||
\section{Специализация: тонкости работы}
|
||||
\begin{longtable}{|p{35mm}|p{135mm}|}
|
||||
\hline
|
||||
Экран & Слова \\ \hline
|
||||
\endhead
|
||||
|
||||
Титул & Здравствуйте, отвлечёмся от фундаментальных механик \\ \hline
|
||||
|
||||
Отбивка & и немного поговорим о практической составляющей программирования \\ \hline
|
||||
|
||||
На прошлом уроке & На прошлой лекции, разобрались с понятиями внутренних и вложенных классов; процессами создания, использования и расширения перечислений. Посмотрели на исключения с точки зрения ООП, обработали немного, разделили понятия штатных и нештатных ситуаций. Детально разобрали понятие исключений, их, скажем так, философию и тесную связь с многопоточностью в джава. \\ \hline
|
||||
|
||||
На этом уроке & В качестве ставшего традиционным общеобразовательного отступления поговорим о файловых системах и представлении данных в запоминающих устройствах; Посмотрим на популярные пакеты ввода-вывода java.io, java.nio, без особенного погружения в их недра, потому что там начинаются всякие сложности. Подробно разберём один из самых популярных ссылочных типов данных String и разные механики вокруг него, такие как стрингбилдер и стрингпул. Получается, На этом уроке мы начнём с файловой системы, как с ней взаимодействовать в целом, с файлами и каталогами. Узнаем как управлять содержимым файлов, затем перейдём к разбору класса String, а так же частично разберёмся с сериализаторами и десериализаторами в Java. \\ \hline
|
||||
|
||||
отбивка файловая система & Настало время разобраться в тонкастях работы языка в части общения программы со внешним миром, а делать это не разобравшись в тонкостях работы файловой системы не совсем эффективно. Далее последует материал, напрямую не относящийся к Java. А если оно напрямую не относится к языку, зачем мы вообще об этом говорим? знать о некоторых различиях нужно, чтобы не удивляться тому, что в одной ОС программа работает, а в другой вылетает с каким-нибудь грустным исключением. Поэтому, если будет слишком уж туго заходить не напрягайтесь, сегодня наша задача заложить такой фундамент, чтобы когда в работе у вас возникнет проблема, вы где-то в подсознании припомнили, что кто-то вам что-то такое рассказывал, пойдёте в поисковик и уже целенаправленно отыщете ответ на свой вопрос. \\ \hline
|
||||
|
||||
Что такое файловая система? ФС - один из ключевых компонентов всех операционных систем. В ее обязанности входит структуризация, чтение, хранение, запись файлов. Выделяют Windows-подход и Linux-подход & Перед тем, как приступить к изучению классов, которые работают с файловой системой, предлагаю разобраться вообще с понятием файловой системы, что она из себя представляет и как с ней взаимодействовать. файловая система (File System) - FS, ФС – один из ключевых компонентов всех операционных систем. В ее обязанности входит структуризация, чтение, хранение, запись файловой документации. Она напрямую влияет на физическое и логическое строение данных, особенности их формирования, управления, допустимый объем файла, количество символов в его названии и прочие. В качестве примеров и в какой-то степени антагонистов идеологий работы с файлами выступают две операционные системы Linux и Windows. \\ \hline
|
||||
|
||||
Linux на этапе установки предоставляет выбор из огромного числа ФС. Также возможно самостоятельно выбрать разделы жёсткого диска и подобрать ФС строго под свои нужды. & В ядре ОС Линукс предусмотрен огромный набор заблаговременно установленных файловых систем. Их задача – помогать пользователю в решении той или иной поставленной задачи. Для определенного раздела можно выбирать свою систему, ориентируясь на предстоящие потребности: обеспечение быстродействия, гарантированное восстановление информации, повышенная производительность. Речь идет как о стандартных, так и о специализированные либо же виртуальных файловых системах. В ОС Линукс еще на этапе установки пользователю предоставляется на выбор большое количество ФС, вмонтированных в ее ядро. Пользователь самостоятельно выбирает вариант, который будет соответствовать его запросам и проблемам, требующим решения в рабочем процессе. Обратите внимание: подобное актуально только для операционной системы Linux и Windows NT (с файловой системой сравнительно новой технологии NTFS). Для обычного Windows не предусмотрено возможности выбора вида файловых систем. Отличными здесь будут и строение каталога, и иерархия самих ФС. \\ \hline
|
||||
|
||||
Разделы GPT и MBR & ОС Линукс также предоставляет возможность разбивки жесткого диска вашего компьютера на отдельные разделы. Пользователи могут определить их границы по так называемым таблицам разделов – GPT, MBR. Основная загрузочная запись (MBR) и таблица разделов GUID (GPT) — это два стиля формата разделов, которые позволяют вашему компьютеру загружать операционную систему с жесткого диска, а также индексировать и упорядочивать данные. Для большинства людей предпочтительным стилем разделов должен быть GPT — более новый из двух. Однако не всегда всё бывает просто. Основная мысль, которую следует запомнить, что у старого загрузчика есть довольно много ограничений, которых лишён новый, поэтому по возможности следует его обновить.\\ \hline
|
||||
|
||||
Основная загрузочная запись (MBR) 05-МБР & Основная загрузочная запись (MBR) — это устаревшая форма разделения загрузочного сектора. Это первый сектор диска, который содержит информацию о том, как разбит диск. Он также содержит загрузчик, который сообщает вашей машине, как загрузить ОС. Main Boot Record состоит из трех частей: Основной загрузчик; Таблица разделов диска; Конечная подпись. \\ \hline
|
||||
|
||||
05- МБР-состав & Итак, основной загрузчик. MBR резервирует первые байты дискового пространства для основного загрузчика. Windows размещает здесь очень упрощенный загрузчик, в то время как другие ОС могут размещать более сложные многоступенчатые загрузчики.
|
||||
|
||||
Таблица разделов диска находится в 0м цилиндре, 0й головке и 1м секторе жесткого диска. Она хранит информацию о том, как разбит диск. MBR выделяет 16 байт данных для каждой записи раздела и может выделить всего 64 байта. Таким образом, Main Boot Record может адресовать не более 4 основных разделов или 3 основных раздела и 1 расширенный раздел. Расширенный раздел используется для создания нескольких логических разделов. Это полезно, когда пользователю нужно создать более четырёх. Однако, операционная система может быть установлена только в основных разделах, а не в логических.
|
||||
|
||||
Конечная подпись - это 2-байтовая подпись, которая отмечает конец MBR. Всегда устанавливается в шестнадцатеричное значение 0x55AA. Вообще, программисты любят всякие 16-теричные подписи вроде 0xDEADBEEF, 0xA11F0DAD, 0xDEADFA11, одну из таких подписей мы видели на самой первой лекции, помните 0xcafebabe? они называются hexspeak. \\ \hline
|
||||
|
||||
05-МБР-особенности & Main boot record обладает некоторыми особенностями, такими как: Возможность инициализировать загрузчик в устаревшем режиме BIOS; Может адресовать до 2 ТБ дискового пространства; Может иметь 4 основных раздела или 3 основных раздела и 1 дополнительный раздел; Может использоваться для загрузки Windows 7 и более ранних версий Windows.
|
||||
|
||||
Из плюсов можно отметить Совместимость со всеми версиями Windows, включая Windows 7 и более ранние версии; Требуется для обеспечения совместимости со старым 32-разрядным оборудованием; Использует 32-битные значения, поэтому имеет меньшие накладные расходы, чем GPT.
|
||||
К минусам можно отнести Максимальную емкость раздела всего в 2 ТБ; Ограничение 4мя основными разделами или 3 основными разделами и 1 расширенным разделом; Не устойчивость к повреждению; Отсутствие встроенного исправления ошибок для защиты данных, поскольку использует BIOS. \\ \hline
|
||||
|
||||
Вопросы для самопроверки & MBR - это:(1) main boot record; master BIOS recovery; minimizing binary risks \\ \hline
|
||||
|
||||
GUID Partition Table (GPT)
|
||||
|
||||
United Extensible Firmware Interface (UEFI) & Таблица разделов GUID (GPT) — это стиль формата раздела, который был представлен в рамках инициативы United Extensible Firmware Interface (UEFI). GPT был разработан для архитектурного решения некоторых ограничений MBR. Стиль GPT новее, гибче и надежнее, чем MBR. GPT использует логическую адресацию блоков для указания блоков данных. Первый блок помечен как LBA0, затем LBA1, LBA2, и так далее GPT хранит защитную запись MBR в LBA0, свой основной заголовок в логическом блоке и записи разделов в блоках со второго по тридцать третий. \\ \hline
|
||||
|
||||
Структура GPT: (постепенное появление)
|
||||
\begin{itemize}
|
||||
\item Защитная MBR;
|
||||
\item Основной тег GPT;
|
||||
\item Записи разделов;
|
||||
\item Дополнительный GPT.
|
||||
\end{itemize}
|
||||
(и эти разом)
|
||||
\begin{itemize}
|
||||
\item Максимальная емкость раздела 9.4ZB;
|
||||
\item 128 первичных разделов;
|
||||
\item Устойчив к повреждению первичного раздела, так как имеет вторичный GPT;
|
||||
\item Возможность использовать функции UEFI.
|
||||
\end{itemize}
|
||||
& GPT состоит из:
|
||||
Защитной основной загрузочной записи. Защитная MBR — это пространство, зарезервированное в таблице разделов для устаревших целей. Оно находится в нулевом логическом блоке адресации. Система, которая не распознает новую таблицу разделов, скорее всего, захочет перезаписать диски с такой таблице, наличие же защитного раздела MBR обеспечивает обратную совместимость с системами, которые не распознают GPT. Защитная MBR охватывает либо весь диск, либо 2 ТБ, в зависимости от того, что меньше.
|
||||
%%
|
||||
Далее основной тег таблицы разделов, охватывающий с первого по 33й логические блоки адресации. LBA1 состоит из основного заголовка GUID Partition Table, который содержит указатель на таблицу разделов. Он также определяет объем свободного места на диске.
|
||||
%%
|
||||
Разделительные блоки - это используемые блоки диска, отформатированные в разделе в стиле GUID Partition Table, где хранятся фактические данные. На диске с 512-байтовыми секторами первый используемый блок это 34й логический блок.
|
||||
%%
|
||||
Схема таблицы разделов требует, чтобы копия основного GPT хранилась в последних секторах диска. Это обеспечивает избыточность схемы GPT, которую можно использовать в качестве резервной на случай повреждения или сбоя основного GPT.
|
||||
%%
|
||||
Из явных плюсов, Максимальная емкость раздела 9.4ZB (Зеттабайт); Максимум 128 первичных разделов; Устойчив к повреждению первичного GPT, поскольку он также имеет вторичный GPT; Возможность использовать функции UEFI, такие как безопасная загрузка, быстрый запуск и т. п. \\ \hline
|
||||
|
||||
MBR vs. GPT (постепенное появление)
|
||||
\begin{enumerate}
|
||||
\item Требования к прошивке: BIOS vs UEFI
|
||||
\item Поддержка Windows: х32 vs х64
|
||||
\item Максимальная ёмкость раздела: (232-1) х 512 байт = 2,19ТБ vs (264-1) x 512 байт = 9,44 ZB
|
||||
\item Количество разделов: 4 (или 3+1) vs 128
|
||||
\item Скорость загрузки: BIOS vs UEFI
|
||||
\item Безопасность данных
|
||||
\end{enumerate} & Резюмируя вышесказанное можно сказать, что различия между MBR и GPT заключаются в следующем:
|
||||
Прошивка — это программное обеспечение, обеспечивающее низкоуровневое управление аппаратным устройством и встроенное в само устройство. Базовая система ввода-вывода (BIOS) и унифицированный расширенный интерфейс встроенного ПО (UEFI) — это две встроенные программы, которые сегодня широко распространены в компьютерах. Для работы MBR требуется устаревшая прошивка BIOS, в то время как GPT, это часть спецификации UEFI. Если ваш диск разбит на разделы MBR, Windows предоставляет инструмент «diskpart» для преобразования его в GPT без потери данных.
|
||||
%%
|
||||
Windows 7 и более ранние версии Windows, работающие на 32-разрядных компьютерах, совместимы только с дисками с разделами MBR. Windows 8 и более поздние версии могут использовать диски с разделами GPT и MBR. 64-разрядные версии более ранних версий Windows могут читать и записывать с дисков с разделами GPT, но не могут загружаться с них.
|
||||
%%
|
||||
Максимальный размер диска, который может адресовать раздел MBR, ограничен 2 ТБ. Это связано с тем, что MBR хранит адреса и размеры блоков в таблице разделов с использованием 32-битных данных. С другой стороны, таблица разделов GPT может использовать 64-битные адреса. Таким образом, теоретический максимальный размер диска с разделами GPT составляет более 9ZB. Следует также отметить, что файловые системы Windows в настоящее время ограничены 256ТБ каждая.
|
||||
%%
|
||||
MBR выделяет 16 байт данных для каждой записи раздела и может выделить всего 64 байта. Таким образом, MBR может адресовать не более 4 основных разделов или 3 основных раздела и 1 расширенный раздел. Вы можете иметь неограниченное количество логических разделов внутри расширенного, однако вы можете установить ОС только в основной. Раздел GPT, с другой стороны, теоретически может иметь неограниченное количество первичных разделов. Несмотря на то, что его реализация в Windows ограничена только 128, каждый из них однако может быть основным.
|
||||
%%
|
||||
Хотя ни разделы MBR, ни разделы GPT не предназначены для работы быстрее друг друга, между ними может быть некоторая разница в скорости загрузки. Это связано с тем, что MBR использует устаревший BIOS, а GPT использует UEFI.
|
||||
%%
|
||||
MBR — это простая схема таблицы разделов, которая объединяет загрузочные данные и разделы. Таким образом, разделы MBR имеют более высокую вероятность потери данных в случае повреждения раздела. GPT разделяет таблицу разделов и блоки данных, что обеспечивает более надежную конфигурацию. Также, GPT имеет функцию безопасной загрузки, которая чуть лучше предотвращает захват процесса загрузки вредоносными программами \\ \hline
|
||||
|
||||
Вопросы для самопроверки & Что такое GPT?(2) General partition trace; GUID partition table; Greater pass timing \\ \hline
|
||||
|
||||
отбивка файловые системы & теперь мы знаем, что такое загрузчики, а значит понимаем, что устройство компьютера это не только монитор мышка и клавиатура. Настало время взглянуть на особенности организации файловой системы в операционной системе.\\ \hline
|
||||
|
||||
ФС - это архитектура хранения информации, размещенной на жестком диске и в оперативной памяти & файловая система всякой ОС, включая Linux – это некая архитектура хранения информации, размещенной на жестком диске и в оперативной памяти. С ее помощью пользователь получает доступ к структуре ядра системы. Он же отвечает за размещение файлов во всех разделах, поддерживая актуальную для него структуру, формирует правила для ее генерации, управляет блоками, исходя из особенностей определенного типа файловой системы.\\ \hline
|
||||
|
||||
отбивка Как обстоят дела в Linux & Начинаем рассмотрение всяких непонятных явлений и технологий, традиционно с тех, что попроще.\\ \hline
|
||||
|
||||
На что влияет файловая система?
|
||||
\begin{enumerate}
|
||||
\item скорость обработки данных;
|
||||
\item сбережение файлов;
|
||||
\item оперативность записи;
|
||||
\item допустимый размер блока;
|
||||
\item возможность хранения информации в оперативной памяти;
|
||||
\item способы корректировки пользователями ядра
|
||||
\item и пр.
|
||||
\end{enumerate} & ОС Линукс предоставляет возможность устанавливать в каждый отдельный блок свою файловую систему, которая и будет обеспечивать порядок поступающих и хранящихся данных, поможет с их организацией. Каждая ФС работает на наборе правил. Исходя из этого и определяется в каком месте и каким образом будет выполняться хранение информации. Эти правила лежат в основе иерархии системы, то есть всего корневого каталога. От того, насколько правильно администратор компьютера выберет тип файловой системы для каждого раздела, зависит ряд параметров, таких как: оперативность записи; скорость обработки данных; сбережение файлов; допустимый размер блока; возможность хранения информации в оперативной памяти; и другие \\ \hline
|
||||
|
||||
ФС. Категории: журналируемые, не журналируемые.
|
||||
\begin{itemize}
|
||||
\item Ext (extended) FS;
|
||||
\item Ext2, 3, 4;
|
||||
\item JFS, ReiserFS, XFS, Btrfs, F2FS;
|
||||
\item EncFS, Aufs, NFS;
|
||||
\item Tmpfs, Procfs, Sysfs;
|
||||
\end{itemize} & Все файловые системы Linux, которые применяются сегодня можно разделить на 2 отдельные категории: Журналируемые. Сохраняющие историю манипуляций пользователя и позволяющие её посмотреть, выполнить диагностику системы в отдельном специальном файле. Отличаются повышенной стойкостью к сбоям в функционировании, сохранностью целостности данных. и Не журналируемые. Здесь не предусмотрено сбережение логов, нет гарантий сохранности информации. Но зато в работе такие файловые системы более быстрые.
|
||||
|
||||
При установке на компьютер операционной системы, пользователь сможет остановить выбор на одной из множества файловых систем представленных на слайде. \\ \hline
|
||||
|
||||
Файлы в Linux (последовательное появление)
|
||||
\begin{itemize}
|
||||
\item Regular File
|
||||
\item Device File
|
||||
\item Soft Link
|
||||
\item Directories
|
||||
\item Block devices and sockets
|
||||
\end{itemize} & В файловой системе, что логично, хранятся файлы. Файлы в линукс - это особенная отдельная категория данных. Если коротко, с точки зрения ОС - всё файл. И все файлы, следовательно, делятся на категории.
|
||||
%%
|
||||
Обычные. Предназначены для хранения двоичной и символьной информации. Это жесткая ссылка, ведущая на фактическую информацию, размещенную в каталоге. Если этой ссылке присвоить уникальное имя, получим Named Pipe, то есть именованный канал.
|
||||
%%
|
||||
файлы для устройств, туннелей. Речь идет о физических устройствах, представленными в Линукс файлами. Могут классифицировать специальные символы и блоки. Обеспечивают мгновенный доступ к дисководам, принтерам, модемам, воспринимая их как простой файл с данными.
|
||||
%%
|
||||
мягкая (символьная) ссылка. Отвечает за мгновенный доступ к файлам, размещенным на любых носителях информации. В процессе копирования, перемещения и других действий пользователя, работающего по ссылке, будет выполняться операция над документом, на который ссылаются.
|
||||
%%
|
||||
Каталоги. Обеспечивают быстрый и удобный доступ к каталогам. Представляет собой файл с директориями и указателями на них. Это некого рода картотека: в папках размещаются документы, а в директориях – дополнительные каталоги.
|
||||
%%
|
||||
Блочные и символьные устройства. Выделяют интерфейс, необходимый для взаимодействия приложений с аппаратной составляющей. Каналы и сокеты. Отвечают за взаимодействие внутренних процессов в операционной системе. \\ \hline
|
||||
|
||||
отбивка Как обстоят дела в Windows & рассмотрим всю линейку файловых систем для Windows, чтобы понять, какую роль они играют в работе системы и как они развивались в процессе становления этой операционной системы \\ \hline
|
||||
|
||||
Файловая система FAT(16) File Allocation Table (с англ. таблица размещения файлов)
|
||||
|
||||
MS-DOS 3.0, Windows 3.x, Windows 95, Windows 98, Windows NT/2000 & была разработана достаточно давно и предназначалась для работы с небольшими дисковыми и файловыми объемами, простой структурой каталогов. Это таблица размещается в начале тома, причем хранятся две ее копии (в целях обеспечения большей устойчивости). Данная таблица используется операционной системой для поиска файла и определения его физического расположения на жестком диске. В случае повреждения и таблицы и ее копии чтение файлов операционной системой становится невозможно. Она просто не может определить, где какой файл, где он начинается и где заканчивается. В таких случаях говорят, что файловая система «упала». система изначально разрабатывалась компанией Microsoft для дискет. Только потом её стали применять для жестких дисков. Сначала это была FAT12 (для дискет и жестких дисков до 16 МБ), а потом она переросла в FAT16, которая была введена в эксплуатацию с операционной системой MS-DOS 3.0. Далее она поддерживается в Windows 3.x, Windows 95, Windows 98, Windows NT/2000 и остальных.\\ \hline
|
||||
|
||||
Файловая система FAT32
|
||||
\begin{itemize}
|
||||
\item возможность перемещения корневого каталога
|
||||
\item возможность хранения резервных копий
|
||||
\end{itemize}
|
||||
& Начиная с Windows 95, компания Microsoft начинает активно использовать в своих операционных системах FAT32 - тридцатидвухразрядную версию FAT. Технический прогресс не стоит на месте и возможностей FAT стало явно недостаточно. FAT32 стала обеспечивать более оптимальный доступ к дискам, более высокую скорость выполнения операций ввода/вывода, а также поддержку больших файловых объемов (объем диска до 2 Тбайт).
|
||||
Из-за использования более мелких кластеров, выгода по сравнению с FAT16 составляет порядка 10\%. Кроме того, необходимо отметить, что FAT32 обеспечивает более высокую надежность работы и более высокую скорость запуска программ. Обусловлено это двумя существенными нововведениями: Возможностью перемещения корневого каталога и резервной копии FAT (если основная копия получила повреждения); Возможностью хранения резервной копии системных данных. \\ \hline
|
||||
|
||||
Файловая система NTFS — от англ. New Technology File System, файловая система новой технологии
|
||||
\begin{itemize}
|
||||
\item права доступа
|
||||
\item Шифрование данных
|
||||
\item Дисковые квоты
|
||||
\item Хранение разреженных файлов
|
||||
\item Журналирование
|
||||
\end{itemize}
|
||||
& Ни одна из версий FAT не обеспечивает хоть сколько-нибудь приемлемого уровня безопасности. Это, а также необходимость в добавочных файловых механизмах (сжатия, шифрования) привело к необходимости создания принципиально новой файловой системы. И ею стала файловая система NTFS. В ней для файлов и папок могут быть назначены права доступа (на чтение, на запись и т.д.). Благодаря этому существенно повысилась безопасность данных и устойчивость работы системы. Кроме того, NTFS обеспечивает лучшую производительность и возможность работы с большими объемами данных.
|
||||
|
||||
Начиная с Windows 2000, используется версия NTFS 5.0, которая, помимо стандартных, позволяет реализовывать Шифрование данных, то есть данные могут быть прочитаны только на компьютере, на котором произошла шифровка. Стало возможно назначать пользователям определенный (ограниченный) размер на диске. Встречаются файлы, в которых содержится большое количество последовательных пустых байтов. Файловая система NTFS позволяет оптимизировать их хранение.
|
||||
Позволяет регистрировать все операции доступа к файлам и томам.
|
||||
|
||||
При этом, необходимо иметь в виду, что если для файла под NTFS были установлены определенные права доступа, а потом вы его скопировали на раздел FAT, то все его права доступа и другие уникальные атрибуты, присущие NTFS, будут утеряны. Так что будьте бдительны. \\ \hline
|
||||
|
||||
05-fat & Преимущества NTFS касаются практически всего: производительности, надежности и эффективности работы с данными (файлами) на диске. Так, одной из основных целей создания NTFS было обеспечение скоростного выполнения операций над файлами (копирование, чтение, удаление, запись), а также предоставление дополнительных возможностей: сжатие данных, восстановление поврежденных файлов системы на больших дисках и т.д. Другой основной целью создания NTFS была реализация повышенных требований безопасности, так как файловые системы FAT, FAT32 в этом отношении вообще никуда не годились. В NTFS можно разрешить или запретить доступ к какому-либо файлу или папке (разграничить права доступа).
|
||||
|
||||
Файловая система FAT для современных жестких дисков просто не подходит (ввиду ее ограниченных возможностей). Что касается FAT32, то ее еще можно использовать, но уже с натяжкой. Если купить жесткий диск на 1000 ГБ, то вам придется разбивать его как минимум на несколько разделов. А если вы собираетесь заниматься видеомонтажом, то вам будет очень мешать ограничение в 4 Гб как максимально возможный размер файла. Всех перечисленных недостатков лишена файловая система NTFS. Так что, даже не вдаваясь в детали и специальные возможности файловой системы NTFS, можно сделать выбор в ее пользу. Последние версии ОС Windows также начали поддерживать некоторые другие ФС, по крайней мере на уровне использования внешних запоминающих устройств. \\ \hline
|
||||
|
||||
Вопросы для самопроверки & Открытый вопрос: Что такое файловая система? (способ разбиения диска на сегменты, чтобы записывать данные). Возможно ли использовать разные файловые системы в рамках одной ОС? (да, главное, чтобы диск был корректно разделён) \\ \hline
|
||||
|
||||
отбивка Файловая система и представление данных & До появления Java 7 все операции проводились с помощью класса File. Немного разберём его, а потом посмотрим, на что его заменили. \\ \hline
|
||||
|
||||
лайвкод 05-Класс-File & В Java есть специальный класс (File), с помощью которого можно управлять файлами на диске компьютера. Для того чтобы управлять содержимым файлов, есть другие классы: FileInputStream, FileOutputStream и другие, которые мы рассмотрим позже. Под словами управлять файлами я подразумеваю, что их можно создавать, удалять, переименовывать, узнавать их свойства и еще много чего. Практически во все классы, которые работают с содержимым файла (читают, пишут, изменяют), можно передавать объект класса File.
|
||||
|
||||
File file = new File("file.txt");
|
||||
|
||||
Здесь мы видим привычное создание объекта, причём в параметре конструктора задано только имя и так называемое расширение файла, а значит файл будет открыт там, где исполняется программа. \\ \hline
|
||||
|
||||
05-директории & представим, что нужно вывести на экран список всех файлов, которые находятся в определенной директории.
|
||||
|
||||
File folder = new File(".");
|
||||
for (File file : folder.listFiles())
|
||||
System.out.println(file.getName());
|
||||
|
||||
Такой код выведет на экран всё содержимое текущей директории, о чём говорит точка в строке с именем файла. Если просто указать имя файла, то будет использован файл в той же папке, что исполняемая программа, а если указана точка, то это означает использование непосредственно данной папки. метод listFiles() возвращает список файлов из указанной папки. А метод getName() выдаёт имя файла с расширением. \\ \hline
|
||||
|
||||
05-манипуляции-х-2 & Вкратце поговорим о методах, которые доступны для объекта класса файл при работе с файлами и директориями, кроме рассмотренных на предыдущем слайде. В первую очередь, можно проверить, является ли объект файлом или директорией. Далее возможно узнать размер файла в байтах и его абсолютный путь (в примерах ранее мы пользовались относительным), порядковый номер жёсткого диска, на котором расположен файл. Манипулировать файлом мы тоже можем, например, удалить его (метод, кстати, возвращает истину или ложь, как результат, то есть удалось ли действие), а потом проверить, существует ли такой файл. Ну и так, например, можно узнать, сколько ещё свободного места доступно на диске, в директории которого мы находимся. Результаты работы методов мы видим на слайде. Могло показаться, что класс предоставляет достаточное количество функционала для работы с файловой системой, но это не совсем так, сейчас узнаем, почему. Также стоит сказать о том, что фактически у класса File почти все методы дублированы: одна версия возвращает String, вторая File\\ \hline
|
||||
|
||||
отбивка Классы Paths, Path, Files, FileSystem в Java 7 и позднее & В Java 7 создатели языка решили изменить работу с файлами и каталогами \\ \hline
|
||||
|
||||
05-файловая-система & Это произошло из-за того, что у класса File был ряд недостатков. Например, в нем не было метода copy(), который позволил бы скопировать файл из одного места в другое (казалось бы, довольно часто необходимая функция). Кроме того, в классе File было достаточно много методов, которые возвращали boolean-значения. При ошибке такой метод возвращает false, а не выбрасывает исключение, что делает диагностику ошибок и установление их причин достаточно непростым делом. Вместо единого класса File появились целых 3 класса: Paths, Path и Files. Если совсем-совсем точно, то Path — это интерфейс, а не класс, а интерфейсы мы рассмотрим буквально на следующей лекции. Также появился класс FileSystem, который предоставляет интерфейс к файловой системе. Файловая система работает как фабрика для создания различных объектов (Path, PathMatcher, Files). Этот объект помогает получить доступ к файлам и другим объектам в файловой системе. Например, можем получить все корневые каталоги и на этом пока остановимся. \\ \hline
|
||||
|
||||
URI - Uniform Resource Identifier (унифицированный идентификатор ресурса)
|
||||
|
||||
URL - Uniform Resource Locator (унифицированный определитель местонахождения ресурса)
|
||||
|
||||
URN - Unifrorm Resource Name (унифицированное имя ресурса)
|
||||
|
||||
05-путь & Разберемся с оставшимися классами уже более подробно, чем они друг от друга отличаются и зачем нужен каждый из них. Начнем с как всегда от простого к сложному. Paths — это совсем простой класс с единственным статическим методом get(). Его создали исключительно для того, чтобы из переданной строки или URI получить объект типа Path. Другой функциональности у него нет. Дополнительно раз и навсегда разделим понятия URI, URL, URN, ай - это идентификатор, Л - это локатор, то есть местонахождение, Н - это имя ресурса. \\ \hline
|
||||
|
||||
05-нет-файла & говоря о файлах очень сильно хочется сделать небольшое отступление и обратить внимание на FileNotFoundException. Это исключение возникает в случаях, которые нужно обработать на этапе компиляции: Файл по указанному пути не существует или существует, но почему-то недоступен (например, запрошена запись в файл со свойством только для чтения или разрешения файловой системы не позволяют получить доступ к файлу). Как видно на структуре, прекрасно видно всю цепочку наследования. Непосредственно FileNotFoundException наследуется от IOException, который сигнализирует о том, что произошло какое-то исключение ввода-вывода. \\ \hline
|
||||
|
||||
05-действия-с-путями & Вернёмся к коду, и, раз уж мы получили объект типа Path, разберёмся, что это за Path такой и зачем он нужен. По большому счету — это переработанный аналог класса File. Работать с ним значительно проще, чем с File. Во-первых, из него убрали многие статические методы, во-вторых, в Path были упорядочены возвращаемые значения методов. В классе File методы возвращали то String, то boolean, то File — разобраться было непросто. Например, метод getParent(), возвращал родительский путь для текущего файла в виде строки. Но при этом есть метод getParentFile(), который возвращал то же самое, но в виде объекта File. Это явно избыточно. Поэтому в Path метод getParent() и другие методы работы с файлами возвращают просто объект Path. Никакой кучи вариантов — все легко и просто. С помощью этого класса можем как получить родителя, так и абсолютный корень пути, проверить начинается ли наш путь с чего-то и не заканчивается ли чем-то. Обратите внимание на то, как работает метод endsWith(). Он проверяет, заканчивается ли текущий путь на переданный путь. Именно на путь, а не на набор символов. В методы startsWith и endsWith() нужно передавать именно путь, а не просто набор символов: в противном случае результатом всегда будет false, даже если текущий путь действительно заканчивается такой последовательностью символов\\ \hline
|
||||
|
||||
05-очистка-путей & Кроме того, в Path есть группа методов, которая упрощает работу с абсолютными, то есть полными и относительными путями. Проверка на абсолютность в скриптовом ноутбуке не может вернуть истину, а в реальном приложении вполне. Метод normalize() - «нормализует» текущий путь, удаляя из него ненужные элементы. Вы, могли заметить, что в популярных операционных системах при обозначении путей часто используются символы “.” (для обозначения текущей директории”) и “..” (для родительской директории). Так вот, если в нашей программе появился путь, использующий “.” или “..”, метод normalize() позволит удалить их и получить путь, в котором они не будут содержаться. У Path довольно много методов, их вы можете рассмотреть подробнее уже самостоятельно. \\ \hline
|
||||
|
||||
05-манипуляции-файл & а пока что перейдём к рассмотрению класса Files. Files — это утилитный класс, куда были вынесены статические методы из класса File. Он сосредоточен на управлении файлами и директориями. Используя статические методы Files, мы можем создавать, удалять, копировать и перемещать файлы и директории. Для этих операций используются соответствующие методы createFile() для файлов, для директорий — createDirectory(), copy(), move() и delete(). для начала, методом createFile() в директории pics создали файл.тхт для экспериментов, далее по немного другому пути создаём директорию методом createDirectory(). После этого перемещаем файл методом move() из директории pics в эту новую папку, (здесь можно заметить параметр REPLACE\_EXISTING, - то есть нужно выполнить замену существующего файла, если он существует), затем копирование файла из тестовой директории обратно в директорию Java методом copy() (который тоже принимает этот параметр, если нужно), а в конце — удаляется файл и тестовая директория методом delete().\\ \hline
|
||||
|
||||
05-работа-с-содержимым & класс Files позволяет не только управлять самими файлами, но и работать с его содержимым. Для записи данных в файл у него есть метод write(), а для чтения — целых 3: read(), readAllBytes() и readAllLines() Мы подробно остановимся на методе записи и методе чтения всех строк. Почему именно на нём? Потому что у него есть очень интересный тип возвращаемого значения — List. То есть он возвращает нам список строк файла. Конечно, это делает работу с содержимым очень удобной, ведь весь файл, строку за строкой, можно, например, вывести в консоль в обычном цикле for. Кстати именно так представляет данные в файле текстовый редактор vim. Создадим файлик cat.txt и добавим в него какое-нибудь многострочное описание, а затем прочитаем его из файла обратно. При этом, обратите внимание, что если файл уже существует, создать его не получится, будет выброшено исключение типа FileAlreadyExists, поэтому будет логично после всех наших манипуляций файл удалить. \\ \hline
|
||||
|
||||
Вопросы для самопроверки & Какое исключение выбрасывает программа, если не может открыть файл? (filenotfoundexception). Ссылка на местонахождение - это: URI, URL, URN. \\ \hline
|
||||
|
||||
отбивка java.io & Полностью отказаться от использования пакета ИО вряд ли получится, поэтому начнём с короткого обзора именно его, ну и ещё потому что он появился раньше. \\ \hline
|
||||
|
||||
05-ввод-вывод-без-потоков & Подавляющее большинство программ обменивается данными со внешним миром. Это, безусловно, делают любые сетевые приложения – они передают и получают информацию от других компьютеров и специальных устройств, подключенных к сети. Оказывается, можно точно таким же образом представлять обмен данными между устройствами внутри одной машины. Так, например, программа может считывать данные с клавиатуры и записывать их в файл, или же, наоборот - считывать данные из файла и выводить их на экран. Таким образом, устройства, откуда может производиться считывание информации, могут быть самыми разнообразными – файл, клавиатура, входящее сетевое соединение и т.д. То же касается и устройств вывода – это может быть файл, экран монитора, принтер, исходящее сетевое соединение и т.п. В конечном счете, все данные в компьютерной системе в процессе обработки передаются от устройств ввода к устройствам вывода. \\ \hline
|
||||
|
||||
05-абстрактный-ввод-вывод & Реализация системы ввода/вывода осложняется не только широким спектром источников и получателей данных, но еще и различными форматами передачи информации. Ею можно обмениваться в двоичном представлении, символьном или текстовом, с применением некоторой кодировки (кодировок только для русского языка их более 4 типов), или передавать числа в различных представлениях. Доступ к данным может потребоваться как последовательный, так и произвольный. Зачастую для повышения производительности применяется буферизация. В Java для описания работы по вводу/выводу используется специальное понятие потока данных (stream). Поток данных это абстракция, физически, конечно никакие потоки в компьютере не текут. Поток связан с некоторым источником, или приемником, данных, способным получать или предоставлять информацию. Соответственно, потоки делятся на входящие – читающие данные и выходящие – передающие (записывающие) данные. Введение концепции stream позволяет абстрагировать основную логику программы, обменивающейся информацией с любыми устройствами одинаковым образом, от низкоуровневых операций с такими устройствами ввода/вывода, для программы нет разницы, передавать данные в файл или в сеть, принимать с клавиатуры или со специализированного устройства. В Java потоки естественным образом представляются объектами. Описывающие их классы как раз и составляют основную часть пакета java.io. Они довольно разнообразны и отвечают за различную функциональность. Все классы разделены на две части – одни осуществляют ввод данных, другие – вывод.\\ \hline
|
||||
|
||||
Классы InputStream и OutputStream
|
||||
|
||||
InputStream – это базовый абстрактный класс для потоков ввода, т.е. чтения.
|
||||
|
||||
05-1-input & Как и говорилось, все типы поделены на две группы. Практически все классы пакета ИО осуществляющие ввод-вывод, так или иначе наследуются от InputStream для входных данных, и для выходных – от OutputStream.
|
||||
Input Stream описывает базовые методы для работы со входящими байтовыми потоками данных. Класс довольно подробно задокументирован, при желании можно прочитать информацию перед методом и понять, как именно он работает. Простейшая операция представлена методом read() (без аргументов). Он является абстрактным и, соответственно, должен быть определен в классах-наследниках. Согласно документации, этот метод предназначен для считывания ровно одного байта из потока, однако возвращает при этом значение типа int. В том случае, если считывание произошло успешно, возвращаемое значение лежит в диапазоне от 0 до 255 и представляет собой полученный байт (значение int содержит 4 байта и получается простым дополнением нулями в двоичном представлении). Обратите внимание, что полученный таким образом байт не обладает знаком и не находится в диапазоне от -128 до +127, как примитивный тип byte в Java. Если достигнут конец потока, то есть в нем больше нет информации для чтения, то возвращаемое значение равно -1. Если же считать из потока данные не удается из-за каких-то ошибок, или сбоев, будет брошено исключение java.io.IOException. Этот класс наследуется от Exception, если вспомнить ранее нами разобранную струтуру наследования, т.е. его всегда необходимо обрабатывать явно. Дело в том, что каналы передачи информации, будь то Internet или, например, жесткий диск, могут давать сбои независимо от того, насколько хорошо написана программа. А это означает, что нужно быть готовым к ним, чтобы пользователь не потерял нужные данные. Когда работа с входным потоком данных окончена, его следует закрыть. Для этого вызывается метод close(). Этим вызовом будут освобождены все системные ресурсы, связанные с потоком. \\ \hline
|
||||
|
||||
Классы InputStream и OutputStream
|
||||
|
||||
OutputStream – это базовый абстрактный класс для потоков вывода, т.е. записи. & \\ \hline
|
||||
|
||||
05-1-output & В классе OutputStream аналогичным образом определяются три метода write() – один принимающий в качестве параметра int, второй – byte[] и третий – byte[], и два int-числа. Все эти методы ничего не возвращают. Для записи в поток сразу некоторого количества байт методу write() передается массив байт. Или, если мы хотим записать только часть массива, то передаем массив byte[] и два числа – отступ и количество байт для записи. Понятно, что если указать неверные параметры – например, отрицательный отступ, отрицательное количество байт для записи, либо если сумма отступ плюс длина будет больше длины массива, – во всех этих случаях кидается исключение IndexOutOfBoundsException. Реализация потока вывода может быть такой, что данные записываются не сразу, а хранятся некоторое время в памяти. Чтобы убедиться, что данные записаны в поток, а не хранятся в буфере, вызывается метод flush(), определенный в OutputStream. Когда работа с потоком закончена, его следует закрыть. Для этого вызывается метод close(). Закрытый поток не может выполнять операции вывода и не может быть открыт заново. В классе OutputStream реализация метода close() не производит никаких действий.
|
||||
Итак, классы InputStream и OutputStream определяют необходимые методы для работы с байтовыми потоками данных. Эти классы являются абстрактными. Их задача – определить общий интерфейс для классов, которые получают данные из различных источников. Такими источниками могут быть, например, массив байт, файл, строка и т.д. \\ \hline
|
||||
|
||||
Классы ByteArrayInputStream и ByteArrayOutputStream
|
||||
|
||||
03-байт-массивы & Самый естественный и простой источник, откуда можно считывать байты, – это, конечно, массив байт. Класс ByteArrayInputStream представляет поток, считывающий данные из массива байт. Этот класс имеет конструктор, которому в качестве параметра передается массив byte[]. Соответственно, при вызове методов read() возвращаемые данные будут браться именно из этого массива. Аналогично, для записи байт в массив применяется класс ByteArrayOutputStream. Этот класс использует внутри себя объект byte[], куда записывает данные, передаваемые при вызове методов write(). Чтобы получить записанные в массив данные, вызывается метод toByteArray(). В этом примере вначале будет создан массив, который состоит из трёх элементов: 1, -1 и 0. Затем, при вызове метода read() данные считывались из массива, переданного в конструктор ByteArrayInputStream. Обратите внимание, в данном примере второе считанное значение равно 255, а не -1, как можно было бы ожидать. Чтобы понять, почему это произошло, нужно вспомнить, что метод read считывает byte, но возвращает значение int, полученное добавлением необходимого числа нулей (в двоичном представлении). Про двоичные представления мы довольно подробно говорили на самой первой лекции. Использовать эти классы может быть очень удобно, когда нужно проверить, что именно записывается в выходной поток. Например, при отладке и тестировании сложных процессов записи и чтения из потоков. Эти классы хороши тем, что позволяют сразу просмотреть результат и не нужно создавать ни файл, ни сетевое соединение, ни что-либо еще. \\ \hline
|
||||
|
||||
Классы FileInputStream и FileOutputStream
|
||||
|
||||
03-файл-потоки & Класс FileInputStream используется для чтения данных из файла. Конструктор такого класса в качестве параметра принимает название файла, из которого будет производиться считывание. При указании строки имени файла нужно учитывать, что она будет напрямую передана операционной системе, поэтому формат имени файла и пути к нему может различаться на разных платформах. Если при вызове этого конструктора передать строку, указывающую на несуществующий файл или каталог, то будет брошено java.io.FileNotFoundException. Если же объект успешно создан, то при вызове его методов read() возвращаемые значения будут считываться из указанного файла.
|
||||
Для записи байт в файл используется класс FileOutputStream. При создании объектов этого класса, то есть при вызовах его конструкторов, кроме имени файла, также можно указать, будут ли данные дописываться в конец файла, либо файл будет полностью перезаписан. Здесь есть очень важный момент, если не указан флаг добавления, то всегда сразу после создания FileOutputStream файл будет создан. При вызовах методов write() передаваемые значения будут записываться в этот файл. По окончании работы необходимо вызвать метод close(), чтобы сообщить системе, что работа по записи файла закончена.
|
||||
При работе с FileInputStream метод available() практически наверняка вернет длину файла, то есть число байт, сколько вообще из него можно считать. Но не стоит закладываться на это при написании программ, которые должны устойчиво работать на различных платформах,– метод available() возвращает число байт, которое может быть на данный момент считано без блокирования. Тот факт, что, скорее всего, это число и будет длиной файла, является всего лишь частным случаем работы на некоторых платформах. В приведенном примере для наглядности закрытие потоков производилось сразу же после окончания их использования в основном блоке. Однако лучше, как мы помним, использовать трай-с-ресурсами, чтобы гарантировать, что поток будет закрыт и будут высвобождены все занимаемые им ресурсы. \\ \hline
|
||||
|
||||
Другие потоковые классы (последовательное появление)
|
||||
\begin{itemize}
|
||||
\item PipedInputStream и PipedOutputStream
|
||||
\item StringBufferInputStream (deprecated)
|
||||
\item SequenceInputStream
|
||||
\item FilterInputStream и FilterOutputStream и их наследники
|
||||
\end{itemize} &
|
||||
Классы PipedInputStream и PipedOutputStream характеризуются тем, что их объекты всегда используются в паре – к одному объекту PipedInputStream привязывается (подключается) один объект PipedOutputStream. Они могут быть полезны, если в программе необходимо организовать обмен данными между модулями (например, между потоками выполнения). Более явно выгода от использования PipedI/OStream в основном проявляется при разработке многопоточных приложений.
|
||||
%%
|
||||
Иногда бывает удобно работать с текстовой строкой String как с потоком байт. Для этого можно воспользоваться классом StringBufferInputStream. При создании объекта этого класса необходимо передать конструктору объект String. Данные, возвращаемые методом read(), будут считываться именно из этой строки. При этом символы будут преобразовываться в байты с потерей точности – старший байт отбрасывается (напомню, что char и как символ и как тип данных состоит из двух байт).
|
||||
%%
|
||||
Класс SequenceInputStream объединяет поток данных из других двух и более входных потоков. Данные будут вычитываться последовательно – сначала все данные из первого потока в списке, затем из второго, и так далее. Конец потока SequenceInputStream будет достигнут только тогда, когда будет достигнут конец потока, последнего в списке.
|
||||
%%
|
||||
Задачи, возникающие при вводе/выводе весьма разнообразны - это может быть считывание байтов из файлов, объектов из файлов, объектов из массивов, буферизованное считывание строк из массивов и т.д. В такой ситуации решение с использованием простого наследования приводит к возникновению слишком большого числа подклассов. Более эффективно применение надстроек (в ООП этот шаблон называется адаптер) Надстройки – наложение дополнительных объектов для получения новых свойств и функций. Таким образом, необходимо создать несколько дополнительных объектов – адаптеров к классам ввода/вывода. В java.io их еще называют фильтрами. Отсюда и название фильтрующих потоков \\ \hline
|
||||
|
||||
Другие потоковые классы (продолжение)(последовательное появление)
|
||||
\begin{itemize}
|
||||
\item LineNumberInputStream, LineNumberReader
|
||||
\item PushBackInputStream
|
||||
\item PrintStream, PrintWriter
|
||||
\end{itemize} &
|
||||
Класс LineNumberInputStream во время чтения данных производит подсчет, сколько строк было считано из потока. Номер строки, на которой в данный момент происходит чтение, можно узнать путем вызова метода getLineNumber(). Также можно и перейти к определенной строке вызовом метода setLineNumber(int lineNumber). Этот класс практически разу объявили устаревшим и вместо него используется LineNumberReader с аналогичным функционалом.
|
||||
%%
|
||||
Этот фильтр позволяет вернуть во входной поток считанные из него данные. Такое действие производится вызовом метода unread(). Понятно, что обеспечивается подобная функциональность за счет наличия в классе специального буфера – массива байт, который хранит считанную информацию.
|
||||
%%
|
||||
PrintStream используется для конвертации и записи строк в байтовый поток. В нем определен метод print(), принимающий в качестве аргумента различные примитивные типы Java, а также тип Object. При вызове передаваемые данные будут сначала преобразованы в строку, после чего записаны в поток. Если возникает исключение, оно обрабатывается внутри метода print и дальше не бросается (узнать, произошла ли ошибка, можно с помощью метода checkError()). Данный класс также считается устаревшим, и вместо него рекомендуется использовать PrintWriter, однако старый класс продолжает активно использоваться, поскольку статические поля out и err класса System имеют именно это тип. \\ \hline
|
||||
|
||||
BufferedInputStream и BufferedOutputStream 05-сравнение-1 & Отдельно стоит сказать о BufferedInputStream и BufferedOutputStream. На практике при считывании с внешних устройств ввод данных почти всегда необходимо буферизировать. BufferedInputStream содержит массив байт, который служит буфером для считываемых данных. То есть когда байты из потока считываются либо пропускаются (методом skip()), сначала заполняется буферный массив, причем, из потока загружается сразу много байт, чтобы не требовалось обращаться к нему при каждой операции read или skip. BufferedOutputStream предоставляет возможность производить многократную запись небольших блоков данных без обращения к устройству вывода при записи каждого из них. Сначала данные записываются во внутренний буфер. Непосредственное обращение к устройству вывода и, соответственно, запись в него, произойдет, когда буфер заполнится. Инициировать передачу содержимого буфера на устройство вывода можно и явным образом, вызвав метод flush(). Для наглядности заполним небольшой файл данными, буквально 10 миллионов символов, не так много. \\ \hline
|
||||
|
||||
05-сравнение-2 & На следующем шаге просто прочитаем эти символы из файла простым потоком ввода из файла, видим, что это заняло сколько-то времени \\ \hline
|
||||
|
||||
05-сравнение-3 & и видим, что буферизованный поток справляется ровно с той-же задачей на пару порядков быстрее. Выгода использования налицо.\\ \hline
|
||||
|
||||
Усложнение данных DataInputStream и DataOutputStream 05-данные-1 & До сих пор речь шла только о считывании и записи в поток данных в виде byte. Для работы с другими типами данных Java определены интерфейсы DataInput и DataOutput и их реализации – классы-фильтры DataInputStream и DataOutputStream. Интерфейсы DataInput и DataOutput определяют, а классы DataInputStream и DataOutputStream, соответственно, реализуют методы считывания и записи значений всех примитивных типов. При этом происходит конвертация этих данных в набор byte и обратно. Чтение необходимо организовать так, чтобы данные запрашивались в виде тех же типов, в той же последовательности, как и производилась запись. Если записать, например, int и long, а потом считывать их как short, чтение будет выполнено корректно, без исключительных ситуаций, но числа будут получены совсем другие. Собственно, ничто не мешает нам взглянуть на это на примере. \\ \hline
|
||||
|
||||
05-данные-2 & Далее прочитаем данные так, как мы записали, видим, что все значения прочитаны корректно \\ \hline
|
||||
|
||||
05-данные-3 & А затем занимаемся нашим любимым делом - всё ломаем и видим, что всё послушно сломалось. А зачем мы это ломаем? правильно, чтобы сломать сейчас и не ломать потом на работе. \\ \hline
|
||||
|
||||
Ещё сложнее ObjectInputStream и ObjectOutputStream & Для объектов процесс преобразования в последовательность байт и обратно организован несколько сложнее – объекты имеют различную структуру, хранят ссылки на другие объекты и т.д. Поэтому такая процедура получила специальное название - сериализация (serialization), обратное действие, – то есть воссоздание объекта из последовательности байт – десериализация. Подробнее изучать мы их будем чуть позже, когда разберёмся с интерфейсами и всякими внешними библиотеками, но не упомянуть их в нашем сегодняшнем контексте было нельзя. \\ \hline
|
||||
|
||||
Классы Reader и Writer
|
||||
|
||||
05-байты-символы
|
||||
|
||||
Классы-мосты InputStreamReader и OutputStreamWriter & Рассмотренные классы – наследники InputStream и OutputStream – работают с байтовыми данными. Если с их помощью записывать или считывать текст, то сначала необходимо сопоставить каждому символу его числовой код. Такое соответствие называется кодировкой. Так вот, джава предоставляет классы, практически полностью дублирующие байтовые потоки по функциональности, но называющиеся читателями и писателями, соответственно. Различия между байтовыми и символьными классами весьма незначительны. FileInputStream - FileReader, FileOutputStream - FileWriter, BufferedInputStream - BufferedReader, PrintStream - PrintWriter и так далее. Классы-мосты InputStreamReader и OutputStreamWriter при преобразовании символов также используют некоторую кодировку. Ее можно задать, передав в конструктор в качестве аргумента ее название. Если оно не будет соответствовать никакой из известных кодировок, будет брошено исключение.\\ \hline
|
||||
|
||||
Вопросы для самопроверки & Возможно ли чтение совершенно случайного байта данных из объекта BufferedInputStream? (да, потому что мы читаем из буфера). Возможно ли чтение совершенно случайного байта данных из потока, к которому подключен объект BufferedInputStream? (нет, потому что потоки всегда только однонаправленные, мы не можем знать, есть ли вообще там данные, которые мы собрались читать) \\ \hline
|
||||
|
||||
отбивка java.nio и nio2 & Основное отличие между двумя подходами к организации ввода/вывода в том, что Java IO является потокоориентированным, а Java NIO – буфер-ориентированным \\ \hline
|
||||
|
||||
io vs nio
|
||||
|
||||
05-потоки-и-буферы & Потокоориентированный ввод/вывод подразумевает чтение или запись из потока и в поток одного или нескольких байт в единицу времени поочередно. Данная информация нигде не кэшируются. Таким образом, невозможно произвольно двигаться по потоку данных вперед или назад. Если вы хотите произвести подобные манипуляции, вам придется сначала кэшировать данные в буфере.
|
||||
Подход, на котором основан Java NIO немного отличается. Данные считываются в буфер для последующей обработки. Вы можете двигаться по буферу вперед и назад. Это дает немного больше гибкости при обработке данных. В то же время, вам необходимо проверять содержит ли буфер необходимый для корректной обработки объем данных. Также необходимо следить, чтобы при чтении данных в буфер вы не уничтожили ещё не обработанные данные, находящиеся в буфере. \\ \hline
|
||||
|
||||
05-ожидание-чтения & Потоки ввода/вывода (streams) в Java IO являются блокирующими. Это значит, что когда в потоке выполнения вызывается read() или write() метод любого класса из пакета java.io.*, происходит блокировка до тех пор, пока данные не будут считаны или записаны. Поток выполнения в данный момент не может делать ничего другого.
|
||||
Неблокирующий режим Java NIO позволяет запрашивать считанные данные из канала (channel) и получать только то, что доступно на данный момент, или вообще ничего, если доступных данных пока нет. Вместо того, чтобы оставаться заблокированным пока данные не станут доступными для считывания, поток выполнения может заняться чем-то другим. \\ \hline
|
||||
|
||||
05-каналы & Каналы – это логические порталы, через которые осуществляется ввод/вывод данных, а буферы являются источниками или приёмниками этих переданных данных. При организации вывода, данные, которые вы хотите отправить, помещаются в буфер, а он передается в канал. При вводе, данные из канала помещаются в предоставленный вами буфер.
|
||||
Также, в Java NIO появилась возможность создать поток, который будет знать, какой канал готов для записи и чтения данных и может обрабатывать этот конкретный канал. Данные считываются в буфер для последующей обработки. Разработчик может двигаться по буферу вперед и назад, что дает нам немного больше гибкости при обработке данных. В то же время нужно проверять, содержит ли буфер необходимый для корректной обработки объем данных. Также не забывать следить, чтобы при чтении данных в буфер не уничтожились еще не обработанные данные, находящиеся там. \\ \hline
|
||||
|
||||
05-канал-буфер & Взглянем на пример с использованием буфера и канала. Подготовив всё необходимое мы прочитали данные в буфер, сохранив число прочитанных байт, а затем посимвольно их вывели в консоль. Для чтения данных из файла используется файловый канал. Объект файлового канала может быть создан только вызовом метода getChannel() для файлового объекта, поскольку нельзя напрямую создать объект файлового канала. Кроме FileChannel есть и другие реализации каналов, которые затрагивать пока не будем. При этом, FileChannel нельзя переключить в неблокирующий режим. \\ \hline
|
||||
|
||||
отбивка String & Класс String отвечает за создание строк, состоящих из символов. \\ \hline
|
||||
|
||||
private final char value[];
|
||||
|
||||
private final byte[] value;
|
||||
|
||||
\begin{itemize}
|
||||
\item immutable
|
||||
\item final
|
||||
\end{itemize} & если быть точнее, заглянув в реализацию и посмотрев способ их хранения, то строки (до Java 9) представляют собой массив символов, а начиная с Java 9 строки хранятся как массив байт. Как я говорил ранее, в первую очередь нас будет интересовать джава8. В отличие от других языков программирования, где символьные строки представлены последовательностью символов, в Java они являются объектами класса String. В результате создания объекта типа String получается неизменяемая символьная строка, т.е. невозможно изменить символы имеющейся строки. При любом изменении строки создается новый объект типа String, содержащий все изменения. А значит у String есть две фундаментальные особенности: это immutable (неизменный) класс; это final класс (у класса String не может быть наследников). \\ \hline
|
||||
|
||||
Экземпляр класса String
|
||||
|
||||
05-конструкторы-строки
|
||||
|
||||
ASCII - American standard code for information interchange & Экземпляр класса String можно создать множеством способов. Несмотря на кажущуюся простоту и если можно так сказать, общеупотребимость, класс строки - это довольно сложная структура данных и гигантский набор методов. Одних только конструкторов, вон, больше 6 штук. В примере выше представлена лишь часть возможных вариантов. Самым простым способом является создание строки по аналогии с s1. Обычно строки требуется создавать с начальными значениями. Для этого предоставлены разнообразные конструкторы. При использовании варианта s2 в памяти создается новый экземпляр строки с начальным значением “Home”. Если в программе есть массив символьных строк, то из них можно собрать строку с помощью конструктора, которому в качестве аргумента передается ссылка на массив char[]. Если нужно создать объект типа String, содержащий ту же последовательность символов, что и другой объект типа String, можно использовать вариант s4. Варианты s5 и s6 позволяют «построить» строку из байтового массива без указания кодировки или с ней. Если кодировка не указана, будет использована ASCII.\\ \hline
|
||||
|
||||
05-конкатенация & C помощью операции конкатенации, которая записывается, как знак +, можно соединять две символьные строки, порождая в итоге совершенно новый объект типа String. Операции такого сцепления символьных строк можно объединять в цепочку. Ниже приведено несколько примеров. Как видно из примера, строки можно сцеплять с другими типами данных, например, целыми числами(int). Так происходит потому, что значение типа int автоматически преобразуется в своё строковое представление в объекте типа String. После этого символьные строки сцепляются, как и прежде. \\ \hline
|
||||
|
||||
05-проблема-конкатенации & Сцепляя разные типы данных с символьными строками, следует быть внимательным, иначе можно получить неожиданные результаты. Такой результат объясняется так называемой ассоциативностью оператора сложения. Оператор сложения работает слева направо, а значит сначала будет выполнять то, что слева, а потом то, что справа. Расширение типа до максимального происходит автоматически, так если складывать целое и дробное, в результате будет дробное, если складывать любое число и строку получится строка. А однажды получив строку всё остальное будет тоже становиться строкой и сложение превратится в конкатенацию. \\ \hline
|
||||
|
||||
Часто используемые методы строки
|
||||
\begin{itemize}
|
||||
\item int length()
|
||||
\item char charAt(int pos)
|
||||
\item char[] toCharArray()
|
||||
\item boolean equals(Object obj)
|
||||
\item boolean equalsIgnoreCase(Object obj)
|
||||
\item String concat(String obj)
|
||||
\item String toLowerCase(), String toUpperCase()
|
||||
\end{itemize} & На слайде представлено несколько часто используемых методов для работы со строками. Это не полный список. Здесь получение длины строки, Извлечение из строки символа, находящегося на позиции pos, индексация символов начинается с нуля, Преобразование строки в массив символов, Посимвольное сравнение строк, Сравнение строк без учета регистра, Объединение двух строк в одну. Этот метод создает новый строковый объект, содержащий вызываемую строку, в конце которой добавляется содержимое параметра строка. Метод выполняет то же действие, что и операция +. Преобразование всех символов строки в нижний регистр (toLowerCase), в верхний регистр (toUpperCase), все их перечислять, конечно же, смысла не имеет, исходный код класса открыт и я рекомендую пройти туда.\\ \hline
|
||||
|
||||
!!!строго лайвкод!!! StringBuffer, StringBuilder 05-билдер & Поскольку строка это достаточно неповоротливо, придумали прекрасные классы, которые позволяют ускорить работу с ними. Сразу пойдём от практики и объявим переменную
|
||||
|
||||
String s = “Example”;
|
||||
|
||||
сделаем цикл, скажем, до 30000, и будем делать одно и тоже 30 тыщ раз. s = s + i; В результате получим строку Example012345... просто какая-то бесполезная очень длинная строчка. Много арифметических действий, много созданий новых строк, а старые будут попадать в маленький ад для Java строк. Шучу, попадут они под сборку мусора. Быстро сделаем более точный замер времени.
|
||||
|
||||
long timeStart = System.nanoTime();
|
||||
for (int i = 0; i < 30000; ++i) {
|
||||
s = s + i;
|
||||
}
|
||||
double deltaTime = (System.nanoTime() - timeStart) * 0.000000001;
|
||||
System.out.println("Delta time: " + deltaTime);
|
||||
|
||||
Жмём Run Ждём и видим, что наша супер сложная программа выполнялась 0,8. секунд. Давайте сделаем побольше, скажем, сто тысяч итераций, а лучше миллион, просто хочется показать контраст.
|
||||
%%
|
||||
Пока ждём расскажу немного о самих стрингбилдере и стрингбуффере и разнице между ними. Если у вас в программе намечается какая-то большущая работа со стрингами в каких то гигантских циклах, вспомните про StringBuilder. И помните, что Если у вас что-то “тормозит”, то с 90\% вероятностью уже придумали способ это ускорить, и велосипеды изобретать не нужно. Если немного приоткрыть завесу, то все строковые классы работают с массивами чаров, только стринг делает это примитивно, выделяя каждый раз новый кусок памяти под массив с длиной равной длине строки, а билдер сразу выделяет большой кусок памяти под массив, добавляет к нему элементы по индексу, а если массив заканчивается, то тогда делает массив ещё больше, раза в два, копирует в него старый, и заполняет уже его. Схожим образом работает ArrayList, соответственно СБУФЕР работает точно также но его можно использовать в многопоточных программах, но со связными списками и многопоточностью Вы будете работать на следующем этапе обучения. СБУФЕР потокобезопасен. потокобезопасным называют класс, с которым можно работать из разных потоков в рамках одной программы, и быть уверенным, что ничего не сломается. Там ОЧЕНЬ интересно и прикольно, происходит рассинхронизация программы на несколько потоков и начинается самое крутое.\\ \hline
|
||||
%%
|
||||
& ну вот, у нас получилось достаточно много времени, по ощущениям примерно столько времени и прошло. Ну а теперь к оптимизации. Разработчики Java знали, что перед программистами будут стоять задачки и посерьёзнее, чем обработка 100000 символьных строк, например, при разборе текстовых файлов, и поиске информации в электронных книгах. И придумали StringBuilder и StringBuffer. Создают они изменяемые строки и динамические ссылки на них. Их разница в том, что StringBuilder не потокобезопасный, и работает чуть быстрее, а StringBuffer - используется в многопоточных средах, но в одном потоке работает чуть медленнее. Мы в нашем примере будем использовать StringBuilder. Повторим тоже самое, но только с использованием StringBuilder
|
||||
|
||||
StringBuilder sb = new StringBuilder("Example");
|
||||
long timeStart = System.nanoTime();
|
||||
for (int i = 0; i < 100\_000; ++i) { sb = sb.append(i); }
|
||||
double deltaTime = (System.nanoTime() - timeStart) * 0.000000001;
|
||||
System.out.println("Delta time (sec): " + deltaTime);
|
||||
|
||||
В конструктор передали начальное значение нашей строки. То есть теперь это всё ещё стринг, но представленный другим классом, не являющимся тем удобным для большинства нужд String, то есть мы до одной из последних редакции джавы не могли просто взять и например вывести в sout его. или, к примеру, не можем неявно его создать, как мы создаём обычные String, но в нём есть много других весьма полезных методов, которых нет в классе String. Например, метод append, который делает, по сути, тоже самое, что и s=s+” ”; то есть добавляет что то в конец существующей строки. Ну и чтобы удостовериться, что никакого обмана там нет, можем сравнить полученные первым и вторым способом строки.
|
||||
|
||||
Хотел сказать Ждём ВАУ эффекта, но ждать не придётся, вот он. Разница очевидна. и в результате сравнения строк, можете проверить сами, у нас вернулся true значит строки получились одинаковые. Вот и всё, что вам пока что нужно знать о строках.
|
||||
|
||||
Надеюсь вы за это время поняли, что String не так то прост. Так вот тоже самое характерно и для всех остальных языков программирования, вообще для всех. Поэтому в чистом Си нет класса String, там есть только массив из чар фиксированной длины, и кому охота работать со стрингами пишет их самостоятельно ручками, думая о выделении памяти и так далее. А для более высокоуровневых языков - каждый разработчик языка пляшет как хочет, поэтому и получается местами сложно, часто не очевидно, и у всех по-разному.\\ \hline
|
||||
|
||||
Вопросы для самопроверки & String - это: -объект; -примитив; -класс. Строки в языке Java это: -классы; -массивы; -объекты. \\ \hline
|
||||
|
||||
отбивка String pool & Строки в бассейне? Бассейн из строк? Бассейн для строк? Ну да, что-то вроде этого. \\ \hline
|
||||
|
||||
05-интернирование-строк & Экземпляр класса String хранится в памяти, именуемой куча (heap), но есть некоторые нюансы. Если строка, созданная при помощи конструктора хранится непосредственно в куче, то строка, созданная как строковый литерал, уже хранится в специальном месте кучи — в так называемом пуле строк (string pool). В нем сохраняются исключительно уникальные значения строковых литералов, а не все строки подряд. Процесс помещения строк в пул называется интернирование (от англ. interning, внедрение, интернирование). Когда мы объявляем переменную типа String и присваиваем ей строковый литерал, то JVM обращается в пул строк и ищет там такое же значение. Если пул содержит необходимое значение, то компилятор просто возвращает ссылку на соответствующий адрес строки без выделения дополнительной памяти. Если значение не найдено, то новая строка будет интернирована, а ссылка на нее возвращена и присвоена переменной. \\ \hline
|
||||
|
||||
05-пул-и-конкатенация & В строке «Best» + «Cat» создаются два строковых объекта со значениями «Best» и «Cat», которые помещаются в пул. «Склеенные» строки образуют еще одну строку со значением «BestCat», ссылка на которую берется из пула строк (а не создается заново), т.к. она была интернирована в него ранее. Значения всех строковых литералов из данного примера известно на этапе компиляции. А вот если предварительно поместить один из фрагментов строки в переменную, мы можем запутать стрингпул и заставить его думать, что в результате получится совсем новая строка. \\ \hline
|
||||
|
||||
05-пул-и-конструктор & Когда мы создаем экземпляр класса String с помощью оператора new, компилятор размещает строки в куче. При этом каждая строка, созданная таким способом, помещается в кучу (и имеет свою ссылку), даже если такое же значение уже есть в куче или в пуле строк. Таким образом, создав четыре одинаковых строки, в памяти зафиксируются только три объекта. Согласитесь, что это нерационально. В Java существует возможность вручную выполнить интернирование строки в пул путем вызова метода intern() у объекта типа String. Принимая во внимание всё вышесказанное, вы можете спросить: «Почему бы все строки сразу после их создания не добавлять в пул строк? Ведь это приведет к экономии памяти». Да, среди достаточно большого количества программистов такое заблуждение присутствует. Именно заблуждение, поскольку не все учитывают дополнительные затраты виртуальной машины на процесс интернирования, а также падение производительности, связанное с аппаратными ограничениями памяти, ведь невозможно читать ячейку памяти одновременно бесконечным числом процессов, электроника ещё до этого не доросла. Можно сказать, что интернирование (в виде применения метода intern()) рекомендуется вообще не использовать. Вместо интернирования необходимо использовать дедупликацию, но подробный разговор о ней мы отложим на некоторое время, поскольку мы пока что не изучили много всяких вспомогательных шестерёнок. Если коротко, во время сборки мусора гарбич коллектор проверяет живые (имеющие рабочие ссылки) объекты в куче на возможность провести их дедупликацию. Ссылки на подходящие объекты вставляются в очередь для последующей обработки. Далее происходит попытка дедупликации каждого объекта String из очереди, а затем удаление из нее ссылок на объекты, на которые они ссылаются. Также для отслеживания всех уникальных массивов байт, используемых объектами String, используется хеш-таблица. При дедупликации в этой хеш-таблице выполняется поиск идентичных массивов байт.\\ \hline
|
||||
|
||||
На этом уроке мы & Поговорили о файловых системах и представлении данных в запоминающих устройствах; Поверхностно посмотрели на популярные пакеты ввода-вывода. Подробно разобрали один из самых популярных ссылочных типов данных String и разные механики вокруг него, такие как стрингбилдер и стрингпул.\\ \hline
|
||||
|
||||
практическое задание & В качестве практического задания предлагаю
|
||||
\begin{itemize}
|
||||
\item создать пару-тройку текстовых файлов. Для упрощения (не разбираться с кодировками) внутри файлов следует писать текст только латинскими буквами.
|
||||
\item написать метод, осуществляющий конкатенацию переданных ей в качестве параметров файлов (не особо важно, в первый допишется второй или во второй первый, или файлы вовсе объединятся в какой-то третий);
|
||||
\item написать метод поиска слова внутри файла.
|
||||
\end{itemize} \\ \hline
|
||||
|
||||
Учитесь так, словно вы постоянно ощущаете нехватку своих знаний, и так, словно вы постоянно боитесь растерять свои знания. Конфуций & Надеюсь, на этой лекции вы не уснули от объёма теоретической информации, потому что я наоборот постарался дать как можно больше практических особенностей, которые кажутся естественными и о них особенно не задумываешься. Всего доброго и до новых встреч. \\ \hline
|
||||
\end{longtable}
|
||||
|
||||
\end{document}
|
||||
|
||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% РАССМОТРЕТЬ ПОЗЖЕ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
%Вместо интернирования необходимо использовать дедупликацию, но подробный разговор о ней мы отложим на некоторое время (рассматривается далее В КАЧЕСТВЕ ТЕОРЕТИЧЕСКОГО ОЗНАКОМЛЕНИЯ, ТАК КАК ПОТОКИ И REFLACTION API ЕЩЁ НЕ ПРОХОДИЛИ).
|
||||
% Немного о Java 9
|
||||
% До Java 8 строка внутренне представлялась, как массив символов char[] в кодировке UTF-16, каждый из которых занимал по два байта в памяти.
|
||||
% В Java 9 было внедрено новое представление для типа String, получившее название компактные строки (Compact Strings). Благодаря новому формату хранения строк (в зависимости от контента) делается выбор между массивом символов char[] и массивом байт byte[].
|
||||
% Поскольку новый способ хранения объектов типа String использует кодировку UTF-16 лишь в том случае, когда в этом есть необходимость, объем памяти, занимаемый пулом строк в куче, будет значительно ниже, что в, свою очередь, уменьшит издержки работы сборщика мусора.
|
||||
% Ключевые моменты:
|
||||
|
||||
|
||||
% Строки в Java представляют собой константы, которые не могут быть изменены
|
||||
|
||||
|
||||
% Создать объект класса String можно двумя способами: при помощи строкового литерала и конструктора
|
||||
|
||||
|
||||
% Строковый литерал сохраняется в пул строк, если до этого он там отсутствовал
|
||||
|
||||
|
||||
% Строка, созданная при помощи конструктора, сохраняется в heap, а не в пул строк
|
||||
|
||||
|
||||
% Java 6: Пул строк хранится в памяти фиксированного размера, именуемого PermGen.
|
||||
|
||||
|
||||
% Java 7, 8: Пул строк хранится в heap и, соответственно, для пула строк можно использовать всю память приложения
|
||||
|
||||
|
||||
% При помощи параметра -XX:StringTableSize=N, где N — размер HashMap, можно изменить размер пула строк. Его размер является фиксированным, поскольку он реализован, как HashMap со списками в корзинах
|
||||
|
||||
|
||||
% Инженеры по оптимизации Java компании Oracle настоятельно не рекомендуют самостоятельно интернировать строки, поскольку это приводит к замедлению работы приложения. Их рекомендация — дедупликация.
|
||||
|
||||
|
||||
% Дедупликация
|
||||
% Как мы написали в самом начале, класс String представляет собой массив байт:
|
||||
|
||||
% private final byte[] value;
|
||||
|
||||
|
||||
% А т.к. созданный экземпляр класса String нельзя модифицировать, т. е. содержимое массива value[] нельзя изменить_,_ то его значение может быть безопасно использовано одновременно несколькими объектами String.
|
||||
% Дедупликация представляет собой не что иное, как переприсваивание виртуальной машиной адресов поля value. Т. е. мы выполняем дедупликацию не объектов String, а массивов их байт. Поля value нескольких объектов типа String с одинаковым значением текста изначально ссылаются на разные участки памяти (разные массивы байт), а после дедупликации будут ссылаться на один и тот же участок памяти, содержащий массив байт.
|
||||
% Кроме того, у нас все еще остаются накладные расходы в виде заголовка объекта, полей и др. Такие накладные расходы зависят от платформы/конфигурации и варьируются в пределах от 24 до 32 байт. Однако, для средней длины объекта String в 45 символов (90 байт + заголовок массива), это все еще значительные цифры. Принимая во внимание вышеперечисленное, актуальный выигрыш в экономии памяти может быть около 10%.
|
||||
% Как работает дедупликация
|
||||
% Во время сборки мусора GC проверяет живые (имеющие рабочие ссылки) объекты в куче на возможность провести их дедупликацию. Ссылки на подходящие объекты вставляются в очередь для последующей обработки. Далее происходит попытка дедупликации каждого объекта String из очереди, а затем удаление из нее ссылок на объекты, на которые они ссылаются. Также для отслеживания всех уникальных массивов байт, используемых объектами String, используется хеш-таблица. При дедупликации в этой хеш-таблице выполняется поиск идентичных массивов байт (символов).
|
||||
% При положительном результате значение поля value объекта String переприсваивается так, чтобы указывать на этот существующий массив байт. Соответственно, предыдущий массив байт value становится ненужным — на него ничего не ссылается и впоследствии он попадает под сборку мусора.
|
||||
% При отрицательном результате, массив байт, соответствующий value, вставляется в хеш-таблицу, чтобы впоследствии быть использованным совместно с новым объектом String в какой-то другой момент в будущем.
|
||||
% Поэкспериментируем, запустив следующую программу:
|
||||
|
||||
% package ru.gb.jcore;
|
||||
|
||||
% import java.lang.reflect.Field;
|
||||
|
||||
% public class Main {
|
||||
|
||||
% public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {
|
||||
|
||||
% char[] chars = {'B', 'e', 's', 't', 'C', 'a', 't'};
|
||||
%
|
||||
% String[] strings = {
|
||||
% new String(chars),
|
||||
% new String(chars)
|
||||
% };
|
||||
%
|
||||
% Field value = String.class.getDeclaredField("value");
|
||||
% value.setAccessible(true);
|
||||
|
||||
% System.out.println("Hash of the first object: " + value.get(strings[0]));
|
||||
% System.out.println("Cache of the second object: " + value.get(strings[1]));
|
||||
|
||||
% System.gc();
|
||||
% System.out.println("Started the garbage collector");
|
||||
|
||||
% Thread.sleep(100);
|
||||
|
||||
% System.out.println("Hash of the first object: " + value.get(strings[0]));
|
||||
% System.out.println("Cache of the second object: " + value.get(strings[1]));
|
||||
% }
|
||||
% }
|
||||
|
||||
|
||||
% Вывод:
|
||||
|
||||
% Hash of the first object: [C@726f3b58
|
||||
% Cache of the second object: [C@442d9b6e
|
||||
% Started the garbage collector
|
||||
% Hash of the first object: [C@726f3b58
|
||||
% Cache of the second object: [C@442d9b6e
|
||||
|
||||
|
||||
% Как видим, дедупликация не сработала. Для ее активации необходимо в параметрах виртуальной машины указать -XX:+UseStringDeduplication, а также активировать сборщик мусора G1 (если он не используется по умолчанию), указав также -XX:+UseG1GC.
|
||||
% Вывод:
|
||||
|
||||
% Hash of the first object: [C@726f3b58
|
||||
% Cache of the second object: [C@442d9b6e
|
||||
% Started the garbage collector
|
||||
% Hash of the first object: [C@442d9b6e
|
||||
% Cache of the second object: [C@442d9b6e
|
||||
|
||||
|
||||
% Результат говорит о следующем: создав два объекта с помощью new, мы получили два разных объекта с разными идентификационными хешами для массивов байт. Запустив сборщик мусора и подождав некоторое время (дедупликация не происходит мгновенно), мы видим, что хеши для двух объектов стали одинаковы (ссылаются на один и тот же массив).
|
||||
% Иллюстративно это выглядит так до дедупликации:
|
||||
|
||||
% После дедупликации:
|
||||
|
||||
% Ключевые моменты:
|
||||
|
||||
% Дедупликация строк доступна с Java 8 Update 20
|
||||
|
||||
% Она активируется параметром для виртуальной машины: -XX:+UseStringDeduplication
|
||||
|
||||
% Дедупликация строк работает только со сборщиком мусора G1. Для его активации в Java 8 необходимо указать параметр для виртуальной машины -XX:+UseG1GC. Начиная с Java 9, G1 является сборщиком мусора по умолчанию
|
||||
% Опыты показывают, что применение дедупликации строк сокращает расходы кучи на примерно 10%, что, в принципе, неплохо, учитывая, что нам не нужно вносить изменение в код
|
||||
% Дедупликация строк работает в фоновом режиме без приостановления работы приложения
|
||||
% В отличие от пула строк, который применим только для строк, интернированных командой intern(), или строковых литералов, но не применим для строк, созданных динамически во время жизни приложения, дедупликация строк применима для строк, созданных всеми этими способами
|
||||
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
\documentclass[../j-spec.tex]{subfiles}
|
||||
|
||||
\begin{document}
|
||||
\section{Интерфейсы}
|
||||
\begin{longtable}{|p{35mm}|p{135mm}|}
|
||||
\hline
|
||||
Экран & Слова \\ \hline
|
||||
\endhead
|
||||
|
||||
Титул & Здравствуйте, добро пожаловать на курс посвящённый инструментарию разработки джава \\ \hline
|
||||
|
||||
Отбивка & и сегодня на повестке дня у нас интерфейсы, но не те интерфейсы, что графические, а те, что программные. \\ \hline
|
||||
|
||||
На прошлом уроке & На прошлом уроке мы поговорили о файловых системах и представлении данных в запоминающих устройствах; Поверхностно посмотрели на популярные пакеты ввода-вывода. Подробно разобрали один из самых популярных ссылочных типов данных String и разные механики вокруг него, такие как стрингбилдер и стрингпул. \\ \hline
|
||||
|
||||
На этом уроке & Поговорим об интерфейсах, рассмотрим понятие и принцип работы, поговорим о ключевом слове implements; Наследование и множественное наследование интерфейсов, реализация, значения по умолчанию и частичная реализация интерфейсов. Естественно, что говоря об интерфейсах нельзя не сказать о функциональных интерфейсах и анонимных классах. Коротко рассмотрим модификаторы доступа при использовании интерфейсов и немного подробнее поговорим о глубинных процессах, таких как раннее и позднее связывание в объектно-ориентированном программировании. \\ \hline
|
||||
|
||||
06-01 & Разговор об интерфейсах хотелось бы построить не так, как мы это делали на лекциях обычно, а от некоторой практической составляющей, чтобы где-то в середине лекции у вас сложилось явное впечатление, что что-то тут явно можно улучшить, а когда мы применим интерфейсы, вы подумали «ааааа так вот зачем оно нужно и как применяется», а потом уже поговорим о теоретической составляющей и особенностях. \\ \hline
|
||||
|
||||
Отбивка & Приложение для примера \\ \hline
|
||||
|
||||
06-02 & Итак начнём с преамбулы, здесь я призываю вас не особенно обращать внимание на то, какие именно классы и методы используются, а внимательно следить за взаимодействием и отношениями объектов, потому что интерфейсы, о которых мы сегодня планируем поговорить - это как раз механизм упрощающий и универсализирующий взаимодействия объектов. Код может показаться непростым, но зато задачу мы поставим таким образом, что если делать нормально, то без интерфейсов не обойтись, и когда мы закончим - у вас, как я только что и сказал, должно появиться ощущение что «о, так вот зачем они нужны». Почему будет сложно? потому что несмотря на то что вы уже знаете принципы ооп - надо уметь их применять.\\ \hline
|
||||
|
||||
06-03 & Итак, что мы будем делать? мы сделаем некий небольшой набросок своего собственного игрового 2д движка, без физики и прочего, а просто с объектами и анимацией, чисто демонстрационный. на его основе можно что угодно запилить, и будет отлично работать. На слайде вы видите окно, кружки летают (имейте ввиду, они перемещаются плавно, если у кого-то что-то дёргается, это интернет с низким ФПС передаёт, а не приложение). пока что ничего тут не обрабатывается, ничего толком не происходит. Итак, самое главное, что нам понадобится, это окно, которое будет как-то взаимодействовать с операционной системой, на окне будет канва, на которой мы будем всё рисовать, и собственно объекты, которые мы будем рисовать. \\ \hline
|
||||
|
||||
06-04 & Быстро создаём окно, буквально константы с размерами, координатами, и конструктор окна прямо здесь же вместе с методом мейн, не вдаваясь в подробности того, как работает фреймворк свинг на котором мы всё это пишем, на эту тему у нас будет отдельный урок, на котором мы закрепим информацию об ООП, многопоточности и отлову исключений в графических интерфейсах. Самое важное для нас сейчас - это то, что окно - это объект с какими-то свойствами и каким-то поведением. Если коротко, стартуем программу, создаём объект окна, говорим, что окно с каким-то заголовком, какого-то размера и находится по каким-то координатам, а когда мы нажмём на крестик, то закрыть нужно будет не только окно, но и программу целиком. \\ \hline
|
||||
|
||||
06-05 & Итак есть такой компонент JPanel, с ним можно много что делать но самое интересное, что можно на нём рисовать. Итак создали наследника Jpanel с названием GameCanvas. Что умеет любой компонент в свинге? он умеет перерисовываться, посредством вызова метода paintComponent. Применим полиморфизм. Из документации мы знаем, что метод пэинтКомпонент вызывается тогда, когда фреймворку надо перерисовать панельку. Мы этот метод переопределим и напишем свою собственную реализацию перерисовки. Давайте сделаем так: создадим конструктор этой пенльки, и будем в конструкторе делать что то незначительное, например, менять цвет фона на синий. Заоверрайдили пэинтКомпонент и тут-же вызвали родительский метод. То есть по сути мы говорим, что нас вполне устраивает то, как перерисовывается панелька, но мы потом захотим туда что-то добавить. Ну и добавим четыре метода, возвращающие границы нашей канвы, левую, правую, верхнюю, нижнюю.\\ \hline
|
||||
|
||||
06-06 & Наладим взаимодействие компонентов и привяжем все действия нашего игрушечного движка ко времени физического мира. В нашем основном окошке, в конструкторе, создали переменную класса GameCanvas и расположили её на окне прям в центре. Пока что всё понятно и хорошо. Всё работает. В принципе можно начать писать логику игры прям здесь, в классе панельки, но это архитектурно не очень хорошо, ведь это же канва на которой мы рисуем, значит здесь мы должны по логике только рисованием заниматься. Давайте примем архитектурное решение писать логику игры в нашем классе с кружками, а GameCanvas будет универсальным, чтобы рисовать вообще всё что угодно. Для этого описали в нашем основном окошке метод, назвали его как-нибудь нормально onDrawFrame и в нём будем описывать реализацию цикла для нашего модного приложения, то есть так называемую бизнес-логику. На данный момент это будут два метода - апдейт который будет как то изменять состояние нашего приложения, и рендер, который будет отдавать команды всяким рисующим компонентам. \\ \hline
|
||||
|
||||
06-07 & Теперь нам надо чтобы при перерисовке наш с вами GameCanvas этот метод дёргал, и тем самым изображение как-то менялось. Для этого нашей канве надо знать как минимум чей метод она будет дёргать, то есть ей нужно знать на каком окне она находится. создаём в классе канвы локальную переменную, которая умеет хранить объекты класса MainCircles и передадим значение этой переменной в конструкторе. И в пэинтКомпоненте будем вызывать controller.onDrawFrame(); Далее, чтобы зациклить это действие мы можем пойти несколькими путями: самый простой - создать постоянно обновляющуюся канву, то есть в методе пэинтКомпонент взять и написать repaint() но это вариант прямо скажем "так себе", он полностью нагрузит одно из ядер процессора только отрисовкой окна - не самое лучшее применение одного из ядер. Второй путь - это применение магии из занятия по потокам. мы можем заставить наш поток какое-то время поспать, начиная с 20-й строки вы видите некоторую конструкцию, смысл которой вам будет понятен немного позже Thread.sleep(16); это даст нам фпс близкий к 60, приемлемо для условной игры, не надо ни больше ни меньше, пожалуй. Получится, что мы отрисовали компонент, посчитали что там изменится апдейтом, отправили обратно на отрисовку рендером, отрисовали пэинтКомпонентом.\\ \hline
|
||||
|
||||
06-08 & Довольно интересно, кстати, как при этом изменился код, вызывающий конструктор канвы, ведь мы из основного класса с окном теперь как-то должны в канву передать это самое основное окно, как вы могли догадаться, для этой цели мы применяем указатель на текущий объект класса this, то есть в конструкторе основного окна мы передали ссылку на экземпляр этого окна канве. немного ломает привычное использование для обращения к полям в конструкторе, но, поверьте, то ли ещё будет. Предлагаю запомнить такой способ применения ключевого слова this, он нам сегодня ещё пригодится. \\ \hline
|
||||
|
||||
06-09 & Закончим с отрисовкой и методом onDrawFrame. Он будет обновлять сцену и рендерить её. Для обновления сцены было бы очень неплохо знать дельту времени, которая прошла с предыдущего кадра, чтобы обновлять физику. Конечно можно писать физику, опираясь на частоту кадра, или на то что мы там спим 16миллисекунд, но это всё очень сомнительная опора, потому что мы гарантированно спим 16миллисекунд, но сколько именно мы будем спать неизвестно, потому что отрисовка происходит не через фиксированные промежутки времени и ещё куча факторов. Лучше всего точно знать сколько времени прошло с предыдущего кадра. поэтому сделаем так чтобы метод onDrawFrame эту самую дельту у канвы получал и отдавал методу обновления. Соответственно считаем дельту в канве. Вот, собственно и вся физика, которая нам понадобится. Теперь посмотрим вот на что - у нас в пэинтКомпонент прилетает объект класса графикс, который отвечает, как это ни удивительно, за графику, то есть за рисование. Это же то, что нам нужно. Давайте этот самый объект отдадим нашему методу onDrawFrame, пусть он что-нибудь рисует. а где рисует? на канве, значит нам надо будет как минимум знать её размеры. какой канвы размеры получаем? нашей, той самой. себя и отдадим. onDrawFrame будет распределять - апдейту отдаст дельту и канву, а рендеру - канву и графику. Для универсальности. Все изменения канвы вы видите на слайде, все изменения основного окна ему соответствуют, изменена сигнатура метода онДроФрейм, на неё мы посмотрим через несколько секунд. Пока самое главное, что нам нужно понять об этих двух объектах - канва считает для нас время и постоянно перерисовывает себя, сообщая об этом факте основному окну, а основное окно на этот факт как-то реагирует. Ну и ООП вокруг этого тоже было бы хорошо понимать, объекты передают ссылки друг на друга и вызывают друг у друга всякие интересные методы. \\ \hline
|
||||
|
||||
06-10 & Давайте попробуем что нибудь нарисовать при помощи нашего с вами игрового цикла, например прямую белую линию. Видим, что отлично получается, теперь мы умеем рисовать и наша канва умеет это отображать. Отлично. Наше приложение будет рисовать какие-то объекты, будут это кружки, квадратики, картинки, человечки или какие-то другие объекты - не важно. Важно, чтобы у программы было описан механизм и поведение этих объектов. Это как раз то, о чём я говорил и говорю - применение архитектуры, применение ООП. Мало просто посмотреть уроки, почитать книжки, посидеть на семинаре. Надо сидеть и думать в рамках парадигмы ООП. Это ещё простенькая архитектура. Я понимаю вас, вы смотрите сейчас, и вроде всё понятно, а сами в жизни бы такое не написали. Я сам несколько лет назад такое в жизни не написал бы, так что не переживайте, всё придёт с опытом. Главное, не путайте понятия уметь программировать и знать язык программирования. В какой-то момент вы поймаете себя на мысли, что вы не думаете о циклах и условиях, а думаете на следующем уровне абстракции, думаете о взаимосвязях, об архитектуре, а пальцы сами набирают какие-то языковые конструкции. \\ \hline
|
||||
|
||||
06-11 & Соответственно, рисовать просто линии круги и прочее - довольно скучно, поэтому будем рисовать объекты, Создадим класс Спрайт. Ни от чего наследоваться не будем. Просто опишем общее для всех рисуемых объектов в нашей программе поведение. Что может быть у всех объектов в приложении общего? размеры, местоположение. Обычно, когда вы начинаете изучать какой-то графический фреймворк вы замечаете, что начало координат у этого фреймворка находится в верхнем левом или нижнем левом углу. Однако очень часто, когда пишутся какие-то игры или другие приложения с использованием графики в качестве координат используется центр объекта. То есть надо условиться - что х и у - это центр любого визуального объекта на нашей канве. И соответственно удобно хранить не длину-ширину, а половину длины и половину ширины. А границы объекта соответственно будем отдавать через геттеры и сеттеры. Дополнительно научим наш спрайт рисоваться. Ну как научим, просто скажем, что он умеет обновляться и рендериться, а его наследники пусть уже решают, как именно они хотят это делать. Но спрайты лишены логики, они ничего не знают о том, как именно будут меняться их свойства. \\ \hline
|
||||
|
||||
06-12 & Поэтому естественно нужно создать класс собственно шарика, который будет по нашему экрану прыгать, а то непорядок какой-то. В конструкторе задаём ему рэндомные размеры. Давайте придумаем ему какие-нибудь глобальные свойства. Можно конечно придумать класс, который будет направлять наш спрайт и вообще задавать ему скорость и прочие физические величины, но мы ж с вами пример пишем, так что придумаем ему просто - скорость по осям х и у соответственно и цвет. для цвета есть совершенно неожиданно называющийся класс Color которому можно задать величины в формате РГБ в диапазоне от 0 до 255. Для шарика переопределяем апдейт и рендер. суперы нам не нужны, они всё равно пустые. Самый простой рендер - мы объекту графики зададим цвет текущего шарика и сделаем fillOval, которому передадим лево, верх, ширину и высоту. Несмотря на то что наши объекты содержат поля типа флоут, мы работаем с пиксельной системой координат (что конечно же не подходит для реальных проектов, там нужно всё сразу переводить в мировые координаты (например принять центр экрана за 0, верх и лево за -1 низ и право за 1, как это делает OpenGL) чтобы рендерить экраны). Но это нам и пяти лекций не хватит чтобы вникнуть так что не будем. А в методе апдейт мы просто прибавляем к нашим текущим координатам нашу скорость, умноженную на дельту времени, то есть как в третьем классе. Расстояние, которое должен был преодолеть наш шарик за то время пока наш поток спал и наша канва рендерилась. ну и обрабатываем отскоки, то есть описываем 4 условия, что при достижении границы мы меняем направление вектора. \\ \hline
|
||||
|
||||
06-13 & давайте быстренько допилим основной класс и будем переходить к беседе об интерфейсах уже, а то чувствую вы подустали от вступлений. В основном классе мы делаем очень прямолинейно - создаём классовый массив из спрайтов и называться он будет sprites и мы говорим что будет у нас допустим 1 кружчек. В методе апдейт мы пробежимся по всем спрайтам и скажем каждому из них - апдейться так как ты умеешь. В методе рендер мы сделаем тоже самое - пробежимся по всем спрайтам и скажем - отрисуйся так, как ты хочешь. И всё, реализацию обновления и отрисовки мы оставили самим объектам, то есть инкапсулировали в них, только каждый объект сам по себе знает, как именно ему обновляться с течением времени, и как рисоваться, а основной экран уже управляет - на какой канве, когда и кого рисовать. Итак инициализировали наше приложение одним новым шариком и напишем небольшую магию по добавлению новых шариков в динамически расширяемый массив. Как известно, в джава все массивы имеют неизменяемую размерность. Это накладывает некоторые архитектурные ограничения на программиста, но мы ж с вами крутые программисты, вон сколько лекций и семинаров позади, можем обмануть систему. Код не сложный, когда старый массив заполнился, просто увеличиваем его в два раза и переносим шарики из старого в новый. \\ \hline
|
||||
|
||||
06-14 & И осталось эти методы откуда-то вызывать. Мы на нашу канву в конструкторе вешаем слушателя мышки, который нам рассказывает когда произоши какие-то события. Сегодня как раз будем анонимные классы проходить, поговорим об этом явлении подробнее. И по этому произошедшему событию мы говорим - добавь нам спрайт с новым мячиком, у которого будут координаты этого события. Для этого конечно пришлось немного модифицировать класс шарика, добавив в него ещё один конструктор, который сразу задаёт начальные координаты. На правую кнопку мышки повесим удаление спрайта из списков обновления и отрисовки. \\ \hline
|
||||
|
||||
06-15 & Получается, мы меньше чем за полчаса написали довольно простое и очень хорошо расширяемое приложение, которое не только рисует вещи, но и на действия пользователя реагирует, кажется, неплохо. Напомню, что самое главное, что мы должны из этого приложения извлечь - это взаимодействия и взаимовлияния объектов. Наследование, полиморфизм, инкапсуляция поведений и свойств. Если честно, я слегка устал от синего цвета нашего фона и решил создать отдельный класс фона, но сразу столкнулся с необходимостью думать головой. Логично было бы предположить, что фон - это спрайт, имеющий прямоугольную форму и всегда рисующийся первым. но вот беда, при изменении размеров окна фон тоже желательно изменить в размерах. Поэтому в отрисовке я просто говорю канве, что она должна изменить свой цвет фона, а что, ссылка то на канву у меня есть. Цвет фона я меняю синусоидально по каждому из трёх компонент цвета, поэтому изменение происходит довольно плавно. В общем, получается, что от спрайта нам фактически нужно только поведение, а свойства не нужны. Но и отказаться от наследования нам бы не хотелось, потому что тогда мы не сможем фон единообразно в составе массива спрайтов обновлять. Это наталкивает нас на мысль об унификации поведения, на мысль об интерфейсе. \\ \hline
|
||||
|
||||
Отбивка Понятие интерфейса & Механизм наследования очень удобен, но он имеет свои ограничения. В частности мы можем наследовать только от одного класса, в отличие, например, от языка С++, где имеется множественное наследование \\ \hline
|
||||
|
||||
фото руля-педалей-коробки, фото юсб, фото клавиатуры-мыки-монитора & В языке Java эту проблему частично позволяют решить интерфейсы. Интерфейсы определяют некоторый функционал, не имеющий конкретной реализации, который затем реализуют классы, применяющие эти интерфейсы. И один класс может реализовать множество интерфейсов. Если сказать проще, интерфейс можно очень-очень грубо представить как очень-очень абстрактный класс. До седьмой джавы это был просто набор методов без реализации. Начиная с восьмой наделали много тонкостей, с ними будем разбираться. Итак интерфейс - это описание методов. Примером интерфейса в реальной жизни может быть интерфейс управления автомобилем, интерфейс взаимодействия с компьютером или даже интерфейс USB, так, компьютеру не важно, что именно находится по ту сторону провода, флешка, веб-камера или мобильный телефон, а важно, что компьютер умеет работать с интерфейсом USB, отправлять туда байты или получать. Потоки ввода-вывода, которые мы проходили чуть раньше - это тоже своего рода интерфейс, соединяющий не важно какой программный код и не важно какой, например, файл. Все методы во всех интерфейсах всегда публичные, и в классическом варианте не имеют реализации. Ну и поскольку все методы всегда паблик то этот модификатор принято просто не писать. Для новичка это неочевидно и сбивает с толку, может показаться что модификатор дефолтный, а на самом деле он публичный, поблагодарим разработчиков джавы и пойдём дальше. \\ \hline
|
||||
|
||||
& \\ \hline
|
||||
& \\ \hline
|
||||
& \\ \hline
|
||||
& \\ \hline
|
||||
& \\ \hline
|
||||
& \\ \hline
|
||||
& \\ \hline
|
||||
& \\ \hline
|
||||
& \\ \hline
|
||||
|
||||
\end{longtable}
|
||||
\end{document}
|
||||
|
||||
****** Для чего же?
|
||||
Например, создадим пару классов, классА и классБ. КлассА будет у нас реализовывать интерфейсА. То есть множественного наследования нет, но мы можем реализовать сколько угодно интерфейсов. Для того, чтобы реализовать интерфейс - мы должны переопределить все его методы, либо сделать класс абстрактным. Вот, статический анализатор кода в идее нам об этом явно говорит. Выведем модное сообщение о том что это класс1 или класс2.
|
||||
И теперь самая соль - мы можем в мэйне объявлять не только классы и создавать объекты, но и создать переменную, которая реализовывает интерфейсА. То есть тут могут лежать абсолютно никак не связанные между собой сущности, главное, чтобы они реализовывали интерфейсА. И мы можем работать с методами интерфейса, которые могут быть для разных классов вообще по-разному реализованы. Такой вот полиморфизм. Понимаете насколько сильно это отличается от наследования, когда мы с вами создавали общий абстрактный класс животное и от него наследовали наших котиков?
|
||||
****** Также важно
|
||||
Что в интерфейсах также разрешено наследование. То есть у нас интерфейс может наследоваться от другого интерфейса, соответственно при реализации интерфейса мы должны переопределить методы всех родителей нашего интерфейса, то есть тут картина очень похожа на наследование классов
|
||||
****** Вернёмся к бэкграунду
|
||||
Он наследуется от спрайта, но ему от спрайта вообще ничего не надо, кроме двух методов. в которых он полностью пишет собственную реализацию. И вот сложилась ситуация в которой нам надо хранить в одном массиве очень похожие объекты но наследовать их друг от друга не логично. Как поправить?
|
||||
****** Напишем некий интерфейс, назовём его GameObject
|
||||
и скажем, что у него есть методы апдейт и рендер, без реализации. То есть это будут некие объекты которые должны уметь рисоваться и апдейтиться. Идём в спрайт, и говорим, что мы реализуем интерфейс гейм объекта. Смотрим, что сломалось, кто навскидку скажет, почему? (модификатор должен быть пабликом). И Фон тоже теперь у нас реализует интерфейс гейм объекта. При этом получается, то фон вообще никак не связан со спрайтом, у них даже набор полей разный. Но оба умеют рисоваться и апдейтиться, благодаря интерфейсу.
|
||||
****** Теперь идём править основное окно и логику
|
||||
Здесь всё просто, мы массив спрайтов меняем на массив геймОбъектов и пробуем запустить. Вопросы, оценка сложности.
|
||||
***** Интерфейсы
|
||||
****** Давайте посмотрим со стороны
|
||||
что мы тут понаписали. У наших классов канвы и спрайта, а также у интерфейса нет никакой специфики, их можно применить где угодно. Универсальные получились штуковины.
|
||||
****** создадим какую-то условную вторую игру
|
||||
новый пакет, новый класс, скопипастим немного кода, чтобы всё запускалось. И не писать же нам по новой спрайты и интерфейсы. сделаем правильное дробление по пакетам. то есть получается, что у нас есть некий библиотечный пакет, и какие-то игры с конкретными реализациями. создадим паке КОММОН и переносим туда канву, спрайт и геймобъект, и ща будем всё чинить.
|
||||
Структура понятна?
|
||||
Что не нравится канве? ондрофрейм нужно паблик
|
||||
Что не нравится мэйн виндоу? гейм канвас надо сделать паблик
|
||||
Гейм объект это интерфейс, ему вообще на всё плевать, только импорты лишние выкинем
|
||||
Спрайт тоже не сломался
|
||||
Идём в шарик он не видит спрайта, сделаем спрайт пабликом
|
||||
он же не видет поля и методы, сделаем их протектед
|
||||
он же не видит методы канвы. сделаем их публичными
|
||||
И идём в мэйн, что тут? он не видит конструктор канвы. делаем пабликом
|
||||
****** второе окно также инитим
|
||||
конструктор, размеры, положение. И вот хотим воспользоваться нашей канвой. Но не даёт же. почему, мы ж так классно всё придумали, никакой специфики. Но нет, канва то может принимать в конструкторе mainCircles. и канва уже у этого класса вызывает метод onDrawFrame(). Как это решается? это решается интерфейсом. нам нам надо написать какой-то интерфейс вроде canvasPaintListener, который будет уметь ждать от канвы вызов метода и как-то по своему его реализовывать, и собственно реализовать этот интерфейс. А то у нас получается катастрофа - игры зависят от общего пакета, а общий пакет вообще ничего не должен знать о том, какие игры он помогает создать.
|
||||
Создаём в общем пакете интерфейс с одним методом, который имеет сигнатуру из нашего mainCircles. И перепишем канву, чтобы она не какой-то класс с шариками на вход принимала, а canvasPaintListener, назовём переменную листнер. и везде используем листнер.
|
||||
И в главном окне мы говорим что ты хороший класс, с прекрасным наследованием, но только ещё будешь реализовывать интерфейс слушателя канвы. а метод у нас уже правильно написан, с верной сигнатурой. Чтобы подчеркнуть, что это реализация интерфейса, напишем аннотацию оверрайд.
|
||||
Во второй игре мы тоже реализуем этот же интерфейс, и передаём канве себя, потому что мы умеем слушать канву
|
||||
***** Анонимные классы
|
||||
****** Итак все мы знаем
|
||||
что классы это такой новый для нашей программы тип данных. Мы уже привычными движениями создаём публичные классы в отдельный файлах и пишем в них логику, но это не всё, что позволяет нам джава. Классы также бывают вложенными и внутренними. Внутренние классы - это классы, которые пишутся внутри класса, который описан в файле, мы его даже можем использовать и вызывать его методы. А также вложенные или локальные классы, которые мы можем объявлять прямо в методах, и работать с ними, как с обычными классами, но область видимости у них будет только внутри метода.
|
||||
К чему я это веду? к анонимным классам. Давайте переименуем наш интерфейс во что то вроде MouseListener и опишем в нём пару методов, например mouseUp(), mouseDown(). Перейдём в наш основной класс и скажем, что наш локальный класс будет реализовывать интерфейс и значит должен переопределять все его методы. Видим. Теперь мы можем экземпляр этого класса создать и даже попользоваться его методами.
|
||||
****** Пока что ловите мысль?
|
||||
Очень часто, какието элементы управления (кнопчки, события входящих датчиков (клавиатура, мышка) требуют на вход каких нибудь слушателей, которые будут отлавливать их события и вообще знать что делать и это всё интерфейсы. Значит если мы туда передадим что-то, что реализует интерфейс, то это нечто уже начнёт ловить события и как-то их обрабатывать. Соответственно весьма часто такие классы создаются без имени, прямо в аргументе методов. Ну и действительно, зачем ему имя, если он будет использован только один раз и только в этом методе? Вот допустим у нас есть переменная MouseListener listener и нам нужно создать туда какой-то экземпляр, который реализует этот интерфейс. для этого есть такой синтаксис:
|
||||
new MouseListener дальше пишем интерфейс, который реализуем
|
||||
{} и у нас по сути открывается объявление класса, где мы пишем реализацию. И получается что мы создаём один экземпляр анонимного класса, который реализует интерфейс MouseListener. И конечно потом мы этот анонимный класс уже кладём в идентификатор (или можем прям вот так передать в метод). Конечно мы можем избежать этого, сейчас поговорим как, но в чужом коде это сплошь и рядом, вот такой замороченный синтаксис, спасибо разработчикам джава.
|
||||
Понятен-ли этот синтаксис? Это создание объекта анонимного класса, никак не называющегося, реализующего интерфейс.
|
||||
Тут можно поговорить о лямбдах, для того чтобы сократить синтаксис мы можем просто убрать то что неизменно для этого анонимного класса - название интерфейса, название метода и класс аргумента.
|
||||
****** Вернёмся к нашим кружочкам
|
||||
Смотрим прям первый метод, invokeLater() он принимает на вход какой-то Runnable. Идём в Runnable и видим, что это интерфейс, который реализует один единственный метод run(). Получается, что мы в invokeLater передаём новый экземпляр анонимного класса который РЕАЛИЗУЕТ ИНТЕРФЕЙС Runnable, вот описание этого класса, мы в нём переопределяем метод run. Вот так это читается. Ровно тоже самое мы сделали с mouseListener. Здесь немного сложнее. Есть интерфейс MouseListener в котором описаны вот эти все методы. И есть специальный класс MouseAdapter в котором все эти методы уже реализованы. Реализации пустые, но они есть, поэтому мы можем не все методы оверрайдить, а только те, которые посчитаем нужными. И когда мы этот MouseAdapter отдаём нашему методу, мы говорим: создай нам новый экземпляр анонимного класса, который НАСЛЕДУЕТСЯ ОТ КЛАССА MouseAdapter и переопредели вот этот метод. Остальные оставляй пустыми. Чувствуете разницу? Как можно избежать таких конструкций и при этом получить понятный код без лишних заморочек? Очень просто. Скажем, что наш класс - реализует тот или иной интерфейс, и переопределим все методы. В некоторые из них даже напишем реализацию, и туда, где требуется интерфейс - передадим себя. Мы же теперь MouseListener, да и Runnable.
|
||||
****** в тему анонимных классов можно ещё коротко сказать о перечислениях
|
||||
в самом частом случае - перечисления это список именованных констант. объявленные в перечислении идентификаторы - это публичные статические финальные поля класса. в более сложных случаях перечисления могут содержать также методы, например, поиска идентификатора по значению, или получения значения из идентификатора
|
|
@ -17,9 +17,11 @@
|
|||
\usepackage{nomencl,imakeidx}
|
||||
\usetikzlibrary{mindmap,trees}
|
||||
\usepackage{booktabs}% http://ctan.org/pkg/booktabs
|
||||
\newcommand{\tabitem}{~~\llap{\textbullet}~~}
|
||||
\usepackage{setspace,fontspec}
|
||||
\usepackage{amsmath}
|
||||
\usepackage{svg}
|
||||
|
||||
\newcommand{\tabitem}{~~\llap{\textbullet}~~}
|
||||
|
||||
\spacing{1.15}
|
||||
\babelfont{rm}{IBM Plex Sans}
|
||||
|
|
Binary file not shown.
|
@ -1,6 +1,6 @@
|
|||
%% Creator: Inkscape 1.1.2 (b8e25be833, 2022-02-05), www.inkscape.org
|
||||
%% Creator: Inkscape 1.2.1 (9c6d41e4, 2022-07-14), www.inkscape.org
|
||||
%% PDF/EPS/PS + LaTeX output extension by Johan Engelen, 2010
|
||||
%% Accompanies image file 'jc-01-jvm-struct.pdf' (pdf, eps, ps)
|
||||
%% Accompanies image file 'jc-01-jvm-struct_svg-tex.pdf' (pdf, eps, ps)
|
||||
%%
|
||||
%% To include the image in your LaTeX document, write
|
||||
%% \input{<filename>.pdf_tex}
|
||||
|
@ -55,18 +55,18 @@
|
|||
\setlength\tabcolsep{0pt}%
|
||||
\put(0.06748601,0.45022737){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}java class file\end{tabular}}}}%
|
||||
\put(0.37724155,0.44898207){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}class loader\end{tabular}}}}%
|
||||
\put(0.09408583,0.28017812){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}method area\end{tabular}}}}%
|
||||
\put(0.09818446,0.28096084){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}method area\end{tabular}}}}%
|
||||
\put(0.31952125,0.28128322){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}heap\end{tabular}}}}%
|
||||
\put(0.46891931,0.28216476){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}stack\end{tabular}}}}%
|
||||
\put(0.61416149,0.28257997){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}java threads\end{tabular}}}}%
|
||||
\put(0.62165019,0.28096084){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}java threads\end{tabular}}}}%
|
||||
\put(0.44202528,0.22326478){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}program counter registers\end{tabular}}}}%
|
||||
\put(0.09198281,0.22045672){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}native internal threads\end{tabular}}}}%
|
||||
\put(0.09818446,0.22009273){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}native internal threads\end{tabular}}}}%
|
||||
\put(0.14223833,0.08731507){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}execution engine\end{tabular}}}}%
|
||||
\put(0.04474887,0.03849335){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}JIT compiler\end{tabular}}}}%
|
||||
\put(0.24633305,0.03931067){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}garbage collector\end{tabular}}}}%
|
||||
\put(0.25035473,0.03748841){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}garbage collector\end{tabular}}}}%
|
||||
\put(0.55476725,0.07504532){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}native method\\interface\end{tabular}}}}%
|
||||
\put(0.79848376,0.07535405){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}native method\\library\end{tabular}}}}%
|
||||
\put(0.32477843,0.33543713){\color[rgb]{0,0,0}\makebox(0,0)[lt]{\lineheight{1.25}\smash{\begin{tabular}[t]{l}runtime data area\end{tabular}}}}%
|
||||
\put(0,0){\includegraphics[width=\unitlength,page=1]{jc-01-jvm-struct.pdf}}%
|
||||
\put(0,0){\includegraphics[width=\unitlength,page=1]{jc-01-jvm-struct_svg-tex.pdf}}%
|
||||
\end{picture}%
|
||||
\endgroup%
|
Loading…
Reference in New Issue