Переменные, типы данных (примитивные и ссылочные), операторы.
JVM, JRE и JDK — это три главных компонента экосистемы Java, которые связаны между собой по принципу матрешки: JDK включает в себя JRE, а JRE включает в себя JVM.
• Что это: «Движок» или программа, которая исполняет скомпилированный Java-байт-код (.class-файлы).
• Что это: Готовый набор инструментов, необходимый исключительно для запуска уже готовых программ на Java.
• Что это: Полноценный набор для создания программ на языке Java.
Переменная — это именованная ячейка в памяти, в которой хранится какое-то значение. Представь коробку с наклейкой: на наклейке — имя, внутри — содержимое.
| ✅ Можно | ❌ Нельзя |
|---|---|
| age | 2age (не может начинаться с цифры) |
| myName | my-name (дефис запрещён) |
| _count | class (зарезервированное слово) |
| camelCase | CamelCase (так именуют классы, не переменные) |
📌 Соглашение Java: переменные именуются в camelCase — первое слово с маленькой буквы, каждое следующее с большой: firstName, totalPrice, numberOfItems.
Можно сначала объявить переменную, а значение присвоить позже:
int score; // Объявили (пока "пустая")
score = 100; // Присвоили значение⚠️ Важно: локальную переменную (внутри метода) нельзя использовать, пока ей не присвоено значение. Компилятор выдаст ошибку.
Это самые простые, «встроенные» типы. Они хранят одно маленькое значение и не являются объектами.
| Тип | Что хранит | Размер | Пример | Диапазон |
|---|---|---|---|---|
| byte | Целое число | 1 байт | byte b = 100; | от -128 до 127 |
| short | Целое число | 2 байта | short s = 30000; | от -32 768 до 32 767 |
| int | Целое число | 4 байта | int age = 25; | от -2 млрд до +2 млрд |
| long | Большое целое | 8 байт | long population = 8000000000L; | Огромные числа |
| float | Дробное число | 4 байта | float price = 9.99f; | ~7 знаков после запятой |
| double | Дробное число | 8 байт | double pi = 3.14159265; | ~15 знаков после запятой |
| char | Один символ | 2 байта | char letter = 'A'; | Один символ в одинарных кавычках |
| boolean | Логическое значение | 1 бит | boolean isJavaFun = true; | Только true или false |
Всё, что не примитив — это ссылочный тип. Переменная ссылочного типа хранит не само значение, а ссылку (адрес) на объект в памяти.
String name = "Вика";int[] numbers = {1, 2, 3, 4, 5};Scanner scanner = new Scanner(System.in);У каждого примитивного типа есть «обёртка» — ссылочный класс:
| Примитив | Обёртка |
|---|---|
| int | Integer |
| double | Double |
| boolean | Boolean |
| char | Character |
Зачем это нужно? Например, коллекции (списки, мапы) в Java не умеют хранить примитивы — только объекты. Поэтому вместо List<int> пишут List<Integer>. Пока просто запомни, что они есть.
Ссылочная переменная может быть равна null — это значит, что она ни на что не ссылается:
String text = null;
// System.out.println(text.length()); // 💥 Ошибка! NullPointerExceptionПримитивные типы не могут быть null.
// Примитивы: каждая переменная — независимая копия
int a = 10;
int b = a; // b получает копию значения 10
b = 20;
System.out.println(a); // 10 (a не изменился!)
// Ссылочные: переменные указывают на ОДИН объект
int[] arr1 = {1, 2, 3};
int[] arr2 = arr1; // arr2 указывает на тот же массив!
arr2[0] = 99;
System.out.println(arr1[0]); // 99 (изменился и arr1!)Операторы — это символы, которые выполняют действия над значениями.
Работают с числами, как обычная математика.
int a = 10;
int b = 3;
System.out.println(a + b); // 13 (сложение)
System.out.println(a - b); // 7 (вычитание)
System.out.println(a * b); // 30 (умножение)
System.out.println(a / b); // 3 (целочисленное деление! 10/3 = 3, а не 3.33)
System.out.println(a % b); // 1 (остаток от деления: 10 = 3*3 + 1)Важная ловушка! Деление двух int даёт int, дробная часть отбрасывается. Если нужно точное деление, хотя бы одно число должно быть double:
System.out.println(10 / 3); // 3
System.out.println(10.0 / 3); // 3.3333333333333335Как в математике, у операторов есть приоритет. От высшего к низшему:
💡 Совет: если сомневаешься в приоритете — ставь скобки. Код (a + b) c читается лучше, чем a + b c, даже если ты помнишь правила.
Возвращают boolean (true или false). Используются в условиях if.
int x = 5;
int y = 10;
System.out.println(x == y); // false (равно?)
System.out.println(x != y); // true (не равно?)
System.out.println(x > y); // false (больше?)
System.out.println(x < y); // true (меньше?)
System.out.println(x >= 5); // true (больше или равно?)
System.out.println(x <= 3); // false (меньше или равно?)Работают с boolean значениями. Нужны для сложных условий.
boolean isAdult = true;
boolean hasTicket = false;
// && — И (оба условия должны быть true)
System.out.println(isAdult && hasTicket); // false
// || — ИЛИ (хотя бы одно true)
System.out.println(isAdult || hasTicket); // true
// ! — НЕ (инвертирует)
System.out.println(!isAdult); // falseСокращённая запись для изменения значения переменной.
int score = 10;
score += 5; // то же самое, что score = score + 5; → 15
score -= 3; // score = score - 3; → 12
score *= 2; // score = score * 2; → 24
score /= 4; // score = score / 4; → 6
score %= 4; // score = score % 4; → 2int count = 5;
count++; // Увеличить на 1 → 6 (то же, что count += 1)
count--; // Уменьшить на 1 → 5
// Разница между префиксом и постфиксом:
int a = 5;
int b = a++; // b = 5, потом a стал 6 (сначала присвоил, потом увеличил)
int c = 5;
int d = ++c; // c стал 6, потом d = 6 (сначала увеличил, потом присвоил)int age = 20;
String status = (age >= 18) ? "взрослый" : "несовершеннолетний";
System.out.println(status); // взрослыйФормат: условие ? значение_если_true : значение_если_false
Историческая справка: автоупаковка и автораспаковка - одно из главных нововведений JDK 5.
Автоупаковка (autoboxing) - процесс автоматического преобразования из примитивного типа в соответствующий класс обертку.
Автораспаковка (unboxing) - делает ровно обратное к автоупаковке - преобразует класс обертку в примитив. А вот если окажется значение обертки null, то при распаковке будет выброшено исключение NullPointerException.
Автоупаковка происходит: когда присваивают примитиву ссылку на класс обертку:
ДО Java 5:
//ручная упаковка или как это было ДО Java 5.
public void boxingBeforeJava5() {
Boolean booleanBox = new Boolean(true);
Integer intBox = new Integer(3);
// и так далее к другим типам
}после Java 5:
//автоматическая упаковка или как это стало в Java 5.
public void boxingJava5() {
Boolean booleanBox = true;
Integer intBox = 3;
// и так далее к другим типам
}когда передают примитив в аргумент метода, который ожидает обертку:
public void exampleOfAutoboxing() {
long age = 3;
setAge(age);
}
public void setAge(Long age) {
this.age = age;
}Автораспаковка происходит: когда присваиваем классу обертке примитивную переменную:
//до Java 5:
int intValue = new Integer(4).intValue();
double doubleValue = new Double(2.3).doubleValue();
char c = new Character((char) 3).charValue();
boolean b = Boolean.TRUE.booleanValue();
//и после JDK 5:
int intValue = new Integer(4);
double doubleValue = new Double(2.3);
char c = new Character((char) 3);
boolean b = Boolean.TRUE;В случаях с арифметическими операциями. Они применяются только к примитивным типам, для этого нужно делать распаковку к примитиву.
// До Java 5
Integer integerBox1 = new Integer(1);
Integer integerBox2 = new Integer(2);
// для сравнения нужно было делать так:
integerBox1.intValue() > integerBox2.intValue()
//в Java 5
integerBox1 > integerBox2когда передают в обертку в метод, который принимает соответствующий примитив:
public void exampleOfAutoboxing() {
Long age = new Long(3);
setAge(age);
}
public void setAge(long age) {
this.age = age;
}В Java есть примитивные типы данных:
Первое отличие: количество занимаемой памяти: примитивы занимают очень мало, ведь они содержат лишь собственное значение, в то время как объекты могут содержать очень и очень много различных значений: как примитивов, так и ссылок на другие объекты.
Второе отличие: Java —это объектно-ориентированный язык, поэтому в ней все работает через взаимодействие между объектами, и примитивы тут не сильно вписываются (собственно, поэтому Java — это не 100% объектно-ориентированный язык).
Третье, вытекающее со второго: так как Java ориентирована на взаимодействие между объектами, у этих объектов есть много различных механизмов для управления. Например, конструкторы, методы, исключения (которые работают в первую очередь с объектами), и т.д.
Собственно, чтобы примитивы могли как-то ввязаться (работать) в этой объектно ориентированной среде и были придуманы обертки (wrappers) для примитивных типов (Integer, Character, Double, Boolean...)
Примитивные поля хранят свое значение: например, если мы задали int i = 9; поле i хранит значение 9. Когда у нас есть ссылка на объект, это значит, что мы имеем поле со ссылкой на объект, или другими словами — со значением адреса объекта в памяти.
Cat cat = new Cat();
Получается, поля со ссылкой на объект тоже хранят значения, значения адреса памяти. То есть, cat хранит значение адреса объекта new Cat() в памяти. Когда мы передаем параметр в некоторый метод, то происходит копирование его значения.
В случае с примитивом будет копироваться значение примитива. Соответственно, в методе будет вестись работа с копией, при изменении которой оригинал не затронется.
В случае со ссылочным типом будет копироваться значение адреса памяти, соответственно, адрес будет все тем же, как и объект, на который он указывает. И если мы будем менять объект по этой новой ссылке, он будет изменен и для старой (ведь они обе указывают на один и тот же объект).
JVM — Java Virtual Machine — это виртуальная машина, которая запускает Java байткод, предварительно созданный компилятором.
JRE — Java Runtime Environment — по сути, это среда для запуска java-приложений, которая содержит JVM, стандартные библиотеки и другие компоненты для запуска апплетов и приложений, написанных на языке программирования Java.
Другими словами JRE — пакет всего необходимого для запуска скомпилированной Java-программы, но при этом не содержащий инструментов и утилит, таких как компиляторы или отладчики для разработки приложений.
JDK — Java Development Kit — расширенный набор JRE, то есть, среда не только для запуска, но и для разработки java-приложений.
JDK содержит все, что есть в JRE, плюс различные дополнительные инструменты — компиляторы и отладчики, которые нужны для создания приложений на Java (также содержит java-доки).

