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

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

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Please open Telegram to view this post
VIEW IN TELEGRAM
Ссылочные типы данных в 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
Что выведет код?

public class Task050625 {
public static void main(String[] args) {
Object[] objects = new Object[3];
objects[0] = 10;
objects[1] = "Hello";
objects[2] = new int[]{1, 2, 3};

System.out.println(objects[0] instanceof Number);
System.out.println(objects[1] instanceof CharSequence);
System.out.println(objects[2] instanceof Object);
}
}


#Tasks
Продолжаем выбирать темы для разбора и голосовать за рассмотрение предложенных! 🤓

Голосуем за тему к рассмотрению в эти выходные!

Выбираем новую тему!
(можете предложить что-то из того, что предлагали на прошлой и позапрошлых неделях и что проиграло в голосовании!)

Не стесняемся! ✌️
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое StringBuilder и чем он отличается от String? 🤓

Ответ:

String — неизменяемый (immutable) класс. При конкатенации создается новый объект.

StringBuilder — изменяемый, эффективен для многократных изменений строки, так как не создает новые объекты.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Интеграция Liquibase с другими инструментами

Maven / Gradle

Maven

Добавьте зависимость в pom.xml:
<dependency>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-core</artifactId>
<version>4.25.0</version>
</dependency>


Конфигурация через pom.xml:
<plugin>
<groupId>org.liquibase</groupId>
<artifactId>liquibase-maven-plugin</artifactId>
<version>4.25.0</version>
<configuration>
<changeLogFile>src/main/resources/db/changelog/db.changelog-master.yaml</changeLogFile>
<url>jdbc:postgresql://localhost:5432/mydb</url>
<username>user</username>
<password>pass</password>
</configuration>
</plugin>


Запуск миграций:
mvn liquibase:update


Gradle

Добавьте в build.gradle:
plugins {
id 'org.liquibase.gradle' version '2.2.0'
}

dependencies {
liquibaseRuntime 'org.liquibase:liquibase-core:4.25.0'
liquibaseRuntime 'org.postgresql:postgresql:42.6.0'
}

liquibase {
changeLogFile 'src/main/resources/db/changelog/db.changelog-master.yaml'
url 'jdbc:postgresql://localhost:5432/mydb'
username 'user'
password 'pass'
}


Запуск:
gradle liquibaseUpdate


Spring Boot (автоконфигурация)

Spring Boot автоматически настраивает Liquibase, если он обнаружен в classpath.

Настройки в application.yml:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydb
username: user
password: pass
liquibase:
change-log: classpath:db/changelog/db.changelog-master.yaml
enabled: true
contexts: dev # Фильтрация по контексту


Поведение:
Миграции запускаются при старте приложения.
Можно отключить через spring.liquibase.enabled=false.


Расширенные функции Liquibase

Динамические свойства (property)

Позволяют выносить повторяющиеся значения в переменные.

Пример (YAML):
databaseChangeLog:
- property:
name: default.schema
value: public
- changeSet:
id: 1
author: dev
changes:
- createTable:
tableName: ${default.schema}.users
columns:
- column:
name: id
type: INT


В
liquibase.properties:
default.schema=public


Через командную строку:
liquibase update -Ddefault.schema=public


Контексты (contexts) и фильтрация changeSet’ов

Контексты позволяют выбирать, какие changeSet’ы применять в определённых окружениях.

Пример (YAML):
- changeSet:
id: 2
author: dev
context: "dev,test" # Будет выполнен только в dev и test
changes:
- insert:
tableName: users
columns:
- column:
name: username
value: "test_user"

- changeSet:
id: 3
author: dev
context: "prod" # Только в production
changes:
- createIndex:
tableName: users
indexName: idx_user_email
columns:
- column:
name: email


Как передать контекст?

Через командную строку:
liquibase update --contexts="dev"


В Spring Boot:
spring:
liquibase:
contexts: dev


#Java #middle #Liquibase
Best Practices

Организация changelog-файлов

Рекомендуемая структура:
src/main/resources/db/changelog/
├── db.changelog-master.yaml # Главный файл
├── changes/
│ ├── 001-create-tables.yaml
│ ├── 002-add-indexes.yaml
│ └── 003-insert-data.yaml
└── rollback/
├── 001-rollback.yaml # Ручные откаты


