es
Feedback
Секреты Java

Секреты Java

Ir al canal en Telegram

Обсуждаем Java, архитектуру, фреймворки и всё, о чем должны говорить разработчики. Владелец: @Alexey_Morl Реклама на бирже: https://telega.in/c/java_secrets

Mostrar más
7 991
Suscriptores
Sin datos24 horas
Sin datos7 días
Sin datos30 días
Archivo de publicaciones
❓Что выведет код на изображении выше?
Anonymous voting

#test
#test

✅Проблема N+1 запроса в Hibernate Она возникает, когда при загрузке связанных сущностей Hibernate выполняет N+1 запросов к ба
Проблема N+1 запроса в Hibernate Она возникает, когда при загрузке связанных сущностей Hibernate выполняет N+1 запросов к базе данных вместо одного, что приводит к лишней нагрузке и снижению производительности приложения. Рассмотрим на примере: Предположим, у нас есть книжный магазин и сущности Author и Book, связанные отношением one-to-many, где один автор может иметь много книг:
@Entity
public class Author {
    @Id
    private Long id;
    private String name;
    @OneToMany(mappedBy = "author", fetch = FetchType.EAGER)
    private List<Book> books;
}

@Entity
public class Book {
    @Id
    private Long id;
    private String title;
    @ManyToOne
    private Author author;
}
И есть записи в базе данных:
authors
 id |     name
----+--------------
  1 | Stephen King
  2 | Jack London
  
books
 id |      title       | author_id
----+------------------+-----------
  1 | It               |  1
  2 | The shining      |  1
  3 | Carrie           |  1
  4 | White Fang       |  2
  5 | Martin Eden      |  2
И вот мы хотим достать всех авторов (а может только некоторых). Делая выборку данных из БД Hibernate выполнит не один запрос, а N+1 (а точнее 1+N), где 1ый запрос - запрос списка нужных сущностей, а каждый последующий из N запросов - связанные сущности для каждой сущности из первого запроса. В нашем случае это дополнительных 2 запроса:
select a1_0.id,a1_0.name from authors a1_0
select b1_0.author_id,b1_0.id,b1_0.title from books b1_0 where b1_0.author_id=?
select b1_0.author_id,b1_0.id,b1_0.title from books b1_0 where b1_0.author_id=?
А если нам нужно сделать выборку 10-20-50... записей? Хочу отметить, что в классе Author я использовал FetchType.EAGER, чтобы наглядно продемонстрировать проблему. Если мы оставим FetchType по умолчанию (для @OneToMany - это FetchType.LAZY), то ситуацию это спасет лишь частично, для тех случаев где мы не будем обращаться к связанным сущностям. Какие есть конкретные решения этой проблемы мы рассмотрим в других постах. 🫡 Секреты Java #hibernate #jpa #nplus1

✅ Инициализация BigDecimal: какой способ выбрать? ▫️Использование конструктора с числом с плавающей точкой: var bigDecimal =
Инициализация BigDecimal: какой способ выбрать? ▫️Использование конструктора с числом с плавающей точкой:
var bigDecimal = new BigDecimal(2.1);
System.out.println("Output: " + bigDecimal);
//Output: 2.100000000000000088817841970012523233890533447265625
Как видим, такой способ может привести к потере точности. Крайне не рекомендуется использовать. ▫️Использование статического метода valueOf():
var bigDecimal1 = BigDecimal.valueOf(2.1);
var bigDecimal2 = BigDecimal.valueOf(1.1234567891234567891234);
System.out.println("Output: " + bigDecimal1);
System.out.println("Output: " + bigDecimal2);
//Output: 2.1
//Output: 1.1234567891234568
Этот вариант работает точнее, но мы не сможем обеспечить очень высокую точность (ограничение double: до 16 символов после запятой). На примере выше с bigDecimal2 это видно. ▫️Использование конструктора с строкой:
var bigDecimal1 = new BigDecimal("1.1234567891234567891234");
var bigDecimal2 = new BigDecimal("2.1");
System.out.println("Output: " + bigDecimal1);
System.out.println("Output: " + bigDecimal2);
//Output: 1.1234567891234567891234
//Output: 2.1
Этот метод инициализации наиболее предпочтителен, поскольку избегает проблем при преобразовании чисел с плавающей точкой и не имеет ограничение по точности. Вы можете записать сколько угодно знаков после запятой. Вывод: Использование конструктора со строкой обеспечивает наиболее точную инициализацию BigDecimal для десятичных чисел высокой точности. Когда переводите double в BigDecimal используйте BigDecimal.valueOf(). Конструктор с числом с плавающей точкой, по возможности, не используйте никогда. 🫡 Секреты Java #bigdecimal

