ТОП-25 трюков, советов и лучших практик программирования на Java

2
6837
Добавить в избранное

Подборка лучших практик программирования на Java для экономии времени, оптимизации и улучшения качества кода.

ТОП-25 трюков, советов и лучших практик программирования на Java

На Java работают более 3 миллиардов устройств и кодят больше 9 миллионов разработчиков. Подсчитать количество приложений просто невозможно. Это мощный и надежный язык, но иногда программирование на Java может быть трудоемким.

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

В этой статье мы подобрали небольшую коллекцию таких практик, трюков и советов, которые сэкономят вам время.

Если вы начинающий Java-программист, то скачать Java можно на официальном сайте.

Организация работы

Чистый код

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

Работа с ошибками

Трассировка стека

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

NullPointer Exception

Исключения нулевого указателя возникает в Java довольно часто при попытке вызова метода несуществующего объекта.

Возьмем для примера следующую строчку кода:

Если объект school окажется равен Null или его метод listStudents вернет Null, вы получите исключение NullPointerException.

Хорошей практикой разработки на Java является предварительная проверка на Null в методах:

Дата и время

System.currentTimeMillis vs System.nanoTime

В Java есть два стандартных способа проведения тайм-операций и не всегда понятно, какой из них следует выбрать.

Метод System.currentTimeMillis возвращает текущее количество миллисекунд от начала эпохи Unix в формате Long. Его точность составляет от 1 до 15 тысячных долей секунды в зависимости от системы.

Метод System.nanoTime имеет точность до одной миллионной доли секунды (наносекунды) и возвращает текущее значение наиболее точного доступного системного таймера.

Таким образом, System.currentTimeMillis отлично подходит для отображения абсолютного времени и синхронизации с ним, а System.nanoTime – для измерения относительных интервалов.

Валидность строки с датой

Если вам необходимо получить объект даты из обычной строки на Java, воспользуйтесь этим полезным вспомогательным классом, который возьмет на себя все сложности валидации и конвертации.

Строки

Оптимизация строк

При конкатенации строк на Java с помощью оператора +, например, в цикле for, каждый раз создается новый объект String, что приводит к потере памяти и увеличению времени работы программы.

Также следует избегать создания Java строки с помощью конструктора класса:

Одинарные и двойные кавычки

Что вы ожидаете получить от этого кода?

Казалось бы, должна вернуться строка HaHa, но на деле будет Ha169.

Двойные кавычки обрабатывают символы как строки, но одинарные ведут себя иначе. Они преобразуют символьные операнды ('H' и 'a') к целочисленным значениям через расширение примитивных типов – получается 169.

Математика

Float vs Double

Программисты часто не могут выбрать необходимую им точность для чисел с плавающей точкой. Float требует всего 4 байта, но и значащих цифр у него только 7, в то время как Double в два раза точнее (15 цифр), но и в два раза расточительнее.

На самом деле большинство процессоров способны работать с Float и Double одинаково эффективно, поэтому воспользуйтесь рекомендацией Бьёрна Страуструпа:

«Выбор нужной точности в реальных задачах требует хорошего понимания природы машинных вычислений. Если у вас его нет, либо проконсультируйтесь с кем-нибудь, либо изучите проблему сами, либо используйте double и надейтесь на лучшее.»

Проверка нечетности

Можно ли использовать этот код для точного определения нечетности числа?

Надеюсь, вы заметили подвох. Если мы решим проверить таким образом отрицательное нечетное число (-5, к примеру), остаток от деления не будет равен единице (а чему он равен?). Поэтому используйте более точный метод:

Он не только решает проблему отрицательных чисел, но и работает более продуктивно, чем его предшественник. Арифметические и логические операции выполняются гораздо быстрее умножения и деления.

Вычисление степени

Возвести число в степень можно двумя способами:

  • простым умножением;
  • с помощью функции Math.pow(double base, double exponent).

Использовать библиотечную функцию рекомендуется только при крайней необходимости, например, в случае дробной или отрицательной степени.

Простое умножение на Java работает в 300-600 раз эффективнее, к тому же его можно дополнительно оптимизировать:

JIT-оптимизация

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

В качестве примера взглянем на две простые операции:

Измерим время выполнения каждой из них:

Запустив этот код несколько раз, получим что-то подобное:

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

Подробнее об этом эксперименте вы можете почитать здесь. А провести собственное испытание можно с помощью онлайн-компилятора Java.

Структуры данных

Объединение хеш-таблиц

Объединять два хеша, итерируя их значения вручную, весьма неэффективно. Вот альтернативное решение этой задачи, которое точно вам понравится:

Array vs ArrayList

Выбор между Array и ArrayList зависит от специфики задачи на Java, которую требуется решить. Помните о следующих особенностях этих типов:

  • Array имеет фиксированный размер и память для него выделяется в момент объявления, а размер ArrayLists может динамически изменяться.
  • Массивы Java работают гораздо быстрее, а в ArrayList намного проще добавлять/удалять элементы.
  • При работе с Array велика вероятность получить ошибку ArrayIndexOutOfBoundsException.
  • У ArrayList только одно измерение, а вот массивы Java могут быть многомерными.