Главный файл (db.changelog-master.yaml):
databaseChangeLog:
- includeAll:
path: db/changelog/changes/
relativeToChangelogFile: true


Почему так?

Удобно управлять версиями.
Легко находить изменения.


Тестирование миграций

Стратегии:
Локальная проверка:
liquibase validate  # Проверка синтаксиса
liquibase update # Применение в test-DB


Интеграционные тесты (JUnit + Testcontainers):
@Testcontainers
@SpringBootTest
class LiquibaseMigrationTest {
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

@Test
void testMigrations() {
// Spring Boot автоматически применит миграции
assertTrue(true); # Если не упало — значит, успешно
}
}


Работа в команде (избежание конфликтов)


Правила:

Именование changeSet’ов:
Используйте id: <дата>-<номер> (например, id: 20240315-1).
Указывайте автора (author: github_username).


Порядок изменений:
Не зависеть от порядка выполнения (например, не предполагать, что таблица A уже существует при создании B).

Использование preConditions:
- changeSet:
id: 20240315-1
author: dev
preConditions:
- tableExists:
tableName: users
changes:
- addColumn:
tableName: users
column:
name: phone
type: VARCHAR(20)


Регулярные обновления:
Перед началом работы выполняйте liquibase update, чтобы получить актуальную схему.

#Java #middle #Liquibase
Что выведет код?

import java.util.Optional;

public class Task060625 {
public static void main(String[] args) {
Optional<String> opt = Optional.ofNullable(null)
.or(() -> Optional.of("Java"))
.map(v -> ((String) v).toUpperCase());

System.out.println(opt.orElse("Default"));
}
}


#Tasks
Продолжаем выбирать темы для разбора и голосовать за рассмотрение предложенных! 🤓

Голосуем за тему к рассмотрению в эти выходные!

Выбираем новую тему!
(можете предложить что-то из того, что предлагали на прошлой и позапрошлых неделях и что проиграло в голосовании!)

Не стесняемся! ✌️
Please open Telegram to view this post
VIEW IN TELEGRAM
Что такое лямбда-выражения в Java? 🤓

Ответ:

Лямбда-выражения в Java — это краткий способ записи анонимных функций, которые можно передавать как аргументы в методы или сохранять в переменные. Они появились в Java 8 и позволяют писать более компактный и читаемый код, особенно при работе с функциональными интерфейсами (интерфейсами с одним абстрактным методом, например, Runnable, Comparator, Predicate).

Синтаксис лямбда-выражения:
(параметры) -> { тело_лямбды }

Если параметр один, скобки () можно опустить.
Если тело состоит из одной строки, фигурные скобки {} и return можно не писать.


#собеседование
Please open Telegram to view this post
VIEW IN TELEGRAM
С 31.05 по 06.06
Предыдущий пост(с 24.05 по 30.05)
Следующая неделя

Воскресный мотивационный пост:
«Ты не программируешь — ты притворяешься, что учишься»

Выбранная голосованием тема:
Архитектурный шаблон MVC в Java Spring: теория, правила, ошибки

Запись встреч:
Многопоточка во всей красе. Часть 1.

Обучающие статьи:
Типы changesets и стратегии развертывания в Liquibase
Откаты (Rollback) и теги в Liquibase
Интеграция Liquibase с другими инструментами

Глубокое изучение типа данных boolean в Java
Ссылочные типы данных в Java

Пост под которым нет поздравлений:
Сегодня каналу исполнился год! 🥳

Авторская статья (которая кому-то не понравилась):

Пагинация, которую начинаешь ненавидеть 😵

Полезные статьи и видео:
Создаём HTTP-сервер на Java NIO
Большой гайд. Пишем микросервисы на Java и Spring Boot, заворачиваем в Docker, запускаем на EKS, мониторим на Grafana

ИСТОРИЯ НЕЙРОСЕТЕЙ - ОТ ПЕРЦЕПТРОНА ДО CHATGPT

Как и всегда, задачи можно найти под тегом - #Tasks, вопросы с собеседований - #собеседование

#memory
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
Настройка OAuth2 для Gmail

Для Gmail предпочтительно использовать OAuth2.

