Java | Фишки и трюки
7.21K subscribers
182 photos
29 videos
6 files
40 links
Java: примеры кода, интересные фишки и полезные трюки

Купить рекламу: https://telega.in/c/java_tips_and_tricks

✍️По всем вопросам: @Pascal4eg
Download Telegram
⌨️ java.time API (Современные Дата и Время)

Старый API даты/времени был неудобным и изменяемым (mutable). Новый пакет java.time (LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Duration, Period) решает эти проблемы!


import java.time.LocalDate;
import java.time.Month;
import java.time.temporal.ChronoUnit;

LocalDate today = LocalDate.now();
LocalDate birthday = LocalDate.of(2024, Month.DECEMBER, 31);
LocalDate nextWeek = today.plus(1, ChronoUnit.WEEKS); // Неизменяемый! Возвращает новый объект

System.out.println("Сегодня: " + today);
System.out.println("День рождения: " + birthday);
System.out.println("Через неделю: " + nextWeek);
// Вывод:
// Сегодня: 2023-10-27 (пример)
// День рождения: 2024-12-31
// Через неделю: 2023-11-03 (пример)


Используйте современный, потокобезопасный и интуитивно понятный API!
#java #datetime #java8 #javatime #api #bestpractice
Please open Telegram to view this post
VIEW IN TELEGRAM
👍121🔥1👏1
⌨️ Дайте определение понятию «интерфейс». Какие модификаторы по умолчанию имеют поля и методы интерфейсов?

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

Начиная с Java 8 в интерфейсах разрешается размещать реализацию методов по умолчанию default и статических static методов.

Интерфейс также может содержать и поля. В этом случае они автоматически являются публичными public, статическими static и неизменяемыми final.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍142
💎Unicode-хаки и комментарии-невидимки в Java 
Java поддерживает Unicode во всех частях кода, включая комментарии и имена переменных. Это открывает неочевидные возможности для экспериментов, но требует осторожности в production-коде.

💠1. Исполняемые комментарии через Unicode 

// \u000d System.out.println("Этот код выполнится!");

После компиляции символ \u000d превращается в перенос строки, и код становится: 

//
System.out.println("Этот код выполнится!");

Применение: 
▸ Демонстрация скрытых уязвимостей в презентациях 
▸ Образовательные эксперименты с компиляцией 
▸ Технические розыгрыши (только для небоевых проектов!) 

💠2. Кириллические идентификаторы 

int размер = 10; // переменная "размер"
System.out.println(размер); // 10

💠Плюсы: 
▸ Локализация кода для образовательных проектов 
▸ Поддержка специфических терминов на национальных языках 

💠3. Графика через Unicode-символы 

System.out.println("\u2591\u2592\u2593"); // Вывод символов псевдографики: ░ ▒ ▓ (разные уровни заливки)
System.out.println("\u265A \u265B"); // Вывод юникод-символов: ♚ ♛ (шахматные фигуры короля и королевы)

💠Практическое применение: 
▸ Консольные интерфейсы с псевдографикой 
▸ Визуализация данных в текстовом режиме 
▸ Генерация ASCII-арта 

💠4. Экранирование спецсимволов 

String regex = "\\p{So}"; // Шаблон для эмодзи и символов
String text = "Alert! ⚠️";
System.out.println(text.replaceAll(regex, "[символ]")); // Alert! [символ]


⚠️ Важные предупреждения: 
▸ Код с Unicode-трюками не проходит код-ревью в серьезных проектах 
▸ Может вызывать проблемы с линтерами и статическими анализаторами 
▸ Затрудняет поиск в кодовой базе (например, \u000d

💠Альтернативы для безопасного использования: 

// Локализация через ResourceBundle
ResourceBundle bundle = ResourceBundle.getBundle("Messages_ru"); // Загрузка ресурсов для локали ru
String message = bundle.getString("welcome"); // Получение локализованной строки по ключу
Please open Telegram to view this post
VIEW IN TELEGRAM
👍81
💎ThreadLocal— потокобезопасное хранилище данных 

ThreadLocal позволяет хранить данные, уникальные для каждого потока, без использования синхронизации. Это особенно полезно в многопоточных приложениях, где необходимо сохранять состояние для конкретного потока.

💠1. Пример

private static final ThreadLocal<DateFormat> dateFormat =
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));

