Java for Beginner
675 subscribers
559 photos
156 videos
12 files
858 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Глубокое изучение типа данных double в Java: сравнение с целочисленными типами

Тип данных double в Java — это примитивный числовой тип, предназначенный для хранения вещественных чисел двойной точности, то есть чисел с плавающей запятой. Он используется везде, где необходима работа с дробями, приближёнными значениями, математическими и научными вычислениями, где целочисленных типов недостаточно.

Размер и назначение

double занимает 8 байт (64 бита) и поддерживает числа с высокой точностью в достаточно большом диапазоне — от ±10⁻³⁰⁸ до ±10³⁰⁸. При этом точность составляет около 15–17 десятичных цифр. Это делает double универсальным для большинства задач, связанных с вещественной арифметикой.


В отличие от int и long, которые могут хранить только целые числа, double может представлять дробные значения. Например, 3.14, -0.0001, 2.71828.

Формат хранения и особенности


Числа типа double хранятся в формате IEEE 754 — стандарте представления чисел с плавающей точкой.

Эти 64 бита делятся на три части:
1 бит знака;
11 бит на экспоненту;
52 бита на мантиссу (значащие цифры).


Такое представление даёт широкий диапазон значений, но не обеспечивает абсолютную точность — дробные значения могут быть представлены с ошибкой округления.


Сравнение с целочисленными типами


byte, short, int, long обеспечивают точную арифметику в рамках своего диапазона. double же — приближённую, и это накладывает ограничения.
Например, выражение 0.1 + 0.2 == 0.3 даст false, потому что 0.1 и 0.2 не имеют точного двоичного представления, и результат чуть отличается от 0.3.
double может без ошибок хранить целые числа, но только до 2⁵³ (около 9 квадриллионов). После этого точность теряется: не каждое целое число может быть представлено уникально.
Попытка точно представить большие целые (long) в double может привести к неожиданным ошибкам.


Автоматическое приведение типов

При работе в выражениях:
Все целочисленные типы (byte, short, int, long) автоматически продвигаются до double, если в выражении участвует double.
Приведение от double к целым типам выполняется явно, с усечением дробной части.


Пример:
int x = 5;
double y = 2.5;
double z = x + y; // x автоматически превращается в double

int i = (int) y; // i = 2, дробная часть отбрасывается


Переполнение и особые значения

В отличие от целочисленных типов, double не оборачивается при переполнении, а уходит в бесконечность:
double x = Double.MAX_VALUE;
System.out.println(x * 2); // Infinity


Также возможны:

NaN (Not a Number) — результат недопустимых операций (например, 0.0 / 0.0)
+Infinity и -Infinity — результат переполнения или деления на ноль

Производительность и использование


Арифметика с double обычно медленнее, чем с целыми числами, особенно на старых процессорах или в JVM без JIT-оптимизаций. В современных системах разница не всегда критична, но при массовых расчётах (for-циклы, обработка массивов) её стоит учитывать.

Когда использовать double, а когда — целые типы

Используйте double, если:

нужно представлять дробные числа;
важна относительная точность, а не абсолютная;
вы работаете с физикой, графикой, научными расчётами.


Выбирайте int или long, если:
работаете с точным счётом (деньги, единицы, индексы);
важна предсказуемость и надёжность арифметики;
нужны точные операции без округлений.


Выбирайте BigDecimal, если:
нужна максимальная точность и надежность расчетов.

Рекомендации

Для точных денежных расчётов не используйте double — он не гарантирует точность. Лучше использовать BigDecimal.
Избегайте == для сравнения double — сравнивайте с допустимой дельтой (например, Math.abs(a - b) < epsilon)
Помните, что double не хранит точных значений, даже для простых дробей вроде 0.1


#Java #для_новичков #beginner #double
Глубокое изучение типа float в Java: сравнение с double и целочисленными типами