Пример конфигурации:
@Configuration
public class MailConfig {

@Bean
public JavaMailSender javaMailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("smtp.gmail.com");
mailSender.setPort(587);
mailSender.setUsername("your.email@gmail.com");

Properties props = mailSender.getJavaMailProperties();
props.put("mail.smtp.auth", "true");
props.put("mail.smtp.starttls.enable", "true");
props.put("mail.smtp.auth.mechanisms", "XOAUTH2");

// Настройка OAuth2 токена через Google API Client Library
// Подробности: https://developers.google.com/identity/protocols/oauth2
return mailSender;
}
}


Примечание: Зарегистрируйте приложение в Google Cloud Console и используйте библиотеку google-auth-library-oauth2-http для получения accessToken.

Создание сервиса отправки почты

Создадим сервис EmailService для отправки писем.

@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

@Autowired
private TemplateEngine templateEngine;

private static final Logger logger = LoggerFactory.getLogger(EmailService.class);

public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
}

public void sendHtmlEmail(String to, String subject, String htmlBody) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(to);
helper.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
helper.setText(htmlBody, true);
mailSender.send(message);
}

public void sendEmailWithAttachment(String to, String subject, String text, File attachment) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo(to);
helper.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
helper.setText(text);
FileSystemResource file = new FileSystemResource(attachment);
helper.addAttachment(attachment.getName(), file);
mailSender.send(message);
}

@Async
public CompletableFuture<Void> sendSimpleEmailAsync(String to, String subject, String text IMPORTANT: text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
message.setText(text);
mailSender.send(message);
return CompletableFuture.completedFuture(null);
}

public void sendTemplatedEmail(String to, String subject, String username) throws MessagingException {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
Context context = new Context();
context.setVariable("username", username);
String htmlBody = templateEngine.process("email-template", context);
helper.setTo(to);
helper.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
helper.setText(htmlBody, true);
mailSender.send(message);
}
}


Конфигурация асинхронности:
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.initialize();
return executor;
}
}


#Java #middle #on_request #JavaMailSender
Примеры кода

Простой текст:
SimpleMailMessage message = new SimpleMailMessage();
message.setTo("user@example.com");
message.setSubject("Тестовое письмо");
message.setText("Привет из Spring Boot!");
mailSender.send(message);


HTML-письмо:
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo("user@example.com");
helper.setSubject(MimeUtility.encodeText("HTML письмо", "UTF-8", null));
helper.setText("<h1>Привет!</h1><p>Это HTML письмо.</p>", true);
mailSender.send(message);


Вложение:

File file = new File("/path/to/file.pdf");
FileSystemResource resource = new FileSystemResource(file);
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8");
helper.setTo("user@example.com");
helper.setSubject(MimeUtility.encodeText("Письмо с вложением", "UTF-8", null));
helper.setText("Смотри вложение!");
helper.addAttachment("file.pdf", resource);
mailSender.send(message);


Обработка ошибок и логирование

JavaMailSender может выбрасывать исключения, которые нужно обрабатывать.

Основные исключения:
MailAuthenticationException
MailSendException
MessagingException


Пример отлова:
try {
mailSender.send(message);
} catch (MailException e) {
logger.error("Ошибка отправки письма", e);
throw e;
}


Повторные попытки
Используйте @Retryable из Spring Retry:
@Retryable(value = MailException.class, maxAttempts = 3)
public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
mailSender.send(message);
}


Зависимость:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>


Мониторинг и метрики

Для продакшен-приложений полезно отслеживать метрики отправки писем с помощью Spring Actuator:
@Service
public class EmailService {

@Autowired
private JavaMailSender mailSender;

@Autowired
private MeterRegistry meterRegistry;

public void sendSimpleEmail(String to, String subject, String text) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(text);
try {
mailSender.send(message);
meterRegistry.counter("email.sent.success").increment();
} catch (MailException e) {
meterRegistry.counter("email.sent.failure").increment();
throw e;
}
}
}


Зависимость:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>


Безопасность и защита данных

Никогда не храните пароли в открытом виде в git-репозиториях.

Рекомендации:
Используйте переменные окружения или секреты (например, Kubernetes Secrets).
Применяйте Spring Cloud Vault или HashiCorp Vault.
Используйте шифрование с Jasypt.
Настройте OAuth2 для Gmail.


Пример с переменными окружения:
spring.config.import=optional:configserver:http://config-server
spring.mail.password=${MAIL_PASSWORD}


#Java #middle #on_request #JavaMailSender