Как говорилось выше, Java Virtual Machine — это виртуальная машина, которая запускает Java байткод, предварительно созданный компилятором.
То есть, JVM не понимает исходный код Java.
Поэтому сперва происходит компиляция .java файлов, которые после компиляции имеют расширение уже .class и которые и которые представлены в виде того самого байт кода, который понимает JVM.
JVM для каждой OC своя, поэтому получив файлы в байт коде, JVM исполняет его, адаптируя под ОС, на которой это происходит.
Собственно, из-за разных JVM версии JDK (или JRE) отличаются для разных ОС (под каждую из них нужна своя JVM).
Давайте вспомним, как происходит разработка на других языках программирования. Вы разрабатываете программу, затем её код компилируется в машинный код для конкретной ОС, и после вы можете его запускать. Другими словами, под каждую систему вам нужно писать разные версии программы.
В то время как на Java, благодаря двойной обработке кода (компиляция и обработка байт кода JVM), вы можете пользоваться преимуществами кроссплатформенности.
Мы создали однажды код, перекомпилировали его в байткод, перенесли его на любую ОС, и уже местная JVM и запускает код.
Это и есть легендарное свойство Java — пишешь однажды, запускаешь где угодно.

Как я и говорил выше, компилятор преобразовывает Java-код в промежуточный — bytecode (файлы с расширением .java в файлы с расширением .class).
Байткод во многом похож на машинный код, только он использует набор инструкций не реального процессора, а виртуального. При этом он может включать в себя участки, ориентированные на использование JIT-компилятора, оптимизирующего выполнение команд под реальный процессор, на котором запущена программа. JIT-компиляция, называемая ещё компиляцией на лету— это технология, которая увеличивает производительность программы, использующей байткод, через компиляции байткода в машинный или в другой формат во время работы программы.
Как вы могли догадаться, JVM и использует JIT-компилятор, когда запускает байткод.
Давайте взглянем на пример bytecode:

Не слишком читаемо, не так ли? Ну так, это и не для нас инструкция, а для JVM. Вот статья, которая поможет получше разобраться в этом вопросе.
JavaBeans — Java-класс с соблюдением определенных правил. Вот несколько правил для написания JavaBean: Класс должен содержать пустой (без параметров) конструктор с открытым доступом с модификатором доступа public. Этот конструктор дает возможность создавать объект данного класса без лишних проблем (чтобы не было лишней возни с параметрами).
Доступ ко внутренним полям класса осуществляется через методы get и set, которые должны быть стандартными. Например, если поле name, то getName и setName и т. д. Это, в свою очередь, позволяет различным инструментам (фреймворкам) без осложнений автоматически определять и обновлять содержание bean-ов.
Класс должен содержать переопределенные версии методов equals() — hashCode() и toString().
Класс должен быть сериализуемым, то есть должен иметь интерфейс маркер — Serializable или имплементировать интерфейс Externalizable. Это нужно для того, чтобы состояние объекта bean можно надежно сохранять, хранить и восстанавливать.
OutOfMemoryError — одна из критических ошибок во время исполнения программы, связанная с работой виртуальной машины Java (JVM). Вызывается в тех случаях, когда JVM не может выделить объект, потому что на него не хватает памяти, и сборщик мусора не может выделить больше памяти.
Некоторые виды OutOfMemoryError: OutOfMemoryError: Java heap space — объект не может быть размещен в куче Java из-за недостатка памяти. Ошибку может вызвать утечка памяти или то, что размер кучи по умолчанию недостаточен для текущего приложения.
OutOfMemoryError: GC Overhead limit exceeded — из-за того, что объем данных едва помещается в кучу, сборщик мусора работает все время, а программа Java работает очень медленно, и как итог — превышается предел накладных расходов сборщика мусора и приложение падает с данной ошибкой.
OutOfMemoryError: Requested array size exceeds VM limit — указывает на то, что приложение попыталось выделить память для массива, размер которого превышает размер кучи, что, опять же может быть из-за недостаточного количества выделенной памяти по умолчанию.
OutOfMemoryError: Metaspace — в куче закончилось место, выделенное для метаданных (метаданные — это инструкции классов, методов).
OutOfMemoryError: request size bytes for reason. Out of swap space — произошел некоторый сбой при попытке выделения памяти из кучи и как итог — нехватка памяти в куче.
Стек трейс (Stack Trace) — это список классов и методов, которые были вызваны до этого момента приложения.
Вызвать стек трейс в определенной точке приложения можно так:
StackTraceElement[] stackTraceElements =Thread.currentThread().getStackTrace();
Таким образом мы получим массив стек трейс элементов, расположенных в порядке LIFO — Last In First Out.

В Java, как правило, когда говорят о стек трейсе то имеется ввиду стек трейс, который выводится в консоли при возникновении ошибки (или исключения).
Получить стек трейс исключений можно так:
StackTraceElement[] stackTraceElements;
try{
...
} catch (Exception e) {
stackTraceElements = e.getStackTrace();
}Ну и если мы говорим о выводе стек трейса исключения в консоли:
try{
...
} catch (Exception e) {
e.printStackTrace();
}Также, если у нас возникнет ошибка, unchecked исключение или checked, которое мы не обработаем, а только будем пробрасывать, то при падении приложения мы получим автоматически стек трейс исключений в консоли.
Небольшой пример стектрейс исключения в консоли:
