ar
Feedback
Секреты Java

Секреты Java

الذهاب إلى القناة على Telegram

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

إظهار المزيد
7 991
المشتركون
لا توجد بيانات24 ساعات
لا توجد بيانات7 أيام
لا توجد بيانات30 أيام
أرشيف المشاركات
#test
#test

✅Примитивы vs обертки над примитивами Когда нам лучше использовать примитивы типа int, boolean и др., а когда обертки над ним
Примитивы vs обертки над примитивами Когда нам лучше использовать примитивы типа int, boolean и др., а когда обертки над ними: Integer, Boolean и др.? ▫️Если вы хотите использовать коллекции и дженерики вам стоит выбрать Wrapper классы, т.к. boxing/unboxing приведет к дополнительным накладным расходам и увеличению нагрузки на Garbage Collector ▫️Если у вас очень большое количество различных математических вычислений, используйте примитивы, вычисления с ними всегда быстрее и менее ресурсозатратны ▫️Используйте Wrapper классы, когда вы хотите, чтобы переменная (поле) могла иметь значение null. Или по другому - когда поле не обязательно для инициализации. Может быть удобно инициализировать переменные нулевым значением, а это невозможно сделать с примитивами. Например у нас есть какой-то Request:
class Request {
  Boolean success;
}
Тут он может по сути иметь три статуса: success = true - успешно выполнен success = false - не успешный success = null - еще не определен (запрос еще не выполнен) В этом случае, если бы мы использовали примитив, то при создании объекта Request и до его условного выполнения, он бы имел статус success = false, что в рамках этого сценария было бы неверно и вводило бы в заблуждение. Делитесь в комментариях, какие еще случаи встречали на практике? 🫡 Секреты Java #autoboxing #primitives

✅Разберемся в понятиях объявления, сигнатуры и перегрузки методов Объявление метода состоит из: <модификаторы> <возв
Разберемся в понятиях объявления, сигнатуры и перегрузки методов Объявление метода состоит из: <модификаторы> <возвращаемый тип> <имя метода> <список параметров> <выбрасываемое исключение>{ <тело метода> } Сигнатура метода - это имя метода и типы параметров в указанном порядке. На примере на изображении это - getCongratulation(String, int). Все остальные части объявления метода, включая имена параметров, не являются частью сигнатуры метода. Причем даже если методы отличаются только типом параметров, и один из типов является наследником другого, это все равно разные сигнатуры. Сигнатура нужна компилятору, чтобы понять какой метод вызывается на данном участке кода, поэтому в пределах класса она должны быть уникальной. Перегрузка метода (Method Overloading) - это объявление методов с одинаковыми названиями в одном классе. НО они должны иметь разные сигнатуры. Поэтому попытки перегрузить метод поменяв, например, возвращаемое значение
public static []char getCongratulation(String name, int age) throws Exception 
{
    return ...;
}
или выбрасываемое исключение
public static String getCongratulation(String name, int age) throws IOException 
{
    return ...;
}
приведут к ошибке компиляции 'method is already defined in class'. Перегрузка относится к полиморфизму времени компиляции (Compile time polymorphism) или по другому - статическому полиморфизму. 🫡 Секреты Java #method #overloading

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

#test
#test

✅ double vs. BigDecimal В Java есть два типа данных, которые могут быть использованы для работы с числами с плавающей точкой:
double vs. BigDecimal В Java есть два типа данных, которые могут быть использованы для работы с числами с плавающей точкой: double и BigDecimal, однако они имеют свои особенности и различия. Тип double представляет собой 64-битное число с плавающей точкой, работающее по стандарту IEEE 754. Этот стандарт определяет способ преобразования действительных чисел в двоичный формат, а т.к в двоичном виде иногда невозможно точно представить десятичные дроби, то как следствие double имеют ограниченную точность и проблемы с округлением. Рассмотрим это на примере:
double first = 0.1;
double second = 0.2;
double result = first + second;

System.out.println(result); // Выводит 0.30000000000000004
Мы ожидали результат 0.3, но фактически он отличается. Класс BigDecimal представляет неизменяемое (immutable) десятичное число с произвольной точностью. Говоря простым языком - BigDecimal состоит из двух частей: целого числа и значения scale, определяющего количество цифр после запятой. Класс BigDecimal обеспечивает более высокую точность, чем double, поскольку он выполняет вычисления с десятичными дробями используя целочисленную арифметику, которая более точна, чем арифметика чисел с плавающей точкой. Те же вычисления, но с BigDecimal:
var first = new BigDecimal("0.1");
var second = new BigDecimal("0.2");
var result = first.add(second);

System.out.println(result); // Выводит 0.3
Результат точный, но важно отметить, что работа с BigDecimal может быть более медленной по сравнению с double, так как BigDecimal использует более сложный механизм для хранения и выполнения операций с числами. В заключение, если вам необходимо обрабатывать числа с высокой точностью без проблем с округлением, например финансовые вычисления, то используйте BigDecimal. Если скорость и производительность является приоритетом, а абсолютная точность не столь важна, то используйте double. 🫡 Секреты Java #double #bigdecimal

Написать код, понятный компьютеру, может каждый, но только хорошие программисты пишут код, понятный людям
© Мартин Фаулер 🫡 Секреты Java #quotes

✅Как управлять выделенной памятью для нашего приложения? Может случится, что в процессе развития наше приложение выросло на с
Как управлять выделенной памятью для нашего приложения? Может случится, что в процессе развития наше приложение выросло на столько, что начинает долго соображать или вовсе падать с OutOfMemoryError. Если дело не в memory leak, то пора выделить приложению больше памяти. Параметры -Xms и -Xmx в Java используются для управления выделением и использованием памяти при выполнении приложений. Пример:
java -Xms512m -Xmx2g MyApp
▫️Тут мы запускаем приложение MyApp стандартной командой (при запуске из Idea идем в "Run" -> "Edit Configurations..." и у выбранного приложения прописываем параметры в "VM options") ▫️Параметр -Xms задает начальный размер выделенной памяти для JVM. В примере выше JVM начинает работу, выделяя 512 мегабайт. Установка оптимального значения -Xms может помочь ускорить запуск JVM и улучшить производительность приложения. ▫️Параметр -Xmx указывает максимальный размер памяти, который JVM может использовать. На примере - 2ГБ. Установка правильного значения -Xmx поможет нам предотвратить переполнение памяти. ▫️Виды значений, которые можно установить для параметров -Xms и -Xmx, указываются в килобайтах (k), мегабайтах (m) или гигабайтах (g). ▫️Без указания параметров, дефолтные значения рассчитываются JVM в зависимости от операционной системы и характеристик компьютера. Обычно имеет смысл устанавливать значение -Xms равным -Xmx, это позволит приложению запускаться быстрее, не тратить время на выделение дополнительной памяти и не конкурировать с другими приложениями за свободную память компьютера. 🫡 Секреты Java #java_memory

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

#test
#test

✅Deadlock или взаимная блокировка Это одна из проблем многопоточности и может возникнуть, когда нескольким потокам одновремен
Deadlock или взаимная блокировка Это одна из проблем многопоточности и может возникнуть, когда нескольким потокам одновременно требуются одни и те же ресурсы, но они получают доступ к ним в разном порядке. Такая ситуация приведет к зависанию потоков или всей программы. Deadlock может возникнуть, когда потоки взаимодействуют с ресурсами в следующем порядке: 1. Поток А блокирует ресурс А и ожидает ресурс Б. 2. Поток В блокирует ресурс Б и ожидает ресурс А. Для наглядности рассмотрим пример кода, иллюстрирующего возникновение deadlock:
public class DeadLockExample {

    public static void main(String[] args) {
        var resourceA = new Object();
        var resourceB = new Object();

        new Thread(() -> someMethod(resourceA, resourceB)).start();
        new Thread(() -> someMethod(resourceB, resourceA)).start();
    }

    @SneakyThrows
    public static void someMethod(Object first, Object second) {
        synchronized (first) {
            System.out.println("First synchronized block");
            Thread.sleep(100);
            synchronized (second) {
                System.out.println("Second synchronized block");
            }
        }
    }
}
В данном примере выполняются два потока. Каждый поток передает в someMethod() одни и те же объекты, но в разном порядке и метод пытается заблокировать сначала один ресурс, а затем другой. Однако первый поток блокирует resourceA и ожидает resourceB, в то время как второй поток блокирует resourceB и ожидает resourceA. Если запустить данный код мы увидим, что потоки достигнут первого блока синхронизации, выведут:
First synchronized block
First synchronized block
и на попытке получить блокировку второго объекта программа зависнет, т.к объекты уже заблокированы. Для избежания deadlock'ов существует ряд методов: ▫️Избегать получения нескольких блокировок для потока ▫️Если потоку необходимо несколько блокировок, то убедится, что каждый поток получает блокировки в одном и том же порядке ▫️Использовать классы Lock для блокировок 🫡 Секреты Java #multithreading

✅Паттерн "Стратегия" Этот паттерн является поведенческим паттерном проектирования, который позволяет заменять какое-либо пове
Паттерн "Стратегия" Этот паттерн является поведенческим паттерном проектирования, который позволяет заменять какое-либо поведение класса во время выполнения и следует принципу "композиция лучше наследования".
Определяет семейство алгоритмов, инкапсулирует каждый из них и делает их взаимозаменяемыми
- такое определение дает "Банда четырех" в своей книге. Разберем на простом примере: Допустим, у нас есть класс SortContext, который должен уметь сортировать набор данных. Сначала мы "определяем семейство алгоритмов и инкапсулируем их" (в нашем случае алгоритмы сортировки) в интерфейс SortStrategy:
public interface SortStrategy {
    void sort(int[] dataset);
}
И реализации алгоритмов:
public class BubbleSort implements SortStrategy {
    public void sort(int[] dataset) {
        // Реализация алгоритма сортировки пузырьком
    }
}

public class QuickSort implements SortStrategy {
    public void sort(int[] dataset) {
        // Реализация алгоритма быстрой сортировки
    }
}
Так будет выглядеть наш класс SortContext с "инкапсулированным и заменяемым" алгоритмом сортировки:
public class SortContext {
    private SortStrategy sortStrategy;

    public void setSortStrategy(SortStrategy sortStrategy) 
    {
        this.sortStrategy = sortStrategy;
    }

    public void sortData(int[] dataset) 
    {
        sortStrategy.sort(dataset);
    }
}
Теперь мы можем использовать класс SortContext для выполнения сортировки данных:
public class Main {
    public static void main(String[] args) {
        int[] dataset = {2, 4, 1, 5, 3};

        Context context = new Context();
        context.setSortStrategy(new BubbleSort()); // используем сортировку пузырьком
        context.sortData(dataset);

        context.setSortStrategy(new QuickSort()); // используем быструю сортировку
        context.sortData(dataset);
    }
}
Паттерн позволяет нам менять алгоритм во время выполнения программы, просто изменяя объект стратегии, который передается классу контекста. 🫡 Секреты Java #pattern

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

#test
#test

✅Magic Numbers и Magic Strings Магические числа и строки - это числа и строки в коде, смысл которых невозможно понять без глу
Magic Numbers и Magic Strings Магические числа и строки - это числа и строки в коде, смысл которых невозможно понять без глубокого понимания происходящего на этом участке кода. Несмотря на "волшебные" названия, эти числа и строки несут большое зло и, при своей кажущейся безобидности, могут быть источником очень больших проблем как для разработчика, так и для бизнеса. Программист, не являющийся автором кода, который столкнется с ними, скорее всего будет вынужден потратить много времени, чтобы разобраться в их значении. Да и сам автор через какое-то время может забыть, какой смысл несут эти числа и строки. Рассмотрим пример:
public double calculateNetProfit(double costPrice, double sellingPrice) {
    return (sellingPrice - costPrice) * (1 - 0.2);
}
Программа вычисляет чистую прибыль с продажи товара. В какой-то момент условия вычисления меняются и вам дают задачу поправить алгоритм. Сможете ли вы с первого взгляда на код понять значения чисел в нем и куда вносить изменения? Скорее всего без вопросов к коллегам и досконального изучения функционала - нет. Решение в таких ситуация достаточно простое - всегда создавайте константы (или локальные переменные) с названиями, которые четко и однозначно передают смысл значения:
private final double VALUE_ADDED_TAX = 0.2;
private final double NET_PROFIT_MULTIPLIER = 1 - VALUE_ADDED_TAX;
    
public double calculateNetProfit(double costPrice, double sellingPrice) {
    return (sellingPrice - costPrice) * NET_PROFIT_MULTIPLIER;
}
Тут нам сразу понятно, что метод учитывает НДС и вычисляет чистую прибыль с учетом налога. Более того, мы можем использовать эту константу и в других вычислениях. И если ставка налога изменится нам не придется искать по всему коду, какие вычисления на ней завязаны. 🫡 Секреты Java #bad_practice #anti_pattern

✅Предпочтительный способ сравнения объектов Сравнивая объекты через оператор равенства "==" мы сравниваем только ссылки на эт
Предпочтительный способ сравнения объектов Сравнивая объекты через оператор равенства "==" мы сравниваем только ссылки на эти объекты, и если ссылки указывают на разные объекты, даже если эти объекты идентичные по "наполнению" мы получим false. С примитивами проще - оператор их сравнивает по значению всегда.
int a = 1;
int b = 1;
System.out.println(a == b);
вернет true, тогда как
Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a == b);
выведет false. Более широкую концепцию равенства предлагает метод equals() класса Object.
a.equals(b);
тут уже результат будет true. Но в это случае есть опасность получить NullPointerException, если в переменную 'a' попадет null. Делать проверку на null перед сравнением будет не самым элегантным решением:
a == null ? b == null : && a.equals(b);
Эту проблему решает статический метод equals() утилитарного класса Objects
Objects.equals(a, b);
Если один из аргументов равен null, результатом сравнения будет false. Если оба равны null - результат true. Метод Objects.equals() обрабатывает null значения, использует equals() объектов для сравнения и запись получается более удобочитаемой. 🫡 Секреты Java #equals

✅В этом посте мы рассмотрим как возможно сделать конвертацию из массива фиксированного размера (Array) в список (List) исполь
✅В этом посте мы рассмотрим как возможно сделать конвертацию из массива фиксированного размера (Array) в список (List) используя стандартные инструменты Java. Рассмотрим пример:
Integer[] array = { 1, 2, 3, 4 };
List<Integer> list = Arrays.asList(array)
Важно отметить, что в этом случае изменения в полученном списке также повлияют на исходный массив и наоборот (так как это всего лишь представление массива в виде списка), а так же, что созданный список будет фиксированной длины (immutable) Чтобы создать список, не связанный с исходным массивом, мы можем использовать следующий прием:
List<Integer> list = List.of(array);
Правда полученный список все еще будет immutable. Немного преобразуем:
List<Integer> list = new ArrayList<>(List.of(array));
и получаем изменяемый (mutable) и не связанный с исходным массивом список. То же самое можно сделать используя Stream API (с Java 8, а метод toList() с Java 16):
List<Integer> list = Arrays.stream(array).toList();
- immutable список
List<Integer> list = Arrays.stream(array)
                .collect(Collectors.toCollection(ArrayList::new));
- стандартный расширяемый ArrayList. Если нам нужен другой тип списка, передаем в параметр toCollection() конструктор нужного типа. По производительности вариант со Stream API немного проигрывает, но разница незначительная и будет заметна только при очень огромных размерах массива. Для преобразования массива в список есть инструменты также и у Guava с Apache Commons Collections, но на самом деле, учитывая необходимость дополнительных зависимостей, они не так интересны. 🫡 Секреты Java #list #array

Привет, коллега! Не буду лукавить и скромничать) В следующие 90 секунд (столько времени ты потратишь на этот текст) я хочу убедить тебя остаться со мной на канале “Секреты Java”. ✅ Дочитай до конца и поймешь – подписаться действительно стоит! Меня зовут Антон и в своей компании я Senior Java Developer/Architect – формирую видение технической части проекта для команды разработчиков и отвечаю за достижение поставленных целей. За последние 6 лет участвовал в 15 проектах, в том числе, и для крупнейших корпораций России. Занимаюсь менторством, люблю и умею учить, всегда доброжелателен и открыт к общению. ✅ На канале ты найдешь: - простые и понятные объяснения сложных концепций; - интересные примеры кода; - полезные советы по оптимизации и улучшению производительности; - разбор bad и good practice, паттернов проектирования и архитектуры - а так же советы по поиску работы и много другое На “Секретах Java” помимо чисто технических вопросов мы также будем обсуждать актуальные тенденции и новости из мира Java, а иногда и за жизнь айтишников можем поговорить. ✅ “Не стыдно чего-нибудь не знать, но стыдно не хотеть учиться” Для того, чтобы стать активным участником нашего будущего грандиозного комьюнити, твой возраст, пол и уровень текущих знаний о Java не имеет никакого принципиального значения. Например, я начал изучать язык в 28 лет, а в IT попал в 29, имея за плечами гуманитарное образование, но при этом всегда помнил следующее изречение Сократа: “Не стыдно чего-нибудь не знать, но стыдно не хотеть учиться”. Поэтому смело спрашивай все, что захочешь, и мы вместе будем разбираться в нашем искусстве. Кстати, я тоже открыт для новых знаний и постоянно продолжаю учиться) ✅ Стремление к профессионализму – ключ к успеху в современном мире. Подписываясь на канал, “Секреты Java”, ты бесплатно получишь доступ к эксклюзивному контенту, улучшишь свои навыки и знания в программировании. Не упускай возможность стать частью нашего сообщества. Здесь ты сможешь общаться с единомышленниками, делиться своими идеями и находить вдохновение для новых проектов. Ты готов к новым вызовам и достижению очередных высот в своей карьере? ✅ Тогда, вперед – жми “Подписаться”!

✅Блок finally и гарантия выполнения Иногда при написании программы нам необходимо, чтобы какой-то блок кода гарантированно бы
Блок finally и гарантия выполнения Иногда при написании программы нам необходимо, чтобы какой-то блок кода гарантированно был выполнен при любых исключительных ситуациях. Если мы обернем код, который может выбросить исключение в try-catch, то такую гарантию нам даст блок finally. Блок finally будет выполняется всегда, кроме ситуаций когда поток не дошел до его выполнения (System.exit(), ошибки JVM, бесконечный цикл и др.) Рассмотрим несколько интересных наглядных примеров:
public String method() {
        try {
            System.out.println("Try block");
        }
        finally {
            System.out.println("Finally block");
        }
}
Вывод программы: Try block Finally block А что вернет этот метод?
public String method() {
  try {  
    return "Try block";  
  }  
  finally {  
    return "Finally block";
  }
}
Ответ: Строку "Finally block" И даже если код в блоке try бросает исключение:
public String method() {
  try {  
    throw new Exception();  
  }  
  finally {  
    return "Finally block";
  }
}
Метод все равно вернет строку "Finally block"! Если в блоке finally есть оператор return или throw, то все брошенные исключения или возвращаемые значения в блоках try и catch выше будут забыты! Поэтому как и любым инструментом, блоком finally необходимо пользоваться осторожно. 🫡 Секреты Java #finally

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