void formatDate(Date date) {
    System.out.println(dateFormat.get().format(date)); // У каждого потока свой экземпляр
}

💠Преимущества: 
▸ Избегает создания нового объекта для каждого вызова 
▸ Гарантирует потокобезопасность без блокировок 

💠2. Контекст безопасности в веб-приложениях 

public class SecurityContext {
    private static final ThreadLocal<User> currentUser = new ThreadLocal<>();

    public static void login(User user) {
        currentUser.set(user);
    }

    public static User getCurrentUser() {
        return currentUser.get();
    }

    public static void logout() {
        currentUser.remove();
    }
}

// В фильтре сервлета
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
    SecurityContext.login((User) req.getAttribute("user"));
    try {
        chain.doFilter(req, res);
    } finally {
        SecurityContext.logout();
    }
}


💠3. Локальный кэш для тяжелых вычислений 

private static final ThreadLocal<Map<String, String>> cache =
    ThreadLocal.withInitial(HashMap::new);

void processData(String key) {
    if (!cache.get().containsKey(key)) {
        cache.get().put(key, expensiveOperation(key));
    }
    System.out.println(cache.get().get(key));
}


⚠️ Опасности: 
▸ Утечки памяти в пулах потоков (например, Tomcat) 
▸ Неожиданное поведение при повторном использовании потоков 
▸ Сложность отладки из-за неявной передачи данных 

💠Практические кейсы: 
▸ Контекст пользователя в веб-приложениях 
▸ Локальный кэш для тяжелых вычислений 
▸ Параметры локали и форматирования дат
Please open Telegram to view this post
VIEW IN TELEGRAM
👍82🎉1
⌨️ Суть лямбда-выражений

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

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

Если говорить ещё проще, лямбда-выражение — это просто другой способ создания и реализации объекта определённого типа. Рассмотрим это на примере создания нового потока.

У класса Thread есть конструктор:

public Thread(Runnable target) {
...
}


То есть в конструктор нужно передать объект типа Runnable. До лямбда-выражений мы сделали бы так:

new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
}).start();

Здесь мы создаём анонимный класс, реализующий интерфейс Runnable, с определённым методом run.
Если использовать лямбда-выражение, тот же код будет выглядеть следующим образом:

Runnable r = () -> System.out.println("Hello World");
new Thread(r).start();

Или проще:

new Thread(() -> System.out.println("Hello World")).start();


Лямбда-выражение заменяет собой анонимный класс, который раньше был бы необходим для реализации Runnable. Лямбда-выражение может использоваться только там, где ожидается реализация функционального интерфейса — интерфейса с единственным абстрактным методом.
А интерфейс Runnable именно такой:

@FunctionalInterface
public interface Runnable {
public abstract void run();
}


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

#java #lambda #Runnable
Please open Telegram to view this post
VIEW IN TELEGRAM
👍145🔥2🤣2
☕️Checked vs Unchecked исключения

🔹Исключения делятся на проверяемые (checked) и непроверяемые (unchecked).

🔹Checked исключения требуют обязательной обработки или объявления через throws. Обычно связаны с внешними ресурсами: файлами, сетью.

🔎Пример:

public void readConfigFile() {
try (FileInputStream file = new FileInputStream("config.txt")) {
int data = file.read();
System.out.println("Данные: " + data);
} catch (FileNotFoundException e) {
System.err.println("Файл не найден!");
} catch (IOException e) {
System.err.println("Ошибка ввода-вывода: " + e.getMessage());
}
}


🔹Unchecked исключения — наследники RuntimeException. Не требуют обработки, сигнализируют о логических ошибках.

🔻 Пример:

public void printLength(String text) {
if (text == null) throw new IllegalArgumentException("Строка не может быть null!");
System.out.println("Длина: " + text.length());
}



public int divide(int a, int b) {
if (b == 0) throw new ArithmeticException("Деление на ноль!");
return a / b;
}


📈Советы:
1. Для checked используйте try или try-with-resources.
2. Для unchecked проверяйте входные данные.
3. Документируйте исключения через
@throws.

🔺Checked — для внешних ошибок, Unchecked — для внутренних багов.
Please open Telegram to view this post
VIEW IN TELEGRAM
13🤝3👍1
☕️GraphQL в Java позволяет клиентам запрашивать только нужные данные, избегая избыточности REST. Пример на Spring Boot:

🔹Схема (schema.graphqls):
type Vehicle {
id: ID!
type: String!
modelCode: String!
}

type Query {
vehicles: [Vehicle!]!
}


🔻Контроллер:
@Controller
public class VehicleController {
@QueryMapping
public List vehicles() {
return List.of(new Vehicle("1", "bus", "XYZ123"));
}
}


💠Пример запроса:
{
vehicles {
id
type
}
}


🔸Ответ:
{
"data": {
"vehicles": [{ "id": "1", "type": "bus" }]
}
}


🔺Для интеграции с БД используйте Spring Data JPA и Querydsl:
public interface VehicleRepo extends JpaRepository, QuerydslPredicateExecutor {}


🔹Maven зависимости:

org.springframework.boot
spring-boot-starter-graphql


com.querydsl
querydsl-jpa


📈Советы:
1. Включите GraphiQL для интерактивных запросов.
2. Используйте
@MutationMapping для мутаций.
3. Настройте кеширование через CacheControl.

🔺GraphQL позволяет запрашивать только нужные поля, снижая нагрузку на сеть.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍141
☕️Spring Data Envers помогает автоматически отслеживать изменения сущностей в базе данных с помощью Hibernate Envers.

🔺Пример сущности с аудитом:
@Entity  
@Audited
public class Post {
@Id @GeneratedValue
private Long id;
private String title;
private String slug;
}


🔺Репозиторий с поддержкой ревизий:
public interface PostRepository extends JpaRepository, RevisionRepository { }  


💠Доступны методы findRevisions() и findLastChangeRevision() для получения истории изменений.
🔻Envers создает таблицы _AUD и REVINFO для хранения версий и метаданных. При изменениях данные копируются в _AUD с типом операции (INSERT, UPDATE, DELETE).


Пример использования:
for (Revision rev : postRepository.findRevisions(postId)) {  
System.out.println("Версия " + rev.getRevisionNumber() + ": " + rev.getEntity());
}


📈Советы:
1. Добавьте зависимости hibernate-envers и spring-data-envers.
2. Включите
@EnableJpaAuditing в конфигурации.
3. Для расширенного аудита используйте
@CreatedBy и @LastModifiedBy.

🔸Метод findRevisions() возвращает все версии сущности — от создания до удаления.
Please open Telegram to view this post
VIEW IN TELEGRAM
3👌2
☕️Spring WebFlux — реактивный фреймворк для асинхронных веб-приложений в Spring 5. Он работает на серверах Netty, Undertow или Servlet 3.1+ и использует Project Reactor для обработки потоков данных без блокировок. Это позволяет обрабатывать тысячи соединений с минимальным числом потоков, что важно для высоконагруженных систем.

🎯Пример контроллера:
@RestController  
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public Mono getUser(@PathVariable Long id) {
return userRepository.findById(id);
}
@GetMapping("/{id}/orders")
public Flux getUserOrders(@PathVariable Long id) {
return userRepository.findById(id)
.flatMapMany(orderRepository::findByUser);
}
}


📇WebClient для неблокирующих HTTP-запросов:
WebClient.create("http://service.com")  
.get()
.uri("/data")
.retrieve()
.bodyToFlux(Data.class)
.subscribe(System.out::println);


📈Советы:
1. Используйте WebFlux для микросервисов с высокой нагрузкой.
2. Комбинируйте с R2DBC для реактивного доступа к БД.
3. Избегайте блокирующих вызовов (JDBC, JPA) в реактивных цепочках.


💠«WebFlux обрабатывает больше запросов с меньшим числом потоков за счет асинхронного планирования».
Please open Telegram to view this post
VIEW IN TELEGRAM
💯63👍3
💠Room — это библиотека для работы с локальными базами данных SQLite в Android, предоставляющая удобный интерфейс для взаимодействия с данными. Она является частью Android Jetpack и значительно упрощает управление базами данных, устраняя необходимость писать сложный SQL-код вручную. Room обеспечивает безопасность и упрощает поддержку приложения благодаря проверке запросов на этапе компиляции и автоматической сериализации объектов.

⁉️Почему стоит использовать Room?

1. Меньше шаблонного кода: Room автоматически обрабатывает многие задачи, такие как создание таблиц, выполнение запросов и преобразование данных.
2. Проверка запросов на этапе компиляции: Если запрос SQL содержит ошибку, она будет обнаружена до запуска приложения.
3. Интеграция с современными инструментами: Room поддерживает работу с LiveData, RxJava, Coroutines и Paging, что делает её идеальной для современных приложений.
4. Удобная миграция данных: Room предоставляет встроенные инструменты для обновления структуры базы данных.

Как подключить Room к проекту?

implementation 'androidx.room:room-runtime:2.6.1'
annotationProcessor 'androidx.room:room-compiler:2.6.1'

✔️Для использования Kotlin Coroutines добавьте:
implementation 'androidx.room:room-ktx:2.6.1'


➡️Пример 1: Создание сущности.

Сущность представляет таблицу в базе данных. Например:
@Entity(tableName = "users")
public class User {
    @PrimaryKey(autoGenerate = true)
    public int id;

    @ColumnInfo(name = "name")
    public String name;

    @ColumnInfo(name = "age")
    public int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

✔️Аннотация @Entity указывает, что класс является таблицей в базе данных.

➡️Пример 2: Создание DAO.

DAO (Data Access Object) определяет методы для взаимодействия с базой данных:
@Dao
public interface UserDao {
    @Insert
    void insert(User user);

    @Query("SELECT * FROM users")
    List getAllUsers();

    @Delete
    void delete(User user);
}

Методы DAO используют аннотации (@Insert, @Query, @Delete) для выполнения операций.

➡️Пример 3: Инициализация базы данных.

✔️Для доступа к базе данных создаётся класс, наследующий RoomDatabase:
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

✔️Инициализация базы данных:
AppDatabase db = Room.databaseBuilder(context, AppDatabase.class, "database-name").build();
UserDao userDao = db.userDao();


➡️Пример 4: Работа с LiveData.

✔️Room легко интегрируется с LiveData для наблюдения за изменениями данных:
// Получение LiveData-списка всех пользователей
@Query("SELECT * FROM users")
LiveData<List<User>> getAllUsers(); // LiveData<List<User>> - тип возвращаемого значения

✔️В UI можно наблюдать за изменениями данных:
userDao.getAllUsers().observe(this, users -> {
    // Обновление интерфейса при изменении данных
});


➡️Пример 5: Миграция базы данных.

✔️Если структура базы данных изменяется (например, добавляется новое поле), необходимо настроить миграцию:
static final Migration MIGRATION_1_2 = new Migration(1, 2) {
    @Override
    public void migrate(@NonNull SupportSQLiteDatabase database) {
        database.execSQL("ALTER TABLE users ADD COLUMN email TEXT");
    }
};
AppDatabase db = Room.databaseBuilder(context, AppDatabase.class, "database-name")
    .addMigrations(MIGRATION_1_2)
    .build();


⚡️Советы по использованию Room:

1. Используйте Flow или LiveData для реактивного обновления UI.
2.
Оптимизируйте запросы, выбирая только необходимые поля из таблицы.
3.
Настройте UPSERT операции, используя стратегию OnConflictStrategy.REPLACE:
  

   @Insert(onConflict = OnConflictStrategy.REPLACE)
   void insertOrUpdate(User user);
  


☄️Room — это мощный инструмент для работы с локальными данными в Android-приложениях. Она упрощает взаимодействие с SQLite, делает код более организованным и поддерживаемым, а также интегрируется с современными архитектурными компонентами Android.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍43
⌨️ Срезы в стримах. Метод takeWhile

В Java 9 появилось два новых метода, полезных для выбора элементов потока с хорошей производительностью: takeWhile и dropWhile.

Допустим, у нас есть следующий список блюд:

List<Dish> specialMenu = Arrays.asList(
new Dish("seasonal fruit", 120),
new Dish("prawns", 300),
new Dish("rice", 350),
new Dish("chicken", 400),
new Dish("french fries", 530));


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

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

Поможет нам в этом операция takeWhile! Она позволяет выполнить срез любого потока данных (даже бесконечного) с помощью предиката. И, к счастью, она прекращает работу сразу же по обнаружении неподходящего элемента. Вот как ее следует использовать:

List<Dish> sliceMenu1
= specialMenu.stream()
.takeWhile(dish -> dish.getCalories() < 320)
.collect(toList());


#java #stream #takeWhile
Please open Telegram to view this post
VIEW IN TELEGRAM
👍202
☕️GraalVM ускоряет Java с помощью JIT-компиляции и поддерживает мультиязычность и нативную компиляцию.

🔹Пример JIT-ускорения:

public class Demo {
public static void workload(int a) {
System.out.println(a + 1); // Простая операция
}
public static void main(String[] args) {
for (int i = 0; i < 100_000; i++) workload(i);
}
}

🔺Запуск:

java -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler Demo


🚀Вызов JavaScript из Java:

import org.graalvm.polyglot.*;
public class Polyglot {
public static void main(String[] args) {
try (Context context = Context.create()) {
int result = context.eval("js", "Math.pow(2, 10)").asInt();
System.out.println(result); // 1024
}
}
}


🔸Нативная компиляция:

native-image -jar app.jar
./app

🔭Работает без JVM!

📈Советы:
1. Для мультиязычности добавьте org.graalvm.sdk.
2. Используйте
@CEntryPoint для нативных методов.
3. Тестируйте производительность через jmh.


GraalVM позволяет запускать Python и Ruby внутри Java через Polyglot API.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
⌨️ Типы классов

В Java существует несколько «типов» (форм) классов, каждый из которых решает свои задачи и обладает своими особенностями. Рассмотрим основные из них:

1. Топ-level класс
Класс, объявленный в собственном файле .java (или несколько в одном файле, но только один может быть public).
Когда используется: для большинства объектов предметной области.

2. Статический вложенный класс (static nested class)
Объявляется внутри другого класса с модификатором static.
Когда используется: чтобы логически сгруппировать вспомогательный класс и избежать загрязнения пакета; не требует ссылки на экземпляр внешнего класса.

3. Нестатический внутренний класс (inner class)
Нестатический класс внутри другого класса. Имеет неявную ссылку на экземпляр внешнего.
Когда используется: когда класс не имеет смысла отдельно от конкретного экземпляра внешнего класса.

4. Локальный класс (local class)
Объявляется внутри метода. Видим только внутри этого метода.
Когда используется: для единичной вспомогательной логики, зависящей от локальных переменных.

5. Анонимный класс (anonymous class)
Определяется прямо при создании объекта, без имени. Часто для реализации интерфейсов/абстрактных классов «на месте».
Когда используется: когда нужна «быстрая» одноразовая реализация интерфейса или абстрактного класса.

6. Абстрактный класс (abstract class)
Может содержать как реализованные, так и абстрактные (без реализации) методы; сам не может быть инстанцирован.
Когда используется: когда есть общая часть реализации для набора подклассов, но нужна возможность «заставить» их реализовать конкретные методы.

7. Финальный класс (final class)
Нельзя унаследовать.
Когда используется: чтобы зафиксировать поведение и запретить расширение (например, для безопасности).

8. Перечисление (enum)
Специальный вид класса для фиксированного набора констант; может содержать поля и методы.
Когда используется: для ограничения значений до заранее известных.

9. Запись (record) [Java 16+]
Лаконичная форма класса-носителя данных: автоматом генерируются конструктор, equals(), hashCode(), toString().
Когда используется: для неизменяемых DTO/Value-объектов.

10. Запечатанный класс (sealed class) [Java 17+]
Позволяет закрыть иерархию наследования: указать, какие классы могут расширять/реализовывать.
Когда используется: когда важно контролировать все возможные подклассы (например, при реализации «альгебраического типа»).

11. Аннотации (annotation types)
Специальный «интерфейс» с префиксом @interface для метаданных.
Когда используется: для декларации информации, обрабатываемой компилятором или фреймворками (например, @Override, @Entity).

Каждый из этих типов классов/типов в Java помогает структурировать код, задавая чёткие контракты, ограничивая использование и сокращая «бесполезный» шаблонный код.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍107🔥2
⌨️ Игра "Камень, ножницы, бумага"


import java.util.Random;
import java.util.Scanner;

public class RockPaperScissors {
public static void main(String[] args) {
String[] options = { "Камень", "Ножницы", "Бумага" };
Random rnd = new Random();
Scanner sc = new Scanner(System.in);
int userScore = 0, compScore = 0;

System.out.println("Игра «Камень, ножницы, бумага». Введите 0-Камень, 1-Ножницы, 2-Бумага, q-выход.");

while (true) {
System.out.print("Ваш выбор: ");
String inp = sc.nextLine();
if (inp.equalsIgnoreCase("q")) break;

int user, comp = rnd.nextInt(3);
try {
user = Integer.parseInt(inp);
if (user < 0 || user > 2) throw new NumberFormatException();
} catch (NumberFormatException e) {
System.out.println("Некорректно, попробуйте снова.");
continue;
}

System.out.printf("Вы: %s, Компьютер: %s → ", options[user], options[comp]);
int outcome = (user - comp + 3) % 3;
// 0=ничья, 2=победа пользователя, 1=проигрыш
if (outcome == 0) {
System.out.println("Ничья");
} else if (outcome == 2) {
System.out.println("Вы выигрываете");
userScore++;
} else {
System.out.println("Проигрываете");
compScore++;
}

System.out.printf("Счёт %d:%d%n", userScore, compScore);
}

System.out.println("Игра окончена. Финальный счёт " + userScore + ":" + compScore);
sc.close();
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
😁14👍3😍3
⌨️ Stream.reduce() vs Collectors.joining()

Когда требуется конкатенировать строки с использованием Stream, можно выбрать один из двух методов: Stream.reduce() или Stream.collect(Collectors.joining()).

Пример с Stream.reduce():
List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().reduce("", (a, b) -> a + b);
System.out.println(result); // Str1Str2Str3


Пример с Collectors.joining():
List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().collect(Collectors.joining());
System.out.println(result); // Str1Str2Str3


Использование reduce() для конкатенации строк не является оптимальным с точки зрения производительности. При каждом вызове операции +, создается новая строка, так как строки в Java неизменяемы. Это приводит к увеличению нагрузки на память из-за создания множества промежуточных объектов.

В свою очередь, метод Collectors.joining() использует StringBuilder для сборки строк, что значительно эффективнее. Он избегает создания лишних объектов и снижает потребление памяти.

#java #Stream #reduce #joining
Please open Telegram to view this post
VIEW IN TELEGRAM
👍141
⌨️ В Java static import (статический импорт) позволяет импортировать статические члены (методы и поля) из класса, чтобы использовать их напрямую без указания имени класса. Это может сделать код более читабельным и компактным.


// Для импорта конкретного статического члена
import static package.Clazz.member;

// Для импорта всех статических членов класса
import static package.Clazz.*;


Пример:

public class MathUtils {
public static final double PI = 3.141592653589793;
public static int add(int a, int b) {
return a + b;
}
public static int subtract(int a, int b) {
return a - b;
}
}



import static MathUtils.PI;
import static MathUtils.add;

public class Main {
public static void main(String[] args) {
System.out.println("Value of PI: " + PI);
System.out.println("Addition result: " + add(5, 3));
}
}



import static MathUtils.*;

public class Main {
public static void main(String[] args) {
System.out.println("Value of PI: " + PI);
System.out.println("Addition result: " + add(5, 3));
System.out.println("Subtraction result: " + subtract(5, 3));
}
}


Плюсы: читаемость, компактность кода.

Минусы:
- не очевидно откуда берется поле или метод
- импорт всех статических членов может привести к конфликтам имен, если разные классы содержат статические члены с одинаковыми именами

#java #static #import
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥41
⌨️ В какой последовательности указывать блоки catch?

Предположим, есть метод, который может выбросить IOException и FileNotFoundException в какой последовательности должны идти блоки catch? Сколько блоков catch будет выполнено?

Общее правило: обрабатывать исключения нужно от «младшего» к старшему. То есть нельзя поставить в первый блок catch(Exception ex) {}, иначе все дальнейшие блоки catch() уже ничего не смогут обработать, так как любое исключение будет соответствовать обработчику catch(Exception ex).

Таким образом, исходя из факта, что FileNotFoundException extends IOException сначала нужно обработать FileNotFoundException, а затем уже IOException:

void method() {
try {
//...
} catch (FileNotFoundException ex) {
//...
} catch (IOException ex) {
//...
}
}


В Java 7 стала доступна новая языковая конструкция, с помощью которой можно перехватывать несколько исключений одним блоком catch:

try {
//...
} catch(IOException | SQLException ex) {
//...
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍143🔥1
⌨️ Иерархия исключений

Исключения делятся на несколько классов, но все они имеют общего предка — класс Throwable, потомками которого являются классы Exception и Error.

Ошибки (Errors) представляют собой более серьёзные проблемы, которые, согласно спецификации Java, не следует обрабатывать в собственной программе, поскольку они связаны с проблемами уровня JVM. Например, исключения такого рода возникают, если закончилась память доступная виртуальной машине.

Исключения (Exceptions) являются результатом проблем в программе, которые в принципе решаемы, предсказуемы и последствия которых возможно устранить внутри программы. Например, произошло деление целого числа на ноль.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍81
⌨️ Что такое generics?

Generics - это технический термин, обозначающий набор свойств языка позволяющих определять и использовать обобщенные типы и методы. Обобщенные типы или методы отличаются от обычных тем, что имеют типизированные параметры.

Примером использования обобщенных типов может служить Java Collection Framework. Так, класс LinkedList<E> - типичный обобщенный тип. Он содержит параметр E, который представляет тип элементов, которые будут храниться в коллекции. Создание объектов обобщенных типов происходит посредством замены параметризированных типов реальными типами данных. Вместо того, чтобы просто использовать LinkedList, ничего не говоря о типе элемента в списке, предлагается использовать точное указание типа LinkedList<String>, LinkedList<Integer> и т.п.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍102
⌨️ try-catch-finally

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

catch — ключевое слово для отметки начала блока кода, предназначенного для перехвата и обработки исключений в случае их возникновения.

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

Общий вид конструкции для обработки исключительной ситуации выглядит следующим образом:

try {
//код, который потенциально может привести к исключительной ситуации
}
catch(SomeException e ) { //в скобках указывается класс конкретной ожидаемой ошибки
//код обработки исключительной ситуации
}
finally {
//необязательный блок, код которого выполняется в любом случае
}


Возможно ли использование блока try-finally (без catch)?
Такая запись допустима, но смысла в такой записи не так много, всё же лучше иметь блок catch, в котором будет обрабатываться необходимое исключение.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍86❤‍🔥1