Тип float — один из двух примитивных типов с плавающей точкой в Java. Он используется для хранения чисел с десятичной частью и обеспечивает определённый баланс между точностью и потреблением памяти. Несмотря на свою "простоту", float имеет множество нюансов, особенно в сравнении с double и целочисленными типами (int, long и т. д.), и может вести себя неожиданно, если не понимать его природу.

Что такое float в Java

float — это 32-битный (4 байта) тип данных, реализующий стандарт IEEE 754 для представления чисел с плавающей точкой.

Это означает, что число хранится в следующем формате:
1 бит — знак числа
8 бит — экспонента
23 бита — мантисса (дробная часть)


Таким образом, float может хранить числа приблизительно в диапазоне от ±1.4 × 10^-45 до ±3.4 × 10^38 с точностью около 6–7 значащих десятичных цифр.

Чтобы обозначить литерал как float, нужно явно указать f или F:
float pi = 3.1415927f;
Без этого литерал будет воспринят как double по умолчанию, что приведет к ошибке компиляции при попытке неявного присваивания.


Сравнение с double

double — это 64-битный тип, также реализующий IEEE 754, но имеющий:
1 бит для знака
11 бит для экспоненты
52 бита для мантиссы

Он способен хранить числа от ±4.9 × 10^-324 до ±1.7 × 10^308, с точностью около 15–16 значащих цифр.

То есть:
float — быстрее, но менее точен, занимает меньше памяти
double — точнее, но требует больше памяти и может быть чуть медленнее в вычислениях на некоторых архитектурах


В реальной практике предпочтение обычно отдают double, особенно в финансовых, статистических или инженерных вычислениях, где важна точность. float чаще применяется в графике (например, координаты вершин), машинном обучении, играх и устройствах с ограниченными ресурсами (встраиваемые системы, Android до определённых API-уровней).

Сравнение с целочисленными типами

Целочисленные типы (byte, short, int, long) хранят точные значения и не допускают погрешностей. Они идеальны для подсчётов, индексов, флагов, битовых масок и всего, что не связано с дробями.
В отличие от них, float и double — не точные типы.


Это означает:
Результаты вычислений могут быть неточными из-за ограниченной точности представления дробных чисел.
Сравнение значений на равенство (==) — рискованно и почти всегда плохая идея.


Простые на вид операции могут давать неожиданный результат:
float a = 0.1f + 0.2f;
System.out.println(a == 0.3f); // false
Это связано с тем, что не все десятичные дроби можно точно представить в двоичной системе.


Работа с памятью и производительность

Обе переменные — float и double — примитивные типы и, следовательно, при размещении в стеке (например, внутри метода) не требуют участия сборщика мусора. Они быстро выделяются и удаляются вместе с фреймом стека. Однако, если переменные — поля объекта, то они хранятся в куче, и их "жизненный цикл" зависит от объекта.

С точки зрения производительности:
На современных процессорах разница между float и double минимальна.
Некоторые GPU и встраиваемые процессоры всё ещё используют float как основной тип с плавающей точкой.
На JVM оба типа оптимизируются, но float может быть чуть быстрее при большом объеме операций и памяти.


#Java #для_новичков #beginner #float
Особенности и подводные камни

Погрешность и потеря точности
Каждое присваивание или операция с float может сопровождаться потерей точности. Например:
float a = 1_000_000;
float b = a + 0.0001f;
System.out.println(a == b); // true — потерялась дробная часть


Нормализованные и денормализованные числа

float поддерживает очень маленькие значения, но при этом точность сильно страдает. Денормализованные значения позволяют представлять числа ближе к нулю, но с меньшей точностью.

NaN, Infinity и -Infinity

float поддерживает специальные значения:
Float.NaN — результат недопустимых операций (например, 0.0f / 0.0f)
Float.POSITIVE_INFINITY и Float.NEGATIVE_INFINITY — результат переполнения или деления на 0
Эти значения не вызывают исключений, и с ними можно работать, но это требует осторожности.