❓Что выведет код на изображении выше
Anonymous voting

#test
#test

✅ ParameterizedTest в JUnit 5 @ParameterizedTest - это аннотация JUnit и очень мощный инструмент для тестирования различных с
ParameterizedTest в JUnit 5 @ParameterizedTest - это аннотация JUnit и очень мощный инструмент для тестирования различных случаев использования кода. Она позволяет запускать один и тот же тестовый метод с разными наборами входных данных. Давайте рассмотрим на примере, как использовать @ParameterizedTest. ▫️Сначала добавим зависимость (помимо необходимых зависимостей JUnit):
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.10.2'
▫️Тестируем различные значения:
public class MyTest {

    @ParameterizedTest
    @ValueSource(ints = {1, 2, 3})
    void test(int arg) {
        assertEquals(arg, arg);
    }
}
При запуске, будет последовательно выполнено три теста с каждым из значений! ▫️Набор параметров мы можем передавать из метода:
public class MyTest {

    @ParameterizedTest
    @MethodSource("provideArguments")
    void test(int a, int b, int result) {
        assertEquals(result, a + b);
    }

    private static Stream<Arguments> provideArguments() {
        return Stream.of(
                Arguments.of(1, 2, 3),
                Arguments.of(4, 5, 9),
                Arguments.of(-1, -1, -2)
        );
    }
}
▫️Либо из csv файла:
public class MyTest {

    @ParameterizedTest
    @CsvFileSource(resources = "/test-data.csv")
    void test(int a, int b, int result) {
        assertEquals(result, a + b);
    }
}
▫️Передаем енамы (причем можем выборочно):
public class MyTest {

    enum Color {
        RED, GREEN, BLUE;
    }

    @ParameterizedTest
    @EnumSource(value = Color.class, names = {"RED", "BLUE"})
    void test(Color color) {
        assertTrue(color == Color.RED
                || color == Color.BLUE);
    }
}
Конечно, возможностей параметризованного тестирования гораздо больше чем рассмотрено в посте, но даже с представленной информацией вы готовы использовать @ParameterizedTest в ваших тестах для более гибкого и подробного тестирования. 🫡 Секреты Java #junit

✅ Java - компилируемый или интерпретируемый язык? Java - это язык программирования, который обычно описывают как "компилируем
Java - компилируемый или интерпретируемый язык? Java - это язык программирования, который обычно описывают как "компилируемый и интерпретируемый". Давайте разберемся, что это означает и как это влияет на процесс создания и запуска Java программ. Компиляция: ▫️Когда вы пишете Java код, вы его пишите в файл с расширением .java. ▫️Этот файл с исходным кодом передается компилятору Java (например, javac идущий в JDK), который преобразует его в байт-код. ▫️В результате компиляции создаются файлы классов с расширением .class, содержащие байт-код, который представляет собой набор инструкций, понятных JVM. Пример компиляции Java кода:
javac MyClass.java
Программа еще не запущена! Компиляция - это все еще процесс разработки. Интерпретация: ▫️Когда вы запускаете программу на Java, созданный байт-код передается JVM. ▫️JVM пошагово (построчно, пооператорно), в реальном времени, преобразует байт-код в машинный код и выполняет его. ▫️В процессе выполнения JVM может также проводить оптимизацию и JIT (Just-In-Time) компиляцию некоторых частей кода в машинный код для повышения производительности (JIT компиляция - отдельная тема). Пример запуска Java программы:
java MyClass
Вот теперь мы запустили программу. Вывод: Java использует смешанный подход, объединяя в себе компиляцию и интерпретацию. Это позволяет создавать эффективные и переносимые программы, которые могут быть запущены на различных платформах без необходимости перекомпиляции исходного кода. 🫡 Секреты Java #javac #compile #jvm

✅Как конвертировать InputStream в String? Это достаточно тривиальная задача и на самом деле у Java есть более 10! способов пр
Как конвертировать InputStream в String? Это достаточно тривиальная задача и на самом деле у Java есть более 10! способов преобразовать InputStream в String. Сначала я думал рассмотреть тут все эти способы, но смысла в этом нет. Если оценить каждый способ по производительноси и элегантности решения, то чемпионы будут бесспорными: ▫️Начиная с Java 9:
String result = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
▫️Так же с Java 9:
var bos = new ByteArrayOutputStream();
inputStream.transferTo(bos);
String result = bos.toString(StandardCharsets.UTF_8);
Эти варианты выигрывают и по производительноси и по простоте. ▫️Если Java ниже 9, то самым производительным способом будет:
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
int nRead;
byte[] data = new byte[1024];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
  buffer.write(data, 0, nRead);
}
buffer.flush();
String result = buffer.toString(StandardCharsets.UTF_8);
но, как видите, не самым элегантным. ▫️Что касается простого и не сильно проигрывающего по производительности способа для Java ниже 9, то это вариант использования Apache Commons IO:
String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8);
Однако, если вы все еще используете Java 8, то вам следует подумать о переходе на более свежую версию. 🫡 Секреты Java #inputstream #string

❓Какой код нужно вставить вместо INSERT CODE HERE на изображении выше, чтобы программа вывела "Hello, outer world!" ?
Anonymous voting

#test
#test

✅Java Naming Conventions. Названия пакетов. Часть 2 В продолжение предыдущего поста. Смотрим рекомендации дальше: ▫️Oracle: P
Java Naming Conventions. Названия пакетов. Часть 2 В продолжение предыдущего поста. Смотрим рекомендации дальше: ▫️Oracle:
Package names are written in all lower case to avoid conflict with the names of classes or interfaces.
По мнению Oracle, названия пакетов должны быть написаны в нижнем регистре. ▫️Google Java Style Guide:
Package names use only lowercase letters and digits (no underscores). Consecutive words are simply concatenated together.
В соответствии с этим соглашением, названия пакетов так же должны быть написаны в нижнем регистре, а слова внутри названия пакета должны быть просто сконкатенированы без использования подчеркиваний. Например сom.example.deepspace, но НЕ com.example.deepSpace или com.example.deep_space. ▫️Иногда имя домена может быть недопустимым для имени пакета. Oracle предлагает некоторые соглашения на этот случай: ⚬ Если домен содержит дефис или другие спец. символы - заменяйте их символом подчеркивания (Oracle рекомендует символ нижнего подчеркивания именно для замены запрещенных в названии пакета символов!). Домен hyphenated-name.example.org -> имя пакета org.example.hyphenated_name ⚬ Если пакет содержит зарезервированные ключевые слова Java в названии, добавьте после них подчеркивание. Домен example.int -> имя пакета int_.example ⚬ Если какой-либо компонент пакета начинается с цифры, или любого другого запрещенного для названия идентификатора символа, добавьте перед ним подчеркивание. Домен 123name.example.com -> имя пакета com.example._123name Соблюдение Java Naming Conventions (не только при названии пакетов) - это следование общепринятым стандартам. Это позволяет создавать более читаемый и понятный для других разработчиков код, что облегчает совместную разработку и поддержку проектов. 🫡 Секреты Java #namingconvention #package

✅ Java Naming Conventions. Названия пакетов. Часть 1 При работе с Java важно следовать соглашениям о наименовании, чтобы обле
Java Naming Conventions. Названия пакетов. Часть 1 При работе с Java важно следовать соглашениям о наименовании, чтобы облегчить понимание кода другим разработчикам и поддерживать единообразие в проекте. Это касается и названий пакетов. Давайте взглянем на рекомендации, которые дают Oracle и Google Java Style Guide: ▫️Oracle:
Companies use their reversed Internet domain name to begin their package names—for example, com.example.mypackage for a package named mypackage created by a programmer at example.com.
Т.е. компаниям следует использовать в проектах в качестве префикса названий пакетов перевернутое доменное имя в Интернете. Т.е. для компании example.org структура пакетов должна начинаться с org.example ▫️Oracle:
Subsequent components of the package name vary according to an organization's own internal naming conventions. Such conventions might specify that certain directory name components be division, department, project, machine, or login names.
Последующие имена компонентов в структуре пакетов определяются уже внутри организации и могут быть: названием проекта, отдела, региона и пр. Например com.example.myproject или com.example.myregion. Продолжение в следующем посте. 🫡 Секреты Java #namingconvention #package

У бойскаутов существует простое правило, которое применимо и к нашей профессии: "Оставь место стоянки чище, чем оно было до твоего прихода." Если мы все будем оставлять свой код чище, чем он был до нашего прихода, то код попросту не будет загнивать. Чистка не обязана быть глобальной. Присвойте более понятное имя переменной, разбейте слишком большую функцию, устраните одно незначительное повторение, почистите сложную цепочку if.
© Роберт Мартин 🫡 Секреты Java #quotes

✅Records При написании программы мы часто используем неизменяемые (immutable) классы, которые предназначены для хранения и пе
Records При написании программы мы часто используем неизменяемые (immutable) классы, которые предназначены для хранения и передачи данных (те же data transfer objects, запросы, команды и пр.) Реализация этих классов очень похожа: поля, конструктор, геттеры и, возможно, equals/hashCode/toString. Пусть у нас есть обычный класс Man:
public final class Man {
    private final String name;
    private final int age;

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

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
Вроде все ок, но сколько шаблонного кода написано! А если нам нужно еще и сравнивать объекты? Добавляем сюда еще и реализации equals()/hashCode(). Records в Java, добавленные в Java 14, предоставляют компактный способ определения неизменяемых типов данных. Вместо того чтобы явно объявлять поля класса, конструкторы и методы доступа, вы можете определить запись одной строкой:
public record Man(String name, int age) {}
И это все! У нас есть геттеры, конструктор, equals()/hashCode()/toString():
var man1 = new Man("John Wick", 46);
var man2 = new Man("John Wick", 46);
System.out.println(man1.name()); // John Wick
System.out.println(Objects.equals(man1, man2)); // true
System.out.println(man1); // Man[name=John Wick, age=46]
Хотите переопределить конструктор или добавить метод? Пожалуйста:
public record Man(String name, int age) {

    public Man {
        name = "Mr. " + name;
    }

    public boolean isTeenager() {
        return age > 12 && age < 20;
    }
}
Record имеет и некоторые ограничения: ▫️Не могут наследовать и быть наследованы, но могут реализовывать интерфейсы ▫️Не могут быть абстрактны Справедливости ради тут стоит упомянуть о библиотеке Lombok, которая может делать подобные вещи, но после Java 14 теперь это возможно и без дополнительных зависимостей. 🫡 Секреты Java #java14 #records

❓Какой модификатор доступа используются для того чтобы сделать члены класса доступными для подклассов, но недоступными внутри пакета?
Anonymous voting

#test
#test

✅В чем разница между методами Stream.map() и Stream.flatMap() ? Оба метода вызываются у объекта Stream и оба возвращают объек
В чем разница между методами Stream.map() и Stream.flatMap() ? Оба метода вызываются у объекта Stream<T> и оба возвращают объект Stream<R>, т.е поток элементов другого типа. ▫️ Метод map() в качестве аргумента принимает лямбду (интерфейс Function), которая применяется к каждому элементу <T> стрима, преобразует его в <R> и передает в выходной стрим.
var words = Arrays.asList("Hello", "World", "!");

// Преобразование каждого слова в верхний регистр
var uppercaseWords = words.stream()
    .map(String::toUpperCase)
    .toList();

// uppercaseWords содержит: [HELLO, WORLD, !]
▫️Метод flatMap(), как можно догадаться из названия, представляет собой комбинацию map и flat, т.е. операция "преобразования" и "выравнивания" (flatting). Чтобы понять, что такое flatting, рассмотрим структуру [ [a, b, c], [d, e, f], [g] ], которая является "двухуровневой". "Выравнивание" - это преобразование такой структуры в "одноуровневую" -> [ a, b, c, d, e, f, g]. flatMap() принимает лямбду (тот же интерфейс Function), которая преобразует каждый элемент в потоке в отдельный поток данных и затем объединяет их в один.
var words = Arrays.asList("Hello", "World", "!");

// Преобразование каждого слова в верхний регистр
var splittedWords = words.stream()
        .flatMap(word -> 
            Arrays.stream(word.split("")))
        .toList();

// splittedWords содержит: [H, e, l, l, o, W, o, r, l, d, !]
В лямбде мы делим каждое слово из списка на буквы (split("")) и превращаем массив букв в отдельный stream (Arrays.stream()), далее эти стримы "выравниваются" в один результирующий. 🫡 Секреты Java #streamapi

✅Swagger: документирование REST API вашего Spring приложения Зачем нам нужна документация? Когда мы пишем backend приложение,
✅Swagger: документирование REST API вашего Spring приложения Зачем нам нужна документация? Когда мы пишем backend приложение, то ожидаем, что к нему будут обращаться другие системы, будь то фронт, моб. приложения или прочие сервисы. Разработчикам, которые захотят использовать наше приложение понадобится API документация, чтобы знать по какому протоколу с нами взаимодействовать (на какие URL какие запросы посылать). Написание документации - точно не самое увлекательное занятие, но библиотека Springdoc OpenAPI сделает все за нас. Она интегрируется напрямую с нашим кодом и автоматически создает документацию Swagger для ваших контроллеров, моделей, запросов и ответов. Для начала работы нужно всего лишь добавить соответствующую зависимость в build.gradle нашего проекта:
implementation 'org.springdoc:springdoc-openapi-ui:1.7.0'
для Spring boot версии 2, либо:
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'
для Spring boot версии 3. И это все! Springdoc OpenAPI автоматически отсканирует наше приложение и сгенерирует документацию. Мы можем добавить деталей:
@Tag(name = "Entities")
@RestController
public class Controller {

    @Operation(summary = "Get entity")
    @GetMapping("/")
    public Object get() {
        return "Hello world!";
    }
}
Тут мы использовали доп. аннотации @Tag для объединения эндпоинтов по тегам и @Operation, чтобы задать описание эндпоинта (страница Swagger после будет выглядеть как на изображении к посту). Конечно, есть и другие аннотации и инструменты этой библиотеки, которые помогут подробно, в деталях, описать ваш API. По дефолту: ▫️http://<host>:<port>/swagger-ui.html - доступ к Swagger UI ▫️http://<host>:<port>/v3/api-docs - API docs Springdoc OpenAPI предоставляет простой и удобный способ создания и поддержания документации вашего API на основе Spring Framework. Используйте его в вашем проекте, чтобы значительно упростить себе жизнь, когда у вас попросят документацию по REST API вашего приложения. 🫡 Секреты Java #spring #swagger

❓Какой код нужно вставить вместо INSERT CODE HERE, чтобы программа вывела true?
Anonymous voting