- Платформа Java: Java Development Kit (JDK): состав, назначение. Кроссплатформенность языка Java.
Java Development Kit (JDK) - это набор сервисов, облегчающих процесс написания кода на языке Java. Инструменты JDK нужны для написания кода, его компиляции, отлаживания и запуска. Он не содержит никаких текстовых редакторов, а оперирует только уже существующими Java -файлами.
Пакет JDK состоит из:
- Java Runtime Environment (JRE)
Среда выполнения работает поверх операционной системы компьютера. JDK JRE отвечает за объединение кода Java с библиотеками. Затем она создает экземпляр JVM, в котором выполняется программа.
- интерпретатор (Java)
- компилятор (javac)
Компилятор делает из начального кода байт-версию. Полученный вариант может работать на любой платформе с JVM.Для компилятора работает принцип «напиши один раз, применяй где угодно».
- архиватор (jar)
- автоматическая документация (javadoc)
Кроссплатформенность:
Кроссплатформенность достигается за счет перевода байт-кода в машинный язык при помощи JVM.
JVM сама по себе не является кроссплатформенной технологией.
Для запуска Java-программ в каждой архитектуре и операционной системе должна быть специально разработанная JVM. Однако, поскольку необходимость в JVM считается приоритетной, каждая современная операционная система предоставляет её.
- Java virtual machine (JVM), JIT-компилятор – определение, свойства, функции. Принципы работы сборщика мусора.
JVM (Java Virtual Machine) — это виртуальная машина, которая выполняет байт-код Java-программ. Она обеспечивает работу программ на разных платформах, то есть позволяет запускать Java-приложения на различных устройствах и операционных системах, где установлена эта программа.
Основное назначение JVM — предоставление среды выполнения для программ, а точнее, непосредственно запуск и выполнение байт-кода. Но есть и другие функции и особенности, которые придают JVM тот вид, который она сейчас имеет.
Основные свойства и функции JVM:
- Платформенная независимость: JVM позволяет Java-программам выполняться одинаково на всех платформах, реализуя принцип «WORA» (Write Once, Run Anywhere).
- Управление памятью: JVM автоматически управляет памятью, выделяя необходимое пространство для объектов и освобождая его посредством сборки мусора. Это предотвращает утечки памяти и оптимизирует использование ресурсов.
- Безопасность: JVM выполняет программы в изолированной среде и проверяет байт-код на наличие некорректных или опасных частей, обеспечивая безопасность выполнения.
- Многопоточность: JVM поддерживает многопоточность, позволяя приложениям выполнять несколько потоков одновременно, что способствует эффективному использованию ресурсов и повышению производительности.
Благодаря этим свойствам и функциям, JVM значительно упрощает разработку, освобождая программистов от необходимости вручную управлять памятью, писать отдельные версии программ для каждой операционной системы и беспокоиться о безопасности выполнения кода.
JIT-компилятор (Just-In-Time компилятор) в Java — это компонент виртуальной машины Java (JVM), который динамически преобразует байт-код в машинный код непосредственно во время выполнения программы. Это позволяет существенно повысить производительность Java-приложений по сравнению с традиционной интерпретацией байт-кода.
Основные свойства и функции JIT-компилятора в Java:
- Динамическая компиляция: JIT-компилятор анализирует исполняемый байт-код и компилирует часто используемые участки в машинный код во время выполнения программы. Это позволяет оптимизировать выполнение наиболее критичных частей кода.
- Оптимизация производительности: Путем компиляции часто вызываемых методов и циклов в машинный код, JIT-компилятор снижает накладные расходы на интерпретацию, что приводит к увеличению скорости выполнения программы.
- Адаптивная оптимизация: JIT-компилятор собирает статистику о работе программы в реальном времени, что позволяет ему применять различные уровни оптимизации в зависимости от частоты вызова методов и других факторов. Это обеспечивает баланс между скоростью компиляции и качеством сгенерированного кода.
- Многоуровневая компиляция: Современные JVM используют несколько уровней компиляции (например, компиляторы C1 и C2), которые применяют различные стратегии оптимизации в зависимости от частоты использования методов. Это позволяет эффективно распределять ресурсы компиляции и достигать оптимальной производительности.
- Управление памятью и безопасность: JIT-компилятор генерирует машинный код непосредственно в памяти, что требует особого внимания к безопасности. Память, содержащая исполняемый код, должна быть защищена от несанкционированного доступа и помечена как доступная только для чтения после записи кода.
Сборка мусора — это процесс автоматического управления памятью. Освобождение памяти (путем очистки мусора) выполняется автоматически специальным компонентом JVM — сборщиком мусора (Garbage Collector, GC).
Для сборки мусора используется алгоритм пометок (Mark & Sweep). Этот алгоритм состоит из трех этапов:
- Mark (маркировка). На первом этапе GC сканирует все объекты и помечает живые (объекты, которые все еще используются). На этом шаге выполнение программы приостанавливается. Поэтому этот шаг также называется "Stop the World" .
- Sweep (очистка). На этом шаге освобождается память, занятая объектами, не отмеченными на предыдущем шаге.
- Compact (уплотнение). Объекты, пережившие очистку, перемещаются в единый непрерывный блок памяти. Это уменьшает фрагментацию кучи и позволяет проще и быстрее размещать новые объекты.
- Системы сборки проектов. Фреймворк Apache Maven: определение, структура, Maven Coordinates, POM-файл.
Apache Maven — это инструмент для автоматизации сборки проектов, который в основном используется для проектов на языке Java. Он упрощает процесс управления зависимостями, сборки, тестирования и развертывания приложений. Maven позволяет разработчикам сосредоточиться на написании кода, а не на управлении процессом сборки.
Определение
Maven предоставляет стандартизированный способ описания проекта, его зависимостей и процесса сборки. Он использует концепцию "конвенции над конфигурацией", что позволяет минимизировать количество конфигурационных файлов и настроек.
Структура проекта
- pom.xml: Основной файл конфигурации проекта.
- src/main/java: Исходный код приложения.
- src/main/resources: Ресурсы, используемые приложением (например, файлы конфигурации).
- src/test/java: Исходный код тестов.
- src/test/resources: Ресурсы, используемые в тестах.
Maven Coordinates
Maven Coordinates — это уникальный идентификатор для артефактов (например, библиотек или модулей), который включает в себя следующие элементы:
- groupId: Уникальный идентификатор группы (обычно соответствует доменному имени организации).
- artifactId: Уникальный идентификатор артефакта (имя проекта).
- version: Версия артефакта.
- packaging: Тип пакета (например, jar, war, pom и т.д.).
POM-файл
POM (Project Object Model) — это XML-файл, который содержит информацию о проекте и его конфигурации. Он является основным файлом для Maven и определяет:
- Зависимости проекта.
- Плагины, используемые для сборки.
- Информацию о проекте (название, описание, версия и т.д.).
- Профили сборки, которые позволяют настраивать сборку для различных сред.
- Типы данных Java: простые и ссылочные. Простые (примитивные) типы данных.
Примитивные типы — это данные, имеющие определённый формат и значение. Они применяются для хранения простой информации, например чисел, символов или логических значений. Ссылочные типы данных в Java не содержат значения, а ссылаются на место, где они расположены. Проще говоря, это не сами данные, а ссылки. Сюда входят строки, массивы, классы и интерфейсы.
char - работа с текстом
boolean - бул тип данных
Ссылочные типы данных
Строки. Состоят из символов Unicode, обозначаются классом String и двойными кавычками. Говоря простыми словами, значения внутри них нельзя поменять после присвоения, но можно объявить новое значение для ссылки.
Массив. Это упорядоченная коллекция однотипных объектов с нумерацией. При этом порядковый номер первого элемента всегда ноль. Массивы — изменяемый тип данных, их содержимое можно корректировать после создания, но нет возможности поменять размер.
Класс. Ссылочный тип данных, состоящий из переменных, то есть из данных и свойств, а также методов, описывающих функции и поведение этих переменных. С классами можно проделывать разные манипуляции: включать друг в друга по ссылке или составлять их других путём наследования. Новые классы копируют свойства и методы предков, что даёт возможность переиспользовать готовые классы и добавлять к ним свои уточнения в поведении.
Интерфейс. Интерфейсы — только методы. Говоря простыми словами, этот тип данных в Джава описывает, как элементы программы будут работать друг с другом. Интерфейсы дают разработчикам возможность дополнять классы новым поведением, не изменяя их свойства
- Переменные: статические и нестатические. Местные переменные, область видимости переменных. Объявление и инициализация переменных. Константы. Спецификаторы доступа.
В Java существует три типа переменных:
- Локальные переменные. Объявляются в методах, блоках или конструкторах. Создаются, когда метод, блок или конструктор запускаются, и уничтожаются после завершения. Область видимости локальных переменных ограничивается объявленным методом, блоком либо конструктором.
- Переменные экземпляра. Могут быть объявлены на уровне класса, до или после использования. Являются видимыми для всех методов, конструкторов и блоков в классе.
- Статические переменные (переменные класса). Объявляются с использованием ключевого слова static внутри класса вне какого-либо метода, конструктора или блока. В отличие от переменных экземпляра, у статических может быть только одна копия для каждого класса, независимо от того, сколько объектов создано.
Для объявления переменной в Java используют синтаксис: тип данных переменная [= значение]. Если нужно объявить больше чем одну переменную указанного типа, допускается применение списка с запятыми.
Для инициализации переменной используют оператор присваивания. Слева указывается имя переменной, справа — её значение.
Константы позволяют задать переменные, которые больше не должны меняться. Как правило, при записи имени константы применяют верхний регистр.
Спецификаторы доступа в Java включают:
- Public. Переменные доступны всем классам и объектам программы. Объявляется при помощи ключевого слова public в начале строки.
- Protected. Области видимости переменных ограничены текущим пакетом и подклассами. Объявляется ключевым словом protected.
- Package protected. Модификатор переменных, доступных только внутри пакета. Объявления не требуется, отсутствие ключевых слов указывает на package protected.
- Private. Переменные, доступные исключительно внутри класса. Определяется ключевым словом private.
- Комментарии: виды, особенности применения.
Практически все языки программирования позволяют оставлять в коде комментарии. Они никак не используются кодом и нужны исключительно для людей: чтобы программист оставлял пометки для себя и для других программистов. Java комментарии игнорируются компилятором, т.к. они несут смысл для разработчика, а не для пользователя. Поэтому можно уменьшить размер компилируемых классов.
Комментарии в Java делятся на два типа:
- комментарий реализации (или комментарий кода);
- документирующий комментарий.
Комментарии кода используются для описания отдельных строк/блоков, а комментарии для документирования используются, чтобы описать спецификацию кода (его интерфейс), не зависящую от его реализации.
Комментарии в Java бывают трех видов:
Однострочные комментарии начинаются с //. После этих двух символов может следовать любой текст, вся строка не будет анализироваться и исполняться.
- Может занимать всю строку: // For Winterfell!
- Может находиться на строке после какого-нибудь кода: System.out.println("I am the King"); // => For Lannisters!
Многострочные комментарии начинаются с /* и заканчиваются на */. Принято каждую строку начинать с символа *, хотя технически это и необязательно:
/*
* The night is dark and
* full of terrors.
*/
*Документирующие комментарии начинаются с /** и заканчиваются на */. Уже для них обязательно каждую строку начинать с символа *.
Документирующие комментарии — это подвид многострочных. При этом несут дополнительную функцию — их можно собрать при помощи специальной утилиты javadoc и выдать в качестве документации к вашему коду.
- Операции языка Java – арифметические, отношения и логические, преобразования числовых типов.
Арифметические
Используются для выполнения базовых математических действий
Бинарные арифметические операторы
Если оба операнда – целые числа, то результат также будет целым (результат округляется вниз до ближайшего целого).
Если деление выполняется с участием типа double или float, результат будет дробным.
Унарные арифметические операторы
Операторы инкремента-декремента (https://pics.esputnik.com/repository/home/37116/images/msg/41711174/1559738060892.gif )
Операции сравнения (отношения)
Логические операции
Работают с логическими значениями (true, false) и возвращают результат типа boolean.
Операция отрицания унарная и применяется к одному операнду. Все остальные операции — бинарные
Оператор
Описание
&& ; AND
Логическое И (сокращенный)
|| ; OR
Логическое ИЛИ (сокращенный)
! ; NOT
Логическое НЕ
^ ; XOR
Исключающее ИЛИ
&& и || поддерживают "короткое замыкание" (если результат известен после проверки первого операнда, второй не вычисляется).
НО есть аналогичные побитовые операторы (&, |), которые проверяют все операнды.
Операции преобразования числовых типов
В Java числовые типы данных делятся на:
Целые: byte, short, int, long
С плавающей точкой: float, double
Неявное преобразование
Происходит, если тип с меньшим диапазоном значений преобразуется в тип с большим:
int a = 10;
double b = a; // Автоматическое преобразование(сплошные линии на схеме)
пунктирные – могут производиться автоматически без ошибок, но при преобразовании мы можем столкнуться с потерей информации.
int a = 2147483647;
float b = a; // от типа int к типу float
System.out.println(b); // 2.14748365E9
Явное преобразование
Происходит, если нужно преобразовать тип с большим диапазоном в тип с меньшим:
long a = 4;
int b = (int) a;
При применении явных преобразований мы можем столкнуться с потерей данных
double a = 10.5;
int b = (int) a; // Явное преобразование, дробная часть отбрасывается
- Символьные строки, методы работы со строками Java.
Строки в Java представлены объектами класса String. Строка — это неизменяемый объект, который хранит последовательность символов. В отличие от примитивных типов данных, таких как int или char, строки относятся к ссылочному типу, а это значит, что они создаются как объекты в памяти.
Способы объявления (инициирования) строки:
- с помощью литералов:
- с помощью ключевого слова “new”:
- используя значения NULL:
Некоторые методы работы со строками в Java:
- Получение длины строки (метод length()).
- Получение символа в строке по его индексу (метод charAt()).
- Конкатенация строк (метод concat() и оператор + для объединения двух строк).
- Сравнение строк (методы equals() и equalsIgnoreCase()).
- Извлечение подстроки (метод substring()).
- Преобразование строки в нижний или верхний регистр (методы toLowerCase() и toUpperCase()).
- Удаление начальных и конечных пробелов (метод trim()).
- Разделение строки на массив подстрок (метод split()).
- Замена символов или подстрок (методы replace() и replaceAll()).
Для изменения строк в Java рекомендуется использовать классы StringBuilder и StringBuffer, которые позволяют изменять строки без создания новых объектов.
Строки в Java неизменяемы, то есть после создания строка не может быть изменена. Например, методы concat(), toLowerCase() и replace() возвращают новую строку, не изменяя исходную.
Иммутабельность строк в Java полезна для оптимизации памяти, поскольку строки могут быть кешированы в строковом пуле, а также для безопасности в многопоточном окружении. Но это надо учитывать при использовании конкатенации строк, поскольку при этом создается много временных объектов, что может снизить производительность.
- Классы StringBuffer и StringBuilder.
Класс String представляет собой последовательность символов. Все определенные в программе строковые литералы, вроде "This is String" — это экземпляры класса String.
У String есть две фундаментальные особенности:
- это immutable (неизменяемый) класс
- это final класс
В общем, у класса String не может быть наследников (final) и экземпляры класса нельзя изменить после создания (immutable).
После создания строк с ними часто совершается множество операций:
- перевод строк в разные регистры;
- извлечение подстрок;
- конкатенация;
- и т.д.
НО! В силу неизменности класса String, в результате каждой операции создаются новые экземпляры строк, а старые отбрасываются, порождая большое количество “мусора”.
Чтобы справиться с созданием временного мусора из-за модификаций объекта String, можно использовать класс StringBuffer.
Это mutable класс, т.е. изменяемый. Объект класса StringBuffer может содержать в себе определенный набор символов, длину и значение которого можно изменить через вызов определенных методов.
Для создания нового объекта используется один из его конструкторов, например:
- StringBuffer() — создаст пустой (не содержащий символов) объект
- StringBuffer(String str) — создаст объект на основе переменной str (содержащий все символы str в той же последовательности)
StringBuffer sb = new StringBuffer();
StringBuffer sb2 = new StringBuffer("Not empty");
Для работы со строками у класса StringBuffer есть ряд методов. Перечислим основные:
- .append — конкатенация строк. Метод перегружен и может принимать различные аргументы.
- .delete(int start, int end) — удаляет подстроку символов начиная с позиции start, заканчивая end
- .deleteCharAt(int index) — удаляет символ в позиции index
- .insert(int offset, String str) — вставляет строку str в позицию offset. Метод insert также перегружен и может принимать различные аргументы
- .replace(int start, int end, String str) — заменит все символы начиная с позиции start до позиции end на str
- .reverse() — меняет порядок всех символов на противоположный
- .substring(int start) — вернет подстроку, начиная с позиции start
- .substring(int start, int end) — вернет подстроку, начиная с позиции start до позиции end
Преимущества:
- StringBuffer — изменяемый класс, поэтому при работе с ним не возникает такого же количества мусора в памяти, как со String. Поэтому если над строками проводится много модификаций, лучше использовать StringBuffer.
- StringBuffer — потокобезопасный класс. Его методы синхронизированы, а экземпляры могут быть использованы несколькими потоками одновременно.
Недостатки: С одной стороны, потокобезопасность — преимущество класса, а другой — недостаток. Синхронизированные методы работают медленнее не сихнронизированных.
StringBuilder — класс, представляющий последовательность символов. Он очень похож на StringBuffer во всем, кроме потокобезопасности.
StringBuilder предоставляет API, аналогичный API StringBuffer’a.
Разница лишь в том, что StringBuffer потокобезопасен, и все его методы синхронизированы, а StringBuilder — нет. Это единственная особенность.
StringBuilder в Java работает быстрее StringBuffer’а благодаря несинхронизированности методов.
Поэтому в большинстве случаев, кроме многопоточной среды, лучше использовать StringBuilder.
- Массивы Java: объявление, инициализация. Основные методы класса Arrays. Доступ к элементам массивов, итерация массивов. Двумерные массивы.
Массивы в Java — это структура данных, которая позволяет хранить набор элементов одного типа.
Для объявления массива необходимо:
- Указать тип элементов.
- Использовать квадратные скобки [].
Пример: int[] numbers; // Массив целых чисел
Инициализация массива
- Явная: При создании сразу задаются значения.
Доступ к элементам массива
Доступ осуществляется по индексу (начинается с 0). Изменить или получить элемент массива можно так:
Для работы с каждым элементом массива можно использовать:
- Цикл for
- Усовершенствованный for (foreach)
- Стримы (с использованием Java Streams, для Java 8+)
Двумерные массивы
- Объявление и инициализация:
Основные методы класса Arrays
- Сортировка массива sort()
- Вывод массива в строку toString()
- Поиск элемента binarySearch() - Работает только на отсортированном массиве
- Копирование массива copyOf()
- Сравнение массивов equals()
- Заполнение массива fill() - Заполняет массив одинаковыми значениями.
- Управляющие конструкции Java: ветвление, циклы. Цикл foreach.
Управляющие конструкции в Java используются для управления потоком выполнения программы. Основные типы:
- Ветвления - условные операторы, выполнение определенной команды или набора команд только при условии истинности некоторого логического выражения;
- Циклы - повторение набора инструкций.
Ветвления
Основное предназначение оператора ветвления – это реализация разветвляющихся алгоритмов.
- if, if-else
- else if
- switch
Синтаксис оператора ветвления if else
Синтаксис оператора ветвления else if
В программировании часто встречается необходимость создания каскадных операторов ветвления. Синтаксис такой структуры имеет вид :
Синтаксис оператора ветвления switch
Конструкция if else может оказаться неудобной, если вы стоите перед необходимостью сделать выбор из нескольких вариантов. Оператор switch применяется для проверки одного выражения на несколько значений.
Выполнение кода начинается с метки case, соответствующей значению выражения choice, и продолжается до следующего оператора break или конца оператора switch. Если ни одна метка не совпадает со значением переменной, выполняется раздел default, если он предусмотрен.
Метка case должна быть целочисленной. Нельзя проверять строки. Все значения case должны быть уникальными литералами. Если в двух операторах case будут одинаковые значения, то транслятор выдаст сообщение об ошибке.
Циклы
В java существует два типа циклов:
- цикл типа «пока» - while и do…while - предназначен для повторения какого-то действия до тех пор, пока выполняется некоторое условие. Пример: увеличивать значение переменной на 3 до тех пор, пока оно не станет трехзначным.
- цикл типа «n-раз» - for (инициализация; условие; итерация) - предназначен для повторения каких-то действий заранее известное количество раз.
Пример: найти факториал числа 6.
Циклы while и do…while
Оператор while повторяет указанные действия до тех пор, пока его выражение имеет истинное значение.
Синтаксис цикла while
Условие необходимости повторения цикла проверяется перед каждым шагом цикла, в том числе перед самым первым.
Java предлагает также возможность использования цикла с постпроверкой условия. Для его записи используется конструкция из операторов do…while.
Тело цикла do…while выполняется по крайней мере один раз. Этот оператор удобно использовать, когда некоторое действие в программе нужно выполнить по крайней мере единожды, но при некоторых условиях придётся повторять его многократно.
Цикл for
Оператор for содержит три параметра : параметр инициализации, параметр повторения, параметр итерации.
Синтаксис цикла for
В первом параметре определяют переменную, с помощью которой будет подсчитываться количество повторений цикла - счетчик. Счётчику задают некоторое начальное значение (указывают, начиная с какого значения он будет изменяться). Во втором параметре указывают некоторое ограничение на счётчик, т.е. до какого значения он будет изменяться. В третьем параметре указывают выражение, изменяющее счётчик после каждого шага цикла.
Цикл foreach
Используется для итерации по коллекциям или массивам. Позволяет упростить работу с данными, исключив необходимость управления индексами.
- Перечислить и дать описание основных принципов объектно-ориентированного программирования (ООП). Достоинства и недостатки ООП.
Принципы:
- Инкапсуляция
Инкапсуляция означает скрытие деталей реализации объекта и предоставление только интерфейса для взаимодействия с ним. Это позволяет изолировать изменения в одной части программы от других частей, что делает код более надежным и устойчивым к изменениям.
- Наследование
Наследование позволяет создавать новые классы на основе существующих. Это способствует повторному использованию кода и созданию иерархий классов. Наследование позволяет наследникам использовать свойства и методы предков и переопределять или расширять их, если это необходимо.
- Полиформизм
Полиморфизм означает способность объектов разных классов обладать общим интерфейсом. Это позволяет обрабатывать объекты разных типов с помощью общих методов и функций. Полиморфизм делает код более гибким и расширяемым.
- Абстракция
Абстракция - это процесс выделения общих характеристик объектов и создание абстрактных классов или интерфейсов для их представления. Абстракция помогает упростить модель системы, делая её более понятной и управляемой.
Достоинства:
- Модульность
Инкапсуляция объектов в себе упрощает разработку, уменьшает количество ошибок и ускоряет разработку при участии большого количества программистов, так как каждый может работать независимо друг от друга.
- Расширяемость
ООП-код легче развивать, дополнять и менять. Этому способствует независимая модульная структура.
- Повторное использование кода
Благодаря абстракциям, полиморфизму и наследованиям можно не писать один и тот же код много раз, что заметно ускоряет создание нового ПО.
- Гибкость
Полиморфизм позволяет быстро адаптировать ООП-код под свои нужды, не описывая новые функции и объекты.
- Безопасность
Извне получить доступ к инкапсулированному коду нельзя, поэтому сломать код – задача не из простых.
- Простота восприятия
Использование ООП упрощает понимание кода за счет взаимодействия с объектами, а не логикой. Не нужно углубляться в то, как построено ПО, чтобы модифицировать его.
Недостатки:
- Больший объем кода
ООП приводит появлению большего количества кода, нежели в императивном программировании.
- Медленные программы
Объекты потребляют больше оперативной памяти, чем примитивные типы данных
- Специфичность
Ряд задач могут лучше решаться в императивном, логическом или функциональном стиле, где использование ООП не даст выигрыша.
- Определение класса. Объявление класса. Спецификаторы доступа. Отношения между классами Java (наследование, зависимость, агрегирование). Статические члены класса. Переменные класса.
Класс в Java — это шаблонная конструкция, которая позволяет описать в программе объект, его свойства (атрибуты или поля класса) и поведение (методы класса).
Объявление класса начинается с ключевого слова class, после которого указывается идентификатор — имя класса. Затем в фигурных скобках перечисляются атрибуты и методы класса.
class Dog {
int age; // возраст
String name; // кличка
public void voice() {
for (int i = 1; i <= age; i++) {
System.out.println("гав-гав");
}
}
}
Спецификаторы доступа в Java (public, private, protected, default) ограничивают видимость и доступ к классам, переменным, методам и конструкторам
1. Public
Модификатор public означает, что элемент (класс, метод, переменная или конструктор) доступен из любого места в программе, включая другие пакеты. Это самый открытый уровень доступа, и его следует использовать, когда элемент должен быть видимым для всего кода.
public class MyClass {
public int myVariable;
public void myMethod() {
// ...
}
}
2. Private
Модификатор private делает элемент доступным только внутри класса, в котором он определен. Это наиболее ограничительный уровень доступа и используется для инкапсуляции и скрытия деталей реализации.
class MyClass {
private int myVariable;
private void myMethod() {
// ...
}
}
3. Protected
Модификатор protected делает элемент доступным только для классов из того же пакета и всех подклассов. Это полезно, когда нужно предоставить доступ к определенным методам и переменным для наследников, но скрыть их от других классов.
class MyClass {
protected int myVariable;
protected void myMethod() {
// ...
}
}
4. Default (пакетный доступ)
Если модификатор доступа не указан явно, то используется уровень доступа по умолчанию, который также называется «пакетный доступ». Элементы с таким уровнем доступа доступны только для классов из того же пакета, что и определенный элемент.
class MyClass {
int myVariable; // default access
void myMethod() { // default access
// ...
}
}
Наследование — механизм, который позволяет описать новый класс на основе существующего (родительского). При этом свойства и функциональность родительского класса заимствуются новым классом. В Java для реализации наследования используется ключевое слово extends.
Агрегация — отношение, когда один объект выступает в качестве части другого. Пример: класс Library имеет поле, содержащее объекты класса Book. Книги могут быть добавлены или удалены из библиотеки, но они также могут существовать независимо от библиотеки.
Композиция — отношение, когда один объект является частью другого объекта и не может существовать независимо от него. Пример: класс Car содержит объект класса Engine в качестве своего поля. Когда объект Car удаляется, объект Engine также удаляется.
Статические члены класса в Java — это поля и методы, которые принадлежат классу, а не конкретному объекту. Это позволяет получить к ним доступ без создания экземпляра класса.
Статические переменные (статические поля) принадлежат классу, а не конкретному объекту. Они инициализируются один раз при загрузке класса. Например, можно использовать статическую переменную для подсчёта количества созданных объектов данного класса.
Статические методы принадлежат классу и вызываются через имя класса. Они не имеют доступа к нестатическим членам класса. Например, такие методы могут выполнять математические операции или преобразования данных.
Для объявления статических переменных, констант, методов и инициализаторов перед их объявлением указывается ключевое слово static.
- Объект класса. Создание объекта. Конструктор класса, конструктор по умолчанию. Ключевое слово this. Перегрузка конструкторов. Доступ к переменным экземпляра.
Объект класса
Объект класса — это конкретный экземпляр, созданный на основе определения класса. Каждый объект имеет свои собственные значения переменных экземпляра (или полей), которые могут отличаться от значений других объектов того же класса.
Создание объекта
Для создания объекта класса в Java используется оператор new, который выделяет память для нового объекта и вызывает конструктор класса. Пример создания объекта:
1MyClass myObject = new MyClass();
Конструктор класса
Конструктор — это специальный метод, который вызывается при создании объекта. Он имеет то же имя, что и класс, и не имеет возвращаемого типа. Конструктор может принимать параметры, которые позволяют инициализировать переменные экземпляра.
Пример конструктора:
1public class MyClass {
2 private int value;
3
4 // Конструктор с параметром
5 public MyClass(int value) {
6 this.value = value; // Инициализация переменной экземпляра
7 }
8}
Конструктор по умолчанию
Конструктор по умолчанию — это конструктор без параметров, который автоматически создается компилятором, если вы не определяете ни одного конструктора в классе. Если вы определяете хотя бы один конструктор, конструктор по умолчанию не создается автоматически.
Пример конструктора по умолчанию:
1public class MyClass {
2 private int value;
3
4 // Конструктор по умолчанию
5 public MyClass() {
6 this.value = 0; // Инициализация переменной экземпляра
7 }
8}
Ключевое слово this
Ключевое слово this используется для ссылки на текущий объект класса. Оно помогает различать переменные экземпляра и параметры метода или конструктора, если они имеют одинаковые имена.
Пример использования this:
1public class MyClass {
2 private int value;
3
4 public MyClass(int value) {
5 this.value = value; // 'this.value' ссылается на переменную экземпляра, а 'value' - на параметр
6 }
7}
Перегрузка конструкторов
Перегрузка конструкторов — это возможность создавать несколько конструкторов с разными параметрами в одном классе. Это позволяет создавать объекты с различными начальными значениями.
Пример перегрузки конструкторов:
1public class MyClass {
2 private int value;
3
4 // Конструктор по умолчанию
5 public MyClass() {
6 this.value = 0;
7 }
8
9 // Конструктор с параметром
10 public MyClass(int value) {
11 this.value = value;
12 }
13
14 // Конструктор с двумя параметрами
15 public MyClass(int value1, int value2) {
16 this.value = value1 + value2;
17 }
18}
Доступ к переменным экземпляра
Переменные экземпляра (поля) класса могут быть доступны через методы класса, включая конструкторы. Для доступа к переменным экземпляра можно использовать this или просто имя переменной, если нет конфликта имен.
Пример доступа к переменным экземпляра:
1public class MyClass {
2 private int value;
3
4 public MyClass(int value) {
5 this.value = value; // Инициализация переменной экземпляра
6 }
7
8 public int getValue() {
9 return this.value; // Доступ к переменной экземпляра
10 }
11}
- Методы, объявление, имя. Статические методы. Доступ к методам. Спецификаторы доступа.
1. Объявление метода
Метод в Java объявляется следующим образом:
[модификатор доступа] [другие модификаторы] тип_возвращаемого_значения имяМетода (параметры) {}
Пример:
public int сложить(int a, int b) {return a + b;}
2. Имя метода
- Должно следовать правилам именования идентификаторов в Java.
- Должно быть глаголом, если метод выполняет действие.
3. Статические методы
- Объявляются с модификатором static.
- Принадлежат классу, а не объекту.
- Вызываются без создания экземпляра класса.
Пример:
public class MathUtils {
public static int multiply(int a, int b) {return a * b; }}
Вызов статического метода:
int result = MathUtils.multiply(5, 10);
4. Доступ к методам (Вызов)
Методы можно вызывать из других методов класса или из других классов:
Экземплярные методы (не статические): вызываются через объект.
MyClass obj = new MyClass();
obj.instanceMethod()
Статические методы: вызываются через имя класса.
MyClass.staticMethod();
5. Спецификаторы доступа
Определяют, где можно использовать метод.
Спецификатор
Доступ внутри класса
Доступ в пакете
Доступ в наследниках
Доступ извне
public
да
да
да
да
protected
да
да
да
нет
default
да
да
нет
нет
private
да
нет
нет
нет
Пример:
public class Example {
private void privateMethod() {} // Доступ только внутри класса
protected void protectedMethod() {} // Доступ в классе и наследниках
public void publicMethod() {} // Доступ везде
}
6. Важные ключевые слова
- this – ссылается на текущий объект.
- super – вызывает метод родительского класса.
- final – запрещает переопределение метода.
- abstract – объявляет метод без реализации (для абстрактных классов).
Пример использования final:
class Base {
public final void display() {
System.out.println("Cannot override this method.");
}
}
- Пакеты Java. Импорт пакетов и классов. Статический импорт.
Пакетом (пространством имен) в Java называется структура вложенных по какому-то признаку папок с размещенными в них классами (интерфейсами, перечислениями, аннотациями), необходимыми проекту.
Вы можете думать о пакетах как о папках на вашем компьютере, в которых хранятся файлы, сгруппированные в соответствии с их функциональностью (назначением). Поскольку проект может состоять из сотен или тысяч классов, имеет смысл поддерживать порядок, помещая их в пакеты.
Конструкция import позволяет "импортировать" один или несколько классов и интерфейсов и после этого обращаться к ним не по полному имени (включающему название package-а), а по непосредственному имени.
Импорт пакетов и классов в Java осуществляется с помощью директивы import, которая указывается после директивы package. Директива указывается в самом начале кода, после чего идёт имя подключаемого класса. Исключение составляют классы из пакета java.lang (например, String), которые подключаются в программу автоматически
Статический импорт в Java — особая форма импорта, для которой вместе с директивой import используется модификатор static. Благодаря статическому импорту можно использовать статические методы без названия класса. Например, писать не Math.sqrt(20), а sqrt(20), так как функция sqrt(), которая возвращает квадратный корень числа, является статической
- Вложенные и внутренние классы Java. Статические и нестатические внутренние классы.
Класс называется вложенным (nested), если он определен внутри другого класса. Создается для того, чтобы обслуживать окружающий его класс. Если вложенный класс оказывается полезен в каком-либо ином контексте, он должен стать классом верхнего уровня.
- Применяются в тех случаях, когда нужно написать небольшой вспомогательный код для другого класса
- Создаются, чтобы скрыть переменные и методы от внешнего мира (еще один способ ограничения области видимости)
- Есть смысл использовать, если предполагается, что они будут использовать элементы родителя, чтобы не передавать лишнего в конструкторах
class OuterClass {
...
class NestedClass {
...
}
}
Они делятся на 2 вида:
- Non-static nested classes — нестатические вложенные классы. По-другому их еще называют inner classes — внутренние классы. Является членом внешнего класса и имеет доступ ко всем его членам, включая приватные. Однако для создания объекта такого класса требуется объект внешнего класса.
- Static nested classes — статические вложенные классы. Объявляется с модификатором static. Он является членом внешнего класса, но не имеет доступа к нестатическим членам внешнего класса напрямую.
Вложенные классы используются, когда один класс логически связан с другим, а внутренние — для тесной интеграции с внешним классом.
Внутренние классы — это классы для выделения в программе некой сущности, которая неразрывно связана с другой сущностью. Помимо того, что внутренний класс может быть просто внутренним классом, он еще бывает:
- локальным классом (local class) - класс, объявленный внутри метода
- анонимным классом (anonymous class) - определяется и используется на месте.
- Наследование. Подклассы и суперклассы. Доступ к членам класса. Конструкторы при наследовании, ключевое слово super.
Наследование – это механизм объектно-ориентированного программирования, который позволяет создавать новые классы (подклассы), наследуя свойства и поведение (методы) существующего класса (суперкласса).
С помощью наследования можно расширить функционал уже имеющихся классов за счет добавления нового функционала или изменения старого.
Чтобы объявить один класс наследником от другого, надо использовать после имени класса-наследника ключевое слово extends, после которого идет имя базового класса.
Для класса Employee базовым является Person, и поэтому класс Employee наследует все те же поля и методы, которые есть в классе Person.
Доступ к членам класса
- public: доступен в любом месте программы.
- protected: доступен в том же пакете и в подклассах.
- private: доступен только внутри того же класса.
- Модификатор доступа по умолчанию: доступен только в пределах одного пакета.
Если в базовом классе определены конструкторы, то в конструкторе производного классы необходимо вызвать один из конструкторов базового класса с помощью ключевого слова super. Например, класс Person имеет конструктор, который принимает один параметр. Поэтому в классе Employee в конструкторе нужно вызвать конструктор класса Person. То есть вызов super(name) будет представлять вызов конструктора класса Person.
При вызове конструктора после слова super в скобках идет перечисление передаваемых аргументов. При этом вызов конструктора базового класса должен идти в самом начале в конструкторе производного класса. Таким образом, логика делегируется конструктору базового класса.
class Employee extends Person{
public Employee(String name){
super(name); // если базовый класс определяет конструктор
// то производный класс должен его вызвать
}
}
(то-есть если определять конструктор, то в начале обязательно вызвать конструктор суперкласса. а если вообще не писать его, то он будет вызван автоматически)
Другое использование слова super:
для вызова метода суперкласса
- Иерархия наследования Java. Преобразование типов при наследовании. Ключевое слово instanceof.
Иерархия наследования в Java — это набор классов, связанных отношением наследования. В терминологии Java наследуемый класс называется суперклассом, а наследующий класс — подклассом. Подкласс наследует все члены, определённые в суперклассе, добавляя к ним собственные, особые элементы.
Класс java.lang.Object всегда находится на вершине любой иерархии наследования Java. Все классы, за исключением самого класса Object, наследуют (или прямо, или косвенно) от этого класса.
щас будет пример, на нем легче понять эту дичь
Допустим, у нас есть следующие классы:
// класс человека
class Person {
private String name;
…
}
// служащий некоторой компании
class Employee extends Person{
private String company;
…
}
*extends - значит наследует класс Person
// класс клиента банка
class Client extends Person{
private int sum; // Переменная для хранения суммы на счете
private String bank;
…
}
В этой иерархии классов следующее наследие:
Суперклассы обычно размещаются выше подклассов, поэтому на вершине наследования находится класс Object, а в самом низу Employee и Client.
Восходящее преобразование (upcasting) в Java происходит, когда объект класса-наследника преобразуется в объект класса-родителя. Такое преобразование осуществляется автоматически. (Объект подкласса также представляет объект суперкласса. Объект подкласса наследует все от родителя - объект Employee всегда является также объектом Person)
Обратное не всегда верно. Например, объект Person не всегда является объектом Employee или Client. Нисходящее преобразование (downcasting) происходит, когда объект класса преобразуется в объект класса-наследника. Во время нисходящего преобразования необходимо явное приведение типа, так как объект класса-наследника содержит дополнительные методы и поля, которых нет в родительском классе.
Пример нисходящего преобразования (от предка к потомку):
(display означает отображение объекта)
В данном случае переменная типа Object хранит ссылку на объект Client. Мы можем без ошибок привести этот объект к типам Person или Client. Но при попытке преобразования к типу Employee мы получим ошибку во время выполнения. Так как kate не представляет объект типа Employee.
instanceof
Ключевое слово instanceof проверяет, является ли объект экземпляром конкретного класса или интерфейса. Оно сравнивает экземпляр с типом и возвращает значение true или false.
Оператор instanceof проверяет именно происхождение объекта, а не переменной.
- Полиморфизм в Java. Перегрузка и переопределение методов.
Полиморфизм является одним из основных принципов ООП. Это способность объекта использовать методы производного класса, который не существует на момент создания базового класса. Полиморфизм позволяет использовать объекты производного класса как объекты базового класса, выполняя при этом методы производного класса.
Например, есть базовый класс Animal и производные от него классы Dog и Cat. У всех этих классов есть метод makeSound(). Если создать массив объектов класса Animal и поместить в него объекты классов Dog и Cat, то при вызове метода makeSound() для каждого из объектов в массиве, будет вызван соответствующий метод класса, которому принадлежит объект.
class Animal {
void makeSound() {System.out.println("Some generic animal sound");}
}
class Dog extends Animal {
@Override
void makeSound() {System.out.println("Woof");}
}
class Cat extends Animal {
@Override
void makeSound() {System.out.println("Meow");}
}
public class PolymorphismExample {
public static void main(String[] args) {
Animal[] animals = { new Dog(), new Cat() };
for (Animal animal : animals) {
animal.makeSound(); // Вызов метода конкретного класса
}
}
}
Переопределение в Java — это возможность класса наследника предоставить свою реализацию метода, который уже предоставлен в родительском классе. Этот механизм позволяет классу наследнику наследовать методы родительского класса и изменять их поведение, если это требуется.
Например, класс Shape имеет метод getArea(), который возвращает площадь фигуры. Классы Circle и Rectangle, наследующие класс Shape, переопределяют метод getArea() для предоставления своей собственной реализации расчета площади.
Чтобы переопределить метод, нужно:
1) В классе-наследнике создать метод с таким же именем, как в родительском классе.
2) Добавить перед ним аннотацию @Override. Эта аннотация сообщит компилятору, что это не ошибка, а намеренное переопределение метода.
3) Написать собственную реализацию для каждого класса-наследника. Если этого не сделать, то будет использована реализация родительского класса.
Перегрузка методов в Java — это возможность класса иметь два или более метода с одинаковыми именами, но разными параметрами. Методы могут отличаться количеством параметров, типом параметров или их порядком.
Например, в классе Rectangle может быть два метода setDimensions(): один принимает два параметра (длину и ширину прямоугольника), а второй — один параметр (длину стороны, если прямоугольник является квадратом).
class Rectangle {
double length, width;
void setDimensions(double length, double width) { //прямоугольник
this.length = length;
this.width = width;
}
void setDimensions(double side) { //квадрат
this.length = side;
this.width = side;
}
double getArea() { return length * width; }
}
- Абстрактные методы и классы Java.
Абстрактный класс — это класс, который не может быть создан (нельзя создать его экземпляр). Он предназначен для наследования и содержит как реализованные, так и абстрактные (незавершённые) методы.
Особенности абстрактных классов:
- При объявлении используется ключевое слово abstract: abstract class class_name{}
- Может содержать абстрактные методы (без тела) и обычные методы (с реализацией).
- Может содержать поля, конструкторы и статические методы.
- Наследование:
- Абстрактный класс может наследоваться только одним классом (single inheritance).
- Интерфейс может быть реализован несколькими классами.
- Полиморфизм:
- Абстрактный класс поддерживает частичную реализацию методов.
- В интерфейсах до Java 8 методы не могли иметь реализации, однако начиная с Java 8 появилась поддержка default-методов.
- Модификаторы:
- В абстрактных классах методы могут быть protected или public.
- Методы интерфейсов по умолчанию являются public.
Когда использовать абстрактные классы
- Если требуется предоставить базовую реализацию для нескольких связанных классов.
- Если нужно определять общие поля или методы с реализацией.
- Если есть смысловая связь "is-a" между базовым и производным классами. (связь заключается в том, что производные классы рассматриваются как специализированные версии базового класса)
- Интерфейсы Java: определение интерфейса, реализация интерфейса. Преимущества применения интерфейсов. Переменные интерфейсов. Наследование интерфейсов. Методы по умолчанию. Статические методы интерфейсов.
Интерфейс описывает поведение, которым должны обладать классы, реализующие этот интерфейс. «Поведение» — это совокупность методов.
Создание интерфейса очень похоже на создание обычного класса, только вместо слова class мы указываем слово interface.
Мы создали интерфейс Swimmable — «умеющий плавать». Это что-то вроде нашего пульта, у которого есть одна «кнопка»: метод swim() — «плыть».
Чтобы использовать интерфейс, его методы должны реализовать какие-то классы нашей программы.
Давай придумаем класс, объекты которого подойдут под описание «умеющий плавать». Например, подойдет класс утки — Duck:
Класс Duck «связывается» с интерфейсом Swimmable при помощи ключевого слова implements. Это значит, что класс, связанный с каким-то интерфейсом, должен реализовать все его методы. В нашем классе Duck прямо как в интерфейсе Swimmable есть метод swim(), и внутри него содержится какая-то логика.
Если бы мы не создали бы метод swim() в классе Duck, компилятор выдал бы нам ошибку.
Преимущества применения интерфейсов
- Множественное наследование: Java не поддерживает множественное наследование классов, но с помощью интерфейсов можно реализовать поведение из нескольких источников.
- Полиморфизм: Код становится более гибким, так как объекты могут быть представлены через интерфейсы.
- Поддержка принципа "Программируй на уровне интерфейсов": Это упрощает замену реализации без изменения кода, который взаимодействует с интерфейсом.
Переменные в интерфейсе по умолчанию являются:
- public static final (константы).
- Неизменяемыми.
Интерфейсы могут наследовать другие интерфейсы с помощью ключевого слова extends. При этом дочерний интерфейс наследует все методы родительского интерфейса.
Класс, реализующий дочерний интерфейс, должен реализовать методы как дочернего, так и родительского интерфейса
Интерфейсы кроме определения методов могут иметь их реализацию по умолчанию. Затем в классе нам необязательно этот метод реализовать, хотя мы можем его и переопределить.
В интерфейсах доступны статические методы - они аналогичны методам класса. Чтобы обратиться к статическому методу интерфейса также, как и в случае с классами, пишут название интерфейса и метод:
- Исключения (exception) Java. Синтаксис объявления исключений. Классификация исключений. Основные классы для работы с исключениями. Исключения при наследовании.
Исключение - любая ошибка, которая возникает в ходе выполнения программы. Это может быть несоответствие типов данных, деление на ноль, и т.д. Операции по их поиску и предотвращению называются обработкой исключений.
Иерархия и классификация
В основе всего лежит класс Throwable. Все возможные конфликты кода с машиной и пользователем описаны в этом классе.
Для удобства обработки и чтения класс Throwable имеет подклассы Error и Exception.
Error – критические ошибки, которые не следует пытаться обрабатывать в собственной программе, поскольку они связаны с проблемами уровня JVM(виртуальной машины). Например, исключения такого рода возникают, если закончилась память, доступная виртуальной машине.
Exception – является результатом проблем в программе, которые, в принципе, решаемые и предсказуемые. Например, произошло деление на ноль в целых числах.
Все исключения делятся на 2 типа:
- Checked (проверяемые *желтые*)
Наличие\обработка Checked исключений проверяется на этапе компиляции. Они должны обрабатываться блоком catch или описываться в сигнатуре метода.
Примеры: ArithmeticException: исключение, возникающее при делении на ноль, IndexOutOfBoundException: индекс вне границ массива
- Unchecked (непроверяемые *красные*)
Наличие\обработка Unchecked исключений происходит на этапе выполнения. Исключения могут не обрабатываться и не быть описанными.
Примеры: InterruptedException: поток прерван другим потоком, ClassNotFoundException: невозможно найти класс.
Синтаксис:
Для обработки исключений java используются следующие операторы: try, catch, finally, throw, throws. Первые три — стандартная структура блока обработки.
В блоке finally набор обязательных действий при возникновении ошибки(запись данных и пр.). Блок исполняется всегда, вне зависимости от срабатывания catch.
throw – используется для явного возбуждения исключения.
throws – используется в сигнатуре методов для предупреждения, о том что метод может выбросить исключение.
Правила для исключений при наследовании:
- Переопределяемый метод в подклассе не может выбрасывать контролируемые исключения, которые выше по иерархии чем исключения в методе супер класса(например, в супер классе IOException, а в подклассе пытаемся кинуть Throwable).
- Конструктор подкласса должен включить в свой блок throws все классы исключений или их супер классы из блока throws конструктора супер класса, к которому он обращается при создании объекта.
- Коллекции: Java collections framework. Классификация интерфейсов коллекций. Интерфейс Collection.
Java Collection Framework — иерархия интерфейсов и их реализаций, которая является частью JDK и позволяет разработчику пользоваться большим количеством структур данных.
Для хранения наборов данных в Java предназначены массивы. Однако их не всегда удобно использовать, прежде всего потому, что они имеют фиксированную длину. Эту проблему в Java решают коллекции. Однако суть не только в гибких по размеру наборах объектов, но в и том, что классы коллекций реализуют различные алгоритмы и структуры данных, например: стек, очередь, дерево и ряд других.
Классы коллекций располагаются в пакете java.util, поэтому перед применением коллекций следует подключить данный пакет.
Хотя в Java существует множество коллекций, но все они образуют стройную и логичную систему. Во-первых, в основе всех коллекций лежит применение того или иного интерфейса, который определяет базовый функционал. Среди этих интерфейсов можно выделить следующие:
- Collection базовый интерфейс для всех коллекций и других интерфейсов коллекций
- Queue: наследует интерфейс Collection и представляет функционал для структур данных в виде очереди
- Deque: наследует интерфейс Queue и представляет функционал для двунаправленных очередей
- List: наследует интерфейс Collection и представляет функциональность простых списков
- Set: также расширяет интерфейс Collection и используется для хранения множеств уникальных объектов
- SortedSet: расширяет интерфейс Set для создания сортированных коллекций
- NavigableSet: расширяет интерфейс SortedSet для создания коллекций, в которых можно осуществлять поиск по соответствию
- Map: предназначен для созданий структур данных в виде словаря, где каждый элемент имеет определенный ключ и значение. В отличие от других интерфейсов коллекций не наследуется от интерфейса Collection
Эти интерфейсы частично реализуются абстрактными классами: AbstractCollection, AbstractList, AbstractSet, AbstractQueue, AbstractSequentialList, AbstractMap.
С помощью применения вышеописанных интерфейсов и абстрактных классов в Java реализуется широкая палитра классов коллекций - списки, множества, очереди, отображения и другие, среди которых можно выделить следующие: ArrayList, LinkedList, ArrayDeque, HashSet, TreeSet, LinkedHashSet, PriorityQueue, TreeMap
Схематично всю систему можно представить следующим образом:
Интерфейс Collection является базовым для всех коллекций, определяя основной функционал:
Интерфейс Collection является обобщенным и расширяет интерфейс Iterable, поэтому все объекты коллекций можно перебирать в цикле по типу for-each.
Среди методов интерфейса Collection можно выделить следующие:
- boolean add(E item): добавляет в коллекцию объект item. При удачном добавлении возвращает true, при неудачном - false
- boolean addAll(Collection<? extends E> col): добавляет в коллекцию все элементы из коллекции col. При удачном добавлении возвращает true, при неудачном - false
- void clear(): удаляет все элементы из коллекции
- boolean contains(Object item): возвращает true, если объект item содержится в коллекции, иначе возвращает false
- boolean isEmpty(): возвращает true, если коллекция пуста, иначе возвращает false
- Iterator<E> iterator(): возвращает объект Iterator для обхода элементов коллекции
- boolean remove(Object item): возвращает true, если объект item удачно удален из коллекции, иначе возвращается false
- boolean removeAll(Collection<?> col): удаляет все объекты коллекции col из текущей коллекции. Если текущая коллекция изменилась, возвращает true, иначе возвращается false
- boolean retainAll(Collection<?> col): удаляет все объекты из текущей коллекции, кроме тех, которые содержатся в коллекции col. Если текущая коллекция после удаления изменилась, возвращает true, иначе возвращается false
- int size(): возвращает число элементов в коллекции
- Object[] toArray(): возвращает массив, содержащий все элементы коллекции
Все эти и остальные методы, которые имеются в интерфейсе Collection, реализуются всеми коллекциями, поэтому в целом общие принципы работы с коллекциями будут одни и те же. Единообразный интерфейс упрощает понимание и работу с различными типами коллекций. Так, добавление элемента будет производиться с помощью метода add(), который принимает добавляемый элемент в качестве параметра. Для удаления вызывается метод remove(). Метод clear() будет очищать коллекцию, а метод size() возвращать количество элементов в коллекции.
- Списки. Интерфейс List. Основные классы, реализующие интерфейс List. ArrayList, особенности, методы. Comparator.
Интерфейс List
Интерфейс List определяет основные операции для работы с упорядоченными коллекциями, такие как добавление, удаление, получение и изменение элементов. Основные методы интерфейса List включают:
- add(E e): добавляет элемент в конец списка.
- add(int index, E element): вставляет элемент в указанную позицию.
- get(int index): возвращает элемент по указанному индексу.
- remove(int index): удаляет элемент по указанному индексу.
- set(int index, E element): заменяет элемент по указанному индексу.
- size(): возвращает количество элементов в списке.
- isEmpty(): проверяет, пуст ли список.
- contains(Object o): проверяет, содержится ли элемент в списке.
Основные классы, реализующие интерфейс List
Существует несколько классов, которые реализуют интерфейс List. Наиболее распространенные из них:
- ArrayList: динамический массив, который может изменять свой размер. Он обеспечивает быстрый доступ к элементам по индексу, но операции вставки и удаления могут быть медленными, если они происходят не в конце списка.
- LinkedList: реализует двусвязный список. Он обеспечивает быструю вставку и удаление элементов, но доступ по индексу медленнее, чем в ArrayList.
- Vector: устаревший класс, который также реализует динамический массив, но синхронизирован для потокобезопасности. В современных приложениях его обычно не используют.
ArrayList
ArrayList — это наиболее часто используемая реализация интерфейса List. Он позволяет хранить элементы в виде массива, который может динамически изменять свой размер. Основные особенности ArrayList:
- Динамическое изменение размера: при добавлении элементов, если массив заполняется, ArrayList автоматически увеличивает его размер.
- Быстрый доступ: доступ к элементам по индексу осуществляется за константное время (O(1)).
- Не синхронизирован: ArrayList не является потокобезопасным, поэтому для многопоточных приложений следует использовать Collections.synchronizedList() или CopyOnWriteArrayList.
Пример использования ArrayList:
Comparator
Comparator — это функциональный интерфейс, который используется для определения порядка сортировки объектов. Он позволяет сравнивать два объекта и определять, какой из них "меньше", "больше" или "равен". Comparator может быть использован для сортировки коллекций, таких как List.
Пример использования Comparator для сортировки списка:
- Интерфейс Set. Основные реализации. HashSet. TreeSet.
важное про Set:
- Не допускает дубликатов.
- Не гарантирует порядок хранения элементов (зависит от конкретной реализации).
- Позволяет выполнять операции, такие как добавление, удаление и проверку на наличие элемента.
пример HashSet
- Основан на хэш-таблице.
- Не гарантирует порядок элементов.
- Основные операции (add(), remove(), contains()) выполняются за O(1) (в среднем).
- Позволяет хранить null значение.
- Требует, чтобы объекты корректно переопределяли методы hashCode() и equals().
пример TreeSet
- Гарантирует отсортированный порядок элементов.
- Основные операции (add(), remove(), contains()) выполняются за O(log n).
- Не допускает null (начиная с Java 8).
Элементы должны реализовывать интерфейс Comparable или передаваться Comparator при создании.