Сравнение на равенство

Из-за округлений не следует использовать == для сравнения двух float.


Вместо этого используют допустимую погрешность:
float a = 0.1f + 0.2f;
float b = 0.3f;
if (Math.abs(a - b) < 1e-6) {
System.out.println("Равны с учетом погрешности");
}


Приведение типов

При смешанных операциях с float и целочисленными типами Java автоматически приводит меньший тип к float.

Например:
int x = 3;
float y = 2.5f;
float result = x + y; // x преобразован в float
Это не вызывает проблем, но может повлиять на точность, если целое число очень большое.


Двоичное представление и неожиданное округление

Некоторые десятичные дроби (например, 0.1, 0.2) не могут быть точно представлены в двоичной системе. Это приводит к накапливающимся погрешностям, особенно при работе с циклами или большими массивами данных.


Когда использовать float, а когда — double


Используй float, если:
Работаешь в среде с ограниченной памятью или производительностью (например, Android, микроконтроллеры)
Требуется снизить объем данных (например, передача координат в 3D-движке)
Максимальная точность не критична


Используй double, если:
Точность важна (финансовые расчеты, физические симуляции)
Объёмы данных позволяют использовать больше памяти
Не хочешь постоянно контролировать потерю точности


#Java #для_новичков #beginner #float
Глубокое изучение типа данных char в Java

Тип char в Java — это единственный примитивный тип, предназначенный для хранения символов. Однако под «символом» в контексте Java подразумевается не просто буква или цифра, а 16-битное значение, соответствующее одному коду Unicode. Это делает char более универсальным, чем аналогичные типы в других языках (например, char в C/C++ — это просто байт).

Тем не менее, с учетом особенностей кодировки Unicode, поведения в выражениях и сравнении с другими примитивами, char — не такой уж простой тип.

Что представляет собой char

В Java char — это целое беззнаковое значение, хранящееся в 16 битах (2 байта). Диапазон значений — от 0 до 65 535. Это соответствует возможным значениям Unicode Code Units, используемым для представления символов в кодировке UTF-16.

В отличие от byte, short, int и других числовых типов, char:
Не поддерживает отрицательные значения.
Не используется в арифметике напрямую (но может быть преобразован в int).
Представляет не число, а код символа, хотя технически это просто целое значение.


Хранение в памяти

Как и все примитивные типы, char хранится в стеке, если используется как локальная переменная, и в куче — если является полем объекта. В обоих случаях он занимает ровно 2 байта (16 бит).

Символ и кодировка Unicode

Java изначально проектировалась с поддержкой международного текста, поэтому char хранит значение по спецификации Unicode. Однако важно понимать: char хранит одну кодовую единицу UTF-16, а не обязательно один графический символ.

Некоторые символы Unicode (например, эмодзи или древние алфавиты) имеют коды за пределами 16 бит. Такие символы представлены в UTF-16 двумя char, известными как суррогатная пара.
Это значит, что один char — это не всегда "один символ", что может привести к ошибкам при разборе строк, итерации по символам или определении длины строки.

Пример:
String s = "😀";
System.out.println(s.length()); // 2, а не 1
Здесь length() возвращает 2, потому что символ представлен двумя char.


Экранирование символов

Литералы char записываются в одиночных кавычках:
char c = 'A';


Java поддерживает экранированные символы, например:
'\n' — перевод строки
'\t' — табуляция
'\'' — апостроф
'\\' — обратный слеш
'\u0041' — Unicode-последовательность (в данном случае это символ 'A')


Unicode-последовательности (\uXXXX) можно использовать даже в имени переменной или в коде, до начала компиляции — они обрабатываются на уровне лексического анализатора, еще до построения синтаксического дерева.

#Java #для_новичков #beginner #char
Поведение в выражениях и преобразования

Несмотря на то, что char — это не числовой тип, он может участвовать в арифметике. В любых выражениях с участием char он неявно преобразуется в int.
char c = 'A';
int x = c + 1; // x == 66


Но обратное требует явного приведения:
char next = (char)(c + 1); // 'B'
Аналогично, попытка присвоить результат арифметики напрямую переменной типа char вызовет ошибку компиляции без приведения.


Сравнение с другими примитивными типами

В отличие от byte, short, int, char — беззнаковый.
Он занимает 2 байта, как short, но short — знаковый (-32 768 до 32 767), а char — 0 до 65 535.
В арифметике char ведет себя как int, автоматически повышая тип.
В отличие от String, char представляет одну кодовую единицу, а не последовательность символов.
В отличие от boolean, char может участвовать в арифметических выражениях и преобразованиях.


Инициализация и значения по умолчанию

Локальные переменные char должны быть явно инициализированы.
Поля классов по умолчанию получают значение \u0000, что соответствует нулевому символу Unicode (не отображается в консоли).


Частые ошибки и подводные камни

Суррогатные пары. При работе со строками, содержащими символы за пределами BMP (Basic Multilingual Plane), важно использовать методы вроде codePointAt() и Character.toChars(), чтобы не потерять часть символа.
Арифметика с char. Все выражения приводятся к int, что может привести к неожиданному переполнению или необходимости явного приведения.
Сравнение символов. Операции c1 < c2 работают корректно, но сравниваются числовые значения Unicode, а не "лексикографическое положение" в каком-либо языке.
Обработка управляющих символов. Символы вроде '\0', '\n', '\r' видны в коде, но не обязательно видны в выводе — их эффект зависит от среды (консоль, IDE, файл и т.д.).


#Java #для_новичков #beginner #char
Распространённые ошибки

1. Логика в контроллерах
Признак: контроллер становится "сервисом", содержащим условия, циклы, доступ к БД. Это нарушает принципы чистой архитектуры и усложняет тестирование.

2. Использование Entity в представлении
Передача @Entity напрямую в шаблон может привести к:
Утечке данных (например, паролей).
Ошибкам LazyInitializationException.
Сильной связанности представления с базой данных.
Решение: использовать DTO или ViewModel.


3. Жёсткая связность между слоями
View не должно зависеть от Repository, а Controller — от Entity. Каждый слой должен взаимодействовать только с соседним.

4. Отсутствие DTO
Использование одной и той же модели во всех сценариях ведёт к путанице и проблемам безопасности. Лучше использовать отдельные классы:
UserCreateRequest
UserResponse
UserUpdateRequest


Рекомендации по проектированию

Структура проекта

Хорошей практикой является разделение кода по слоям:
com.example.myapp
├── controller
├── service
│ └── impl
├── repository
├── dto
├── model
├── config


URL-дизайн

Соблюдайте RESTful-стиль:
GET /users — получить список пользователей.
GET /users/{id} — получить конкретного пользователя.
POST /users — создать.
PUT /users/{id} — обновить.
DELETE /users/{id} — удалить.


Использование DTO
public class UserResponse {
private Long id;
private String name;
}


public class UserCreateRequest {
private String name;
private String email;
}


Расширения и адаптации MVC

SPA + API
При использовании Vue, React или Angular, представление полностью переносится на фронтенд. В этом случае Spring работает как REST API с @RestController, и классическая схема MVC трансформируется в «REST + JSON».

Поддержка реактивности
Spring WebFlux реализует неблокирующую модель с Mono и Flux, сохраняя при этом логическую структуру MVC. Подходит для высоконагруженных и асинхронных приложений.

Тестирование компонентов MVC


Контроллеры — @WebMvcTest, MockMvc.
Сервисы —
@SpringBootTest или с моками (@MockBean).
Репозитории —
@DataJpaTest.

#Java #для_новичков #beginner #on_request #mvc
Глубокое изучение типа данных boolean в Java

Тип boolean — самый компактный и в то же время наименее числовой из всех примитивных типов в Java. Он представляет логические значения true или false и лежит в основе всех условных конструкций языка. Несмотря на простоту, поведение boolean в Java имеет ряд важных нюансов, связанных с его представлением в памяти, взаимодействием с другими типами, ограничениями и поведением на уровне JVM.

Что представляет собой boolean

Тип boolean может принимать только два значения: true и false. Он не является числовым типом и не участвует в арифметике. Java строго типизирована, поэтому невозможно, как в C или Python, неявно преобразовать boolean в число или наоборот. Это ограничение сделано для повышения читаемости и безопасности кода.

Например, такой код в Java недопустим:
int x = true; // Ошибка компиляции
if (1) { ... } // Тоже ошибка


Хранение в памяти

На уровне языка boolean воспринимается как логическое значение, но в памяти JVM его представление зависит от контекста:
При хранении в массиве или в виде поля класса, boolean может занимать целый байт или даже больше, в зависимости от выравнивания и архитектуры. В спецификации Java не указано точное количество битов, которое должен занимать boolean, только то, что он представляет true/false.

В локальных переменных boolean может быть оптимизирован компилятором и размещен в стеке наряду с другими переменными. Однако фактический объем занимаемой памяти определяется JVM и может отличаться от теоретически минимального.

На уровне байткода логические значения часто реализуются как int со значением 0 или 1, но это скрыто от разработчика и не влияет на язык напрямую.

Инициализация и значение по умолчанию

Локальные переменные типа boolean не инициализируются автоматически — попытка использовать их без явного присваивания вызовет ошибку компиляции.
Поля классов и массивов по умолчанию получают значение false.


Операции с boolean

Java предоставляет классический набор логических операций:
!a — логическое отрицание (NOT)
a && b — логическое И (AND, с коротким замыканием)
a || b — логическое ИЛИ (OR, с коротким замыканием)
a ^ b — исключающее ИЛИ (XOR)
Кроме того, возможны сравнения (==, !=) между значениями типа boolean, но не упорядоченные сравнения (<, >, <=, >=) — они не имеют смысла и запрещены.
В отличие от числовых типов, boolean не участвует в операциях сложения, вычитания или побитовых вычислений. Java запрещает такие действия на уровне компилятора.


Сравнение с другими примитивами

boolean — единственный логический тип в Java. Нет аналогов bit, flag, bool8, bool32 и т. д.
Он не является числом. В то время как char, byte, short, int, long, float и double можно свободно преобразовывать друг в друга, boolean изолирован от них.
Это делает boolean более строго типизированным и безопасным, но менее гибким, если требуется, например, сериализация логики в числовом формате.


#Java #для_новичков #beginner #boolean
Типизация и ограничения

Нельзя привести boolean к int или обратно. Даже явно: (int) true — ошибка.
Нельзя использовать boolean в качестве индекса массива или как аргумент методов, ожидающих число.
Нет Boolean.parseInt(), как у числовых типов — только Boolean.parseBoolean() с String.


Упаковка и объектный аналог

Для работы с boolean как объектом существует класс-обертка Boolean. Он используется при работе с коллекциями (List<Boolean>) или API, которые требуют объектов.

Как и другие обертки, Boolean поддерживает автоупаковку и автораспаковку:
Boolean obj = true;     // упаковка
boolean val = obj; // распаковка
Также есть кэширование значений: Boolean.TRUE и Boolean.FALSE — это единственные экземпляры класса Boolean, которые обычно и используются.


Подводные камни и особенности

