Skip to content

+-------------+
| JDK |
| +---------+ |
| | JRE | |
| | +---+ | |
| | |JVM| | |
| | +---+ | |
| +---------+ |
+-------------+

Write once, run anywhere

JDK (Java Development Kit) - набор программ для разработки. JRE, загрузчик кода java, компилятор javac, архиватор jar, генератор документации javadoc и другие утилиты, для разработки.
JRE (Java Runtime Environment) - окружение, необходимое для запуска Java-программ. Включает в себя стандартную библиотеку. В нее входят, базовые пакеты lang, util... и пакеты для работы с различными форматами, базами данных, пользовательским интерфейсом.
JVM (Java Virtual Machine) - Виртуальная машина отвечает за само выполнение кода. Она работает с bytecode (тем, что находится внутри файлов с расширением .class).

JVM

Memory model java (depends on JVM version)

image.png
image.png
Native Memory - вся доступная системная память. ​Heap (куча) - объекты и статические переменные. Содержит все объекты приложения.
Переменные объекта - в heap с объектом.
Статические переменные - в heap с классом.
Eden, S0, S1, Old Generation - Подробнее в главе .
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
public boolean equals(Object o){
if (this == o) return true; //reflect
if (o == null) return false; // null check
// class type check and class casting
if (getClass() != o.getClass()) return false;
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.
@Ovveride
protected Config clone() throws CloneNotSupportedException {
// we need to copy every reference with
// new instance if object is muable
// also each internal link
Config copied = (Config) super.clone();
copied.serUrl(new CustomObject("copied"));
}
По умолчанию «поверхностное копирование» - в объектах будут ссылки на одни и те же сущности поэтому нужно делать “Глубокие копии” где изменяемые данные с вложенной структурой тоже копируются, а не только их ссылки.
Это диктуется соглашением, по которому клон не должен зависеть от оригинала. По контракту клон должен быть другим объектом (!= оригиналу) и не зависеть от оригинала.
Альтернативы (многие считают что более удобные) метода clone - конструктор копирования и паттерн factory method.
public String toString() - getClass().getName() + '@' + Integer.toHexString(hashCode())
public final native void notify()  - возобновляет поток, из которого был вызван метод wait() для того же объекта
public final native void notifyAll() - возобновляет все потоков, из которых был вызван метод wait() для того же объекта. Управление передается одному из этих потоков.
public final void wait() throws InterruptedException - переводит вызывающий поток в состояние ожидания. В этом случае вызывающий поток освобождает монитор, который имеет доступ к ресурсу. Ожидание продолжается до тех пор, пока другой поток, который вошел в монитор, не вызовет метод.
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
interface Converter<F, T> {
T convert(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.

Типы данных, передача в метод, ключевые слова

| Type | byte (8 bits) | java size | range from -2(n-1) to +2(n-1)-1 |
| ------- | ------------- | --------- | ------------------------------------------- |
| byte | 1 | 4 | (-2 in 7) to (2 in 7 - 1) = -128 to 127 |
| short | 2 | 4 | (-2 in 15) to (2 in 15 - 1) |
| int | 4 | 4 | (-2 in 31) to (2 in 31 - 1) |
| long | 8 | 8 | (-2 in 63) to (2 in 63 - 1) |
| float | 4 | 4 | not important |
Want to print your doc?
This is not the way.
Try clicking the ··· in the right corner or using a keyboard shortcut (
CtrlP
) instead.