JDK (Java Development Kit) - набор программ для разработки. JRE, загрузчик кода java, компилятор javac, архиватор jar, генератор документации javadoc и другие утилиты, для разработки.
JRE (Java Runtime Environment) - окружение, необходимое для запуска Java-программ. Включает в себя стандартную библиотеку. В нее входят, базовые пакеты lang, util... и пакеты для работы с различными форматами, базами данных, пользовательским интерфейсом.
JVM (Java Virtual Machine) - Виртуальная машина отвечает за само выполнение кода. Она работает с bytecode (тем, что находится внутри файлов с расширением .class).
Thread Stack - локальные переменные и методы что вызвал поток (стек).
локальная переменная из методов в объекте – в stack, сам объект в heap.
локальная переменная ссылка на объект – в stack, сам объект в heap.
локальные переменные примитивных типов – в stack и не видны другим потокам.
Поток не может совместно использовать примитивную локальную переменную, только передать копию. Если два потока выполняют один и тот же код, они создадут свои копии в стеках. Все данные в стеке GC roots.
Metaspace (Permanent generation в Java 8) - метаданные классов . Это пространство также является общими для всех. Так как metaspace является частью native memory, то его размер зависит от платформы.
Code cache - JIT-компилятор компилирует часто исполняемый код, преобразует его в нативный машинный код и кэширует для более быстрого выполнения.
Компиляция
AOT-компиляция (ahead-of-time, статическая) – процесс превращения текста на языке программирования в нативный код на машинном языке. Так работают языки вроде C++.
JIT-интерпретация (just-in-time, динамическая) – «умная» интерпретация. Среда выполнения анализирует исполняемый код, оптимизируя часто вызываемые участки. Таким способом программа работает значительно быстрее. Именно с JIT-компиляцией связана необходимость «прогрева» программ перед тестированием производительности.
Execution Engen - преобразует bytecode в машинный.
Интерпретатор - Он считывает байт-код, интерпретирует (конвертирует) в машинный код и выполняет их последовательно. Проблема с интерпретатором заключается в том, что он интерпретирует каждый раз, даже один и тот же метод, что снижает производительность системы. Для преодоления этой проблемы в версии 1.1 появились JIT-компиляторы.
Class
Class loaders
System
class loader --> classpath
|
v
Platform
class loader --> jre/lib/ext
|
v
Bootstrap
class loader --> rt.jar
В JVM встроено как минимум три стандартных загрузчика:
Bootstrap – Написан на C++. Это базовый загрузчик, который загружает все системные классы из архива rt.jar (java.lang.* в частности) при старте.
Platform(Extension java 9) – загружает классы расширений из папки jre/lib/ext,загружаются по мере их использования.
System (Application) – загружает классы из classpath конкретного приложения,загружаются по мере их использования.
Перед тем как загрузить класс, ClassLoader проверит, не может ли это сделать его родитель. Если класс уже загружен, то загрузка не потребуется.
Право загрузки класса рекурсивно делегируется от самого нижнего загрузчика в иерархии к самому верхнему.
Загрузка класса
Сначала загружается класс и цепочка его предков. Он загружается один раз.
Выполняется выделение памяти и инициализация статических полей и блоки инициализации в порядке объявления.
Инстанцируется сам экземпляр и цепочки наследования, с самого дальнего родителя.
Выделяется память в куче для экземпляра, получается ссылка на этот экземпляр.
Выполняются выделение памяти и инициализация нестатических полей и блоков инициализации в порядке объявления.
Вызывается конструктор.
Типы классов
Абстрактный - помеченный ключевым словом abstract. Не может иметь экземпляры, может иметь абстрактные методы(с модификатором abstract) и обычные.
Финальный - с модификатором final, нерасширяемый. Модификаторы abstract и final несовместимы, но по отдельности применимы к различным внутренним классам (кроме анонимного).
Nested classes - Java позволяет создавать одни классы внутри других. Внутренние и вложенные классы могут иметь несколько уровней вложенности.
Внутренний (non-static nested / inner) - объявленный внутри другого класса. Не может иметь статических объявлений. Имеет доступ ко всем внутренностям экземпляра внешнего класса. Если член внешнего класса foo перекрыт членом внутреннего (shadowing), обратиться к внешнему можно с помощью конструкции OuterClassname.this.foo, без перекрытия сработает просто foo. Инстанциируется только от экземпляра внешнего класса: outer.new Inner().
Локальный - объявленный внутри метода. Является внутренним классом, в случае объявления в статическом методе без доступа к экземпляру внешнего класса. Не имеет модификаторов доступа.
Анонимный - локальный класс, объявленный без имени, непосредственно при инстанцирование, расширением другого класса или интерфейса. Анонимный может расширять только один класс или интерфейс. Не может быть абстрактным или финальным. Замена Лямбда-выражение.
Вложенные (static nested) - имеет доступ ко всем статическим членам внешнего класса. В остальном ничем не отличается от обычного класса;
Class Object / 11 methods
Object - superclass/parent всех классов в java которые неявно наследуются от него.
public final native Class<?> getClass() - Возвращает класс этого экземпляра. То есть результатом вызова .getClass() переменной типа Foo может быть как Foo.class, так и .class любого из его подклассов. Компилятор страхуется от ClassCastException в рантайме подменой возвращаемого типа метода на Class<? extends Foo>.
public native int hashCode(), public boolean equals(Object obj)
HashCode - для эффективного поиска. Default - адрес объекта (зависит от JVM).
Equals - для разрешения коллизий. Default - Сравнивает ссылки как ==.
Эти два метода придуманы для использования в Collections. Методы нужно переопределить чтобы эффективно использовать экземпляры как ключи в HashMap или HashSet. Они работают эффективнее, при хорошем распределение хэшей.
Контракты:
Нужно переопределять только вместе.
Если объекты равны у них одинаковый hashCode, но если hashCode одинаковый объекты не всегда равны
(одинаковые поля для хэш кода или предельное значение int).
Переопределение:
@Ovveride
publicbooleanequals(Object o){
if(this== o)returntrue;//reflect
if(o ==null)returnfalse;// null check
// class type check and class casting
if(getClass()!= o.getClass())returnfalse;
RestConfig that =(RestConfig) o;
// field check
return connectTimeOut == that.connectTimeOut
&& readTimeOut ==this.readTimeOut
&&Objects.equals(url, that.url);
}
Рефлексивность - a.equals(a)
Null - Ничто не может быть equals(null).
Симметрия - a.equals(b), то b.equals(a)
Транзитивность - a.equals(b) и a.equals(c), то b.equals(c)
Согласованность - equals и hashCode должны возвращать одни и те же значения для одного и того же объекта при каждом последующем вызове, даже если состояние объекта изменилось. Это делает реализацию для изменяемых (mutable) объектов крайне сложной.
protected native Object clone() throws CloneNotSupportedException– реализации нет, вызов выбросит исключение. Нужно писать свою реализацию, делать при этом ее public и с маркерным интерфейсом Cloneable.
По умолчанию «поверхностное копирование» - в объектах будут ссылки на одни и те же сущности поэтому нужно делать “Глубокие копии” где изменяемые данные с вложенной структурой тоже копируются, а не только их ссылки.
Это диктуется соглашением, по которому клон не должен зависеть от оригинала.
По контракту клон должен быть другим объектом (!= оригиналу) и не зависеть от оригинала.
Альтернативы (многие считают что более удобные) метода clone - конструктор копирования и паттерн factory method.
public String toString() - getClass().getName() + '@' + Integer.toHexString(hashCode())
- переводит вызывающий поток в состояние ожидания. В этом случае вызывающий поток освобождает монитор, который имеет доступ к ресурсу. Ожидание продолжается до тех пор, пока другой поток, который вошел в монитор, не вызовет метод.
protected void finalize() - deprecated работал при сборке мусора, ненадежен.
Abstract class vs Interface
Abstract Class
Описывает состояние и поведение
Средство наследования реализации
Строит иерархию наследования с отношением IS A(близкая связь)
extends один раз
Interface
Описывает только поведение
Средство наследования API
Устанавливает контракт (классы, у которых вообще нет ничего общего)
implements много
extends несколько раз нельзя из за diamond problem когда у разных parents одинаковая сигнатура методов и не понятно какой использовать в child при вызове.
Абстрактные метод: Без реализации, в абстрактном классе, со словом abstract. Методы интерфейса неявно абстрактные.
default метод в interface: До выхода Java 8, если добавлять в interface новые методы, это приводило к ошибкам компиляции в классах имплементирующих метод, чтобы не изменять каждый отдельный класс были придуманы методы с реализацией.
Если имплементировать 2 Interface с одинаковой сигнатурой default методов - ошибка компиляции diamond problem.
Функциональные интерфейсы
Нужны для использования с lambda выражениями.
Это интерфейс, который содержит ровно один абстрактный метод, то есть описание метода без тела.
Статические методы и default методы при этом не в счёт.
@FunctionalInterface
interfaceConverter<F,T>{
Tconvert(F from);
}
Converter<String,Integer> con =(from)->Integer.valueOf(from);
Integer converted = con.convert("123");
log.info(converted);// 123
| Functional interface | Descriptor |
|----------------------|----------------|
| Predicate<T> | T->boolean |
| BiPredicate<T,U> | (T,U)->boolean |
| Consumer<T> | T->void |
| BiConsumer<T, U> | (T,U)->void |
| Supplier<T> | ()->T |
| Function<T,R> | T->R |
| BiFunction<T,U,R> | (T,U)->R |
| UnaryOperator<T> | T->T |
| BinaryOperator<T> | (T,T)->T |
Reflection
Это средства манипуляции данными на основе знания о структуре классов этих данных. Использование Reflection API медленное и небезопасное. Позволяет ломать инвариантность состояний экземпляра, нарушать инкапсуляцию, и менять финальные поля.
Используют рефлексию обычно в тестах, в инструментах разработки, в фреймворках (особенно в связке с runtime-аннотациями).
Инициализация классов-конфигураций в Spring Framework. Фреймворк с помощью рефлекшна сканирует внутренности классов. Поля и методы, помеченные специальными аннотациями, воспринимаются как элементы фреймворка. AOP.
Garbage Collectors
Сборщик мусора отслеживает доступность каждого созданного объекта в heap и удаляет не используемые. Сборщиков мусора много, но почти все работают по схеме:
Mark - когда сборщик мусора находит какие куски памяти используются, а какие нет.
Sweep - на этом шаге удаляются объекты которые были помечены раньше.
Compact - собрать оставшиеся объекты в одну чанку.
При сборке мусора перемещаются между eden, s1, s2, old – только хедеры объектов.
-XX:+UseSerialGC - вызвать конкретный сборщик
-XX:Min(Max)HeapFreeRatio=? задают долю свободного места в каждом поколении.
Если на объект никто не ссылается и не в GC Roots:
Class: Classes loaded by a system class loader; contains references to static variables as well
Stack Local: Local variables and parameters to methods stored on the local stack
Active Java Threads: All active Java threads
Objects used as monitors for synchronization
Типы сборщиков мусора
Serial GC - самый старый, однопоточный.
Структура памяти: Eden, Survivor 0, Survivor 1, Tenured(Old)
Плюсы: Использует мало ресурсов, нет оверхедов и побочных эффектов.
Минусы: Долгие паузы на сборку мусора при заметных объемах данных.
Когда использовать: RAM около 100Мб, не важны короткие остановки, 1 ядро процессора.
Parallel GC - параллельный, STW паузы короче, сам определяет количество потоков по ресурсам и параметрам. Можно указать параметры производительности — максимальное время сборки, пропускную способность — и сборщик будет стараться не превышать их.
-XX:MaxGCPauseMillis=400 - максимальная пауза
-XX:ParallelGCThreads=? - количество тредов
Структура памяти: Eden, Survivor 0, Survivor 1, OldGen
Плюсы: авто подстройки под требуемые параметры производительности и меньшие паузы.
Минусы: фрагментация памяти.
Когда использовать: простой и эффективный, подходит для большинства приложений.
Нет скрытых накладных расходов.
Shenandoah GC - OpenJDK начиная с 12-й верси -XX:+UseShenandoahGC.
Java передает параметры по значению — скопировать значение и передать копию.
В метод передается копия ссылки. Через которую можно изменять объект.
Сколько памяти занимает объект
Зависит от вендора JVM(32 или 64) и архитектуры CPU, говорим только про 64. Любой объект кратен 8 bytes. Минимальный размер когда есть только заголовок - 12 head + 4 padding = 16 bytes. Alignment/padding gap - чтобы адрес был кратным машинному слову, для ускорения чтения из памяти, уменьшения количества бит для указателя на объект, для уменьшения фрагментации памяти.
Обратиться к члену класса-родителя, который перекрыт (shadowed) членами наследника или локальными переменными:
int foo = super.foo
Вызвать в конструкторе конструктор родителя:
SubClass() { super("subclass param"); }
default - В Java 8 вместе с лямбдами и стримами появилась необходимость дополнить новыми методами интерфейсы. Чтобы не ломать обратную совместимость, добавили методы с телом через слово default. Новые методы в старых интерфейсах.
static - Ключевое слово static используется для объявления вложенных классов, статических методов, полей, блоков инициализации и статических импортов.
Статические поля и методы – члены класса, а не экземпляра(instance), потому к ним можно обращаться через имя класса. Код статического блока или метода имеет доступ только к статическим членам. Статические поля не участвуют в сериализации.
Инициализаторы статических полей выполняются в неявном статическом блоке.
Статические методы - используют раннее связывание, то есть вызов конкретного метода разрешается на этапе компиляции, не работают перегрузка и переопределение в наследниках.
Статический блок инициализации - выполняется потокобезопасно, один раз после загрузки класса загрузчиком. Блоков может быть несколько, выполнятся они в порядке объявления.
Static import - импортирует статические члены классов в .java-файл.
Аннотации
Retention Policy / @Retention – где останется аннотация после компиляции.
SOURCE – аннотация присутствует только в исходном коде. Два типа:
Маркеры - вариант документации. Примеры: @Immutable и @ThreadSafe из Hibernate.
Инструкции - для инструментов разработки @SuppressWarnings и @Override могут влиять на предупреждения и ошибки компиляции.
CLASS – попадает в байткод .class-файла, но игнорируется загрузчиком классов. Недоступна для рефлекшна. Используется для инструментов, обрабатывающих байткод.
RUNTIME – для снабжения метаинформацией, доступной во время выполнения программы. Используется для чтения метаинформации класса/объекта через Reflection API. Используется во множестве популярных фреймворков: Spring, Hibernate, Jackson.
Мета-аннотации – это аннотации для объявления других аннотаций. Вообще мета-аннотациями можно назвать любую аннотацию с таргетом ANNOTATION_TYPE.
@Repeatable – применяема несколько раз к одному и тому же элементу.
@Deprecated – устаревший.
@Inherited – применяется к наследникам. @Target - определяет, в каком контексте может применяться объявляемая аннотация.
Возможные таргеты:
TYPE – Объявление класса, интерфейса, аннотации или enum-а.
FIELD – Объявление поля (включая константы enum-ов).
METHOD – Объявление метода.
PARAMETER – Формальный параметр в объявлении метода.
CONSTRUCTOR – Объявление конструктора.
LOCAL_VARIABLE – Объявление локальной переменной.
ANNOTATION_TYPE – Объявление аннотации. Применяется для создания мета-аннотации.
PACKAGE – Объявление пакета (в package-info.java).
TYPE_PARAMETER – Объявление generic типа-параметра.
TYPE_USE – Любое использование типа. Например: (@NonNull String) myObject.
MODULE – Объявление модуля.
Виды String
Классы String, StringBuffer и StringBuilder. Класс String final для того чтобы работал string pool, не изменяемости паролей и прочего, потокобезопасности, имена классов. А эти два вспомогательных класса реализуют для него паттерн Builder и служат способом редактирования строки без относительно дорогого пересоздания объекта.
Все методы StringBuffer синхронны. В Java 1.5 ему на замену пришел несинхронизированный вариант StringBuilder. Похоже на HashMap и Hashtable. В остальном эти два класса почти ничем не отличаются, имеют одинаковый набор методов и конструкторов.
Для буфера и билдера не работает синтаксический сахар строк:
Их нельзя создать литералом, вместо этого используется обычный конструктор.
Нельзя конкатенировать +, вместо этого используются обычные методы insert и append.
Сам оператор конкатенации константных выражений, компилируется в интернированную строку, но для не-констант неявно использует StringBuilder String a = “cat”; String b = “ca” + “t”; a == b → true
Exception
Иерархия исключений:
Throwable, RunTime, Error, Exception - classes.
Error - ошибки jvm, исполняемой среды. (Нельзя обработать и восстановиться)
Exception - ошибки которые мы можем обработать, ошибка в коде. (Можно восстановиться)
Хорошей практикой считается использовать не проверяемые исключения. Т.к. проверяемые раскрывают инкапсуляцию(были введены давно и устарели), по комментарию к методу можно легко понять что нужно ловить. Во многих языках применяются только не проверяемые исключения.
Разница между в checked и unchecked что мы обязаны обработать checked или пробросить дальше.
Блок finally может вернуть значение и shadowing исключение, как будто его и не было.
Interface Closable закрывает ресурс при вызове close(). Autocloseable – закрывает автоматически при try with resources.
Stream API
Это средства потоковой обработки данных в функциональном стиле. Для работы с набором данных как с одной сущностью, можно представить в виде конвейера.
Операции:
Source(Источником) - может быть заранее заданный набор данных, или динамический генератор, возможно даже бесконечный.
Intermediate(Промежуточные) - операции модифицируют стрим. Можно вызвать сколько угодно таких операций. Возвращает Stream.
Terminal(Терминальная) - операция выполняет всю цепочку и возвращает значение. Может быть только одна, в конце stream. Стрим не выполнится до вызова терминальной операции.
// Source
Stream.of(myArray)// Создание из массива
myCollection.stream()// Создание из коллекции
Stream.iterate(0, i -> i * i)// Бесконечный стрим квадратов
Stream.generate(()->newRandom().nextInt())// Случайных чисел