Нет арифметики. Переход с языков вроде C, где true — это 1, а false — это 0, может вызвать ошибки. Java не допускает смешивания логики и чисел.
Отсутствие побитовых операций. Нельзя использовать & или | на boolean как побитовые операторы. Да, есть &, | и ^, но они действуют как логические, без короткого замыкания — в отличие от && и ||.
Нет поддержки масок и битов. Если требуется управлять отдельными битами, используют int или byte, а не boolean[].
Выражения с boolean всегда проверяются как логические. Даже if (a & b) — это проверка boolean, а не побитовая операция над числами.
Интерфейсы и структуры не позволяют оптимизировать boolean до одного бита. Например, boolean[] всегда тратит 1 байт (или больше) на элемент, а не один бит, как хотелось бы при экономии памяти. Для работы с компактными флагами используют BitSet или битовые маски на базе int.


#Java #для_новичков #beginner #boolean
Ссылочные типы данных в Java

Ссылочные типы данных (reference types) являются неотъемлемой частью объектно-ориентированной модели Java. В отличие от примитивов, они представляют объекты и хранят ссылки (адреса) на область памяти, где находится фактическое содержимое.

1. Концепция и философия


Java — строго объектно-ориентированный язык, и ссылки — основа его динамической природы.

Зачем нужны ссылочные типы:

Инкапсуляция данных и поведения. Объекты объединяют данные и методы, действующие над ними.
Гибкость. Ссылочные типы позволяют моделировать сложные структуры (например, графы, деревья, коллекции).
Полиморфизм и наследование. Через ссылки возможна работа с объектами по интерфейсам и абстрактным типам.


List<String> names = new ArrayList<>(); // ссылка на объект ArrayList


2. Представление и поведение ссылок

Ссылочная переменная не содержит сам объект, а только указатель на него.

String a = "Hello";
String b = a;
Обе переменные a и b ссылаются на один и тот же объект "Hello" в куче (heap). Изменение объекта (если он мутабелен) через одну ссылку отразится на другой.


Пример с мутабельным объектом:
List<Integer> list1 = new ArrayList<>();
List<Integer> list2 = list1;
list1.add(10);
System.out.println(list2); // [10]


3. Классы, интерфейсы и массивы

К ссылочным типам относятся:

Классы (например, String, Object, Scanner, ArrayList)
Интерфейсы (например, List, Runnable, Serializable)
Массивы (int[], String[], Object[][]) — в Java массивы — это объекты!
Специальный тип null — означает отсутствие объекта


4. Работа JVM с ссылками

При работе со ссылочными типами:
Объекты размещаются в куче (heap).
Ссылки могут храниться в стеке вызовов, в полях объектов, в массиве и т.д.
JVM использует сборку мусора (GC) для очистки неиспользуемых объектов.


class User {
String name;
}

User user1 = new User();
User user2 = user1;
user1 = null; // объект всё ещё доступен через user2


5. Особенности ссылочных типов

a) Сравнение ссылок
String a = new String("Java");
String b = new String("Java");
System.out.println(a == b); // false — разные объекты
System.out.println(a.equals(b)); // true — сравнение содержимого
== сравнивает ссылки, а equals() — содержимое (если метод переопределён корректно).


b) NullPointerException
String s = null;
System.out.println(s.length()); // исключение
Поэтому рекомендуется использовать Objects.requireNonNull, Optional, или делать явные проверки на null.


6. Автоматическая работа с памятью


Одно из главных преимуществ Java — автоматическое управление памятью. Не нужно вручную освобождать объекты, как в C/C++.

Но важно помнить:
Объекты "висят" в памяти, пока есть активные ссылки.
Циклические ссылки не мешают сборке мусора (в отличие от простых reference counting-систем).


7. Массивы как ссылочные типы

Массивы в Java — полноценные объекты.

int[] nums = {1, 2, 3};
System.out.println(nums.length); // 3
Массивы имеют поля и методы (length — поле, а не метод), и их тип — int[], String[] и т.д.


8. Использование в обобщениях

Ссылочные типы активно применяются в дженериках (обобщениях).

List<Integer> list = new ArrayList<>();


Нельзя использовать примитивы в параметрах типа:
List<int> wrongList; // ошибка
Поэтому применяются классы-обёртки: Integer, Double, Boolean и т.д.


9. Влияние на производительность

Работа со ссылками может быть дороже, чем с примитивами:
Дополнительные обращения к памяти
Участие в GC
Наличие виртуального вызова методов (vtable)
Частые создания объектов (например, при упаковке/распаковке)


Оптимизации:
Избегать ненужных аллокаций
Использовать StringBuilder вместо + в циклах
Применять ObjectPool, если объекты часто переиспользуются


#Java #для_новичков #beginner #reference_types
Объекты в Java

Объекты являются фундаментальным понятием в Java и лежат в основе всей объектно-ориентированной парадигмы языка. Каждый объект представляет собой экземпляр класса и объединяет в себе состояние (поля) и поведение (методы).

1. Введение

В Java всё, что не является примитивным типом, относится к ссылочным типам, а основные единицы этих типов — это объекты.

Объект-ориентированная модель основана на трёх ключевых принципах:
Инкапсуляция: объединение данных и методов для управления ими.
Наследование: возможность создавать новые классы на основе уже существующих, унаследовав их состояние и поведение.
Полиморфизм: возможность работать с объектами через их общий (абстрактный) тип, подставляя разные конкретные реализации.


Вся динамика и гибкость Java-приложений строится на том, что объекты создаются во время выполнения программы, передаются по ссылке, могут образовывать сложные графы связей и автоматически удаляться сборщиком мусора.

2. Создание объектов

2.1. Ключевое слово new

Самый распространённый способ создания объекта — использование оператора new, который выполняет два основных действия:
Выделяет память в куче (heap) под новый объект.
Вызывает конструктор соответствующего класса, чтобы инициализировать поля объекта.


Person person1 = new Person("John", 25);


Здесь:
new Person("John", 25) — порождает новый объект класса Person, вызывая конструктор Person(String name, int age).
Полученная ссылка на вновь созданный объект присваивается переменной person1 типа Person.
Переменная person1 не содержит непосредственно самого объекта, а лишь указывает на область памяти, где объект расположен.


2.2. Фабричные методы и другие способы

Кроме new, объекты могут создаваться через:
Фабричные методы (static factory methods), например, List.of(...) или Optional.of(...).
Клонирование (когда класс поддерживает интерфейс Cloneable и реализует метод clone()).
Десериализация (с помощью API сериализации или при работе с JSON/XML).
Рефлексия (пересоздание экземпляра через Class.newInstance() или Constructor.newInstance()).

Однако в подавляющем большинстве случаев для явного создания объекта используется именно new.


2.3. Размещение ссылок и объектов

Объекты всегда размещаются в куче (heap).

Ссылочные переменные (person1 в примере) могут храниться:
В стеке вызовов (если это локальная переменная метода).
В полях других объектов (если объект содержится в качестве поля другого объекта).
В элементах массива (если это массив ссылок).
В статических полях классов (в специальной области памяти, связанной с загрузчиком классов).


3. Существование и удаление объектов

3.1. Существование объектов

Объект продолжает «жить» в памяти до тех пор, пока на него существует хотя бы одна активная ссылка.


Пример:
Person person1 = new Person("John", 25);
Person person2 = person1; // person2 ссылается на тот же самый объект
person1 = null; // теперь единственная ссылка на объект — person2


В момент создания объекта через new в куче появляется новый экземпляр Person.
Переменные person1 и person2 указывают на одну и ту же область памяти.
Когда мы присвоили person1 = null;, объект всё ещё существует, поскольку на него ссылается person2.
Как только все ссылки будут убраны (например, person2 = null; или метод, в котором была локальная ссылка, завершится и стек «очистится»), объект становится недостижимым.

Java применяет алгоритм mark-and-sweep для сборки мусора, поэтому даже если два объекта ссылаются друг на друга, но на них никто извне не ссылается, они будут помечены как недостижимые и удалены.

#Java #для_новичков #beginner #reference_types #Object