JSON

Сериализация и десериализация

JSON – невероятно удобный и полезный синтаксис для хранения и обмена данными. Java полностью поддерживает его.

Сериализовать данные можно так:

Получится вот такая JSON-строка:

Десериализация на Java выглядит примерно так:

JSON-файл, использованный в примере (jsonDemoFile.json):

Ввод-вывод

FileOutputStream vs. FileWriter

Запись файлов на Java осуществляется двумя способами: FileOutputStream и FileWriter. Какой именно метод выбрать, зависит от конкретной задачи.

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

У FileWriter другое призвание: работа с потоками символов. Так что если вы пишете текстовые файлы, выбирайте этот метод.

Производительность и лучшие практики

Пустая коллекция вместо Null

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

Создание объектов только при необходимости

Создание объектов – одна из самых затратных операций в Java. Лучшая практика – создавать их только при необходимости, когда они действительно нужны.

Взаимоблокировки (deadlocks)

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

Вот пример такой взаимоблокировки потоков:

Вывод этой программы:

Взаимоблокировки можно избежать, если изменить порядок вызова потоков:

Вывод:

Резервирование памяти

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

  • Xms – минимальный пул выделения памяти;
  • Xmx – максимальный пул выделения памяти;
  • XX:PermSize – начальный размер, который будет выделен при запуске JVM;
  • XX:MaxPermSize – максимальный размер, который может быть выделен при запуске JVM.

Решение распространенных задач

Содержимое директории

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

Выполнение консольных команд

Java позволяет выполнять консольные команды прямо из кода с помощью класса Runtime. При этом очень важно не забывать об обработке исключений.

Для примера попробуем открыть PDF-файл через терминал на Java:

Воспроизведение звука

Звук – важная составляющая часть многих приложений, например, игр. Язык программирования Java предоставляет средства для работы с ним.

Отправка email

Отправка электронной почты на Java очень проста. Нужно лишь установить Java Mail и указать путь к нему в classpath проекта.

Захват координат курсора

Чтобы захватить события мыши, необходимо реализовать интерфейс MouseMotionListener. Когда курсор попадает в определенную область, срабатывает обработчик события mouseMoved, из которого можно получить точные координаты.

Лучшие ресурсы и книги по Java

Интересуетесь программированием на Java?

Подпишитесь на нашу рассылку, чтобы получать больше интересных материалов:

И не беспокойтесь, мы тоже не любим спам. Отписаться можно в любое время.




Комментариев: 2

  1. Саша Серов

    private int getListOfStudents(File[] files) {
    if (files == null)
    throw new NullPointerException(«File list cannot be null»);
    }

    // better
    private int getListOfStudents(File[] files) {
    Objects.requireNonNull(fiels, «File list cannot be null»)
    }

    // Строки
    // Не показательно
    String bad = new String(«Yet another string object»);
    // быстрое инстанцирование
    String good = «Yet another string object»

    // Показательней
    String veryBad = «»
    for (int i = 0; i < 10; i++) {
    veryBad += "" + i; // Не превратится в билер
    }

    String bad = "Yet" + " another" + "string" + "object"
    String good = StringUtils.join("Yet"," another","string","object")

    "Программисты часто не могут выбрать необходимую им точность для чисел с плавающей точкой. Float требует всего 4 байта, но и значащих цифр у него только 7, в то время как Double в два раза точнее (15 цифр), но и в два раза расточительнее."(c)
    И пофиг что, плавающая точка. Какая точность? 😉

    JIT-оптимизация
    — Волшебный микро-бенчмарк. no commets ;-D

    "Array имеет фиксированный размер и память для него выделяется в момент объявления, а размер ArrayLists может динамически изменяться."(c)
    А в нутри ArrayList что, как он резайзится?))) Как заресайзить массив

    JSON Сериализация и десериализация
    "JSON – невероятно удобный и полезный синтаксис для хранения и обмена данными. Java полностью поддерживает его. Сериализовать данные можно так:"
    — А можно еще 100500 способами и либами. Если пример уж лучше Jackson или Gson в качестве примера привести

    "Ввод-вывод Запись файлов на Java осуществляется двумя способами: FileOutputStream и FileWriter. Какой именно метод выбрать, зависит от конкретной задачи."
    — А еще java.nio.file.Files и RAF

    "Создание объектов – одна из самых затратных операций в Java"
    — Вот вот ленивая инициалиазция с синхронизацией — другое дело 😀

    "Взаимоблокировки (deadlocks)"
    public static Object addLock = new Object();
    public static Object subLock = new Object();

    — ReentrantLock -не, не слышал

    1. Спасибо за комментарий! Вы не могли бы помочь в правке статьи? Будем рады сотрудничеству, пишите на [email protected].

Добавить комментарий