15 полезных практик программирования на Java для написания хорошего кода
Java с самого начала был одним из самых популярных языков программирования. В нынешние времена быстрого развития, когда многие языки программирования, которые когда-то были популярны, давно мертвы, Java по-прежнему актуален и быстро развивается в соответствии с тенденциями.
Согласно статистике, предоставленной oracle, по состоянию на декабрь 2021 года Java занимала первое место в рейтинге языков программирования для разработки приложений, таких как DevOps, искусственный интеллект, виртуальная реальность, большие данные, разработка для мобильных устройств и микросервисов.
Из-за огромной конкуренции, чтобы выделиться среди сотен разработчиков Java вокруг вас, ваша работа должна соответствовать определенным стандартам, которых вы можете достичь, следуя некоторым передовым методам написания кода на Java.
Лучшие практики Java
Код должен быть написан в соответствии с набором правил и стандартов написания кода. Применяя передовые методы разработки на Java, можно значительно улучшить качество вашего кода, сделав его более понятным для вас и других, гибким, пригодным для повторного использования и удобным в сопровождении. Некоторые передовые методы работы с Java могут даже повысить производительность кода, сделав его значительно более эффективным.
1) Используйте правильные имена переменных и классов
Прежде чем приступить к написанию кода, установите правильное для вашего проекта. Заранее определите имена для каждого класса, интерфейсов, методов, переменных и т. д. Если другие разработчики также работают с вами над одним и тем же проектом, они также должны следовать выбранному неймингу, чтобы сохранить единообразие. Осмысленное соглашение об именах чрезвычайно важно, поскольку все, от классов до интерфейсов, идентифицируется по их именам в коде.
Не назначайте случайные имена только для того, чтобы быстрее писать код, используйте осмысленные и не требующие пояснений имена, чтобы их можно было прочитать и позже понять вам и вашим товарищам по команде, инженерам по обеспечению качества и персоналу, которые будут заниматься обслуживанием проекта.
2) Доступ к классам должен быть приватным
В Java считается хорошей практикой сохранять устанавливать экземпляры классов приватными. Для этого модификатор закрытого доступа является идеальным выбором. Эта практика рекомендуется для поддержания инкапсуляции, одной из фундаментальных концепций ООП. Хотя это чрезвычайно базовая концепция объектно-ориентированного программирования, многие новые разработчики знают о ней, но по-прежнему не назначают должным образом модификаторы доступа к классам и предпочитают использовать публичные классы.
Рассмотрим этот класс :
public class Teacher {
2. public String name;
3. public String subject;
4. }
Инкапсуляция здесь нарушена, так как любой может изменить эти значения следующим образом:
1. Teacher T01 = new Teacher();
2. Teacher.name = "Sam";
3. Teacher.subject = “Science”;
Использование модификатора закрытого доступа с членами класса сохраняет поля пиватными, не позволяя пользователю изменять данные, за исключением методов установки.
Вот еще один пример использования модификатора приватного доступа:
1. public class Teacher {
2. private String name;
3. private String subject;
4.
5. public void setName(String name) {
6. this.name = name;
7. }
8. public void setSubject(String subject)
9. this.subject = subject;
10. }
11. }
3) Используйте символы подчеркивания в длинных числовых литералах
Эта функция была представлена в Java 7, что помогает писать длинные числовые литералы более понятным способом. Так что вместо того, чтобы писать старым и общепринятым способом, вот так,
int num = 58356823;
Вы можете назначить длинную числовую переменную, вот таким образом:
int num = 58_356_823;
Применение одной из таких передовых практик Java делает ваш код читабельным, хорошо структурированным и уникальным.
4) Никогда не оставляйте Catch пустым
Лучшие разработчики Java предпочитают писать правильное и осмысленное сообщение в блоке catch при обработке исключений. Неопытные разработчики часто оставляют блок catch пустым, так как изначально они единственные, кто работает над кодом, но когда исключение перехватывается пустым блоком catch и программа получает исключение, она ничего не показывает, что усложняет отладку и увеличивает время поиска ошибок.
5) Используйте StringBuilder или StringBuffer для конкатенации строк
Использование оператора «+» для объединения строк является обычной практикой во многих языках программирования, включая Java.
Это обычная практика, и это не является ошибкой, однако, когда вам необходимо объединить несколько строк, оператор «+» оказывается неэффективным, поскольку компилятор Java создает несколько промежуточных объектов String перед созданием окончательной объединенной строки.
Лучшей практикой Java в этом случае будет использование «StringBuilder» или «StringBuffer». Эти встроенные функции изменяют String без создания промежуточных объектов String, экономя время обработки и ненужное использование памяти.
Например,
1. String sql = "Insert Into Users (name, age)";
2. sql += " values ('" + user.getName();
3. sql += "', '" + user.getage();
4. sql += "')";
код выше можно было бы написать с помощью StringBuilder следующим образом:
1. StringBuilder sqlSb = new StringBuilder("Insert Into Users (name, age)");
2. sqlSb.append(" values ('").append(user.getName());
3. sqlSb.append("', '").append(user.getage());
4. sqlSb.append("')");
5. String sqlSb = sqlSb.toString();
6) Избегайте избыточных инициализаций
Хотя это очень распространенная практика, не рекомендуется инициализировать переменные значениями: 0, false и null. Эти значения уже являются значениями инициализации переменных-членов по умолчанию в Java.
7) Использование расширенных циклов for вместо циклов for со счетчиком
Цикл «For» используется с переменной-счетчиком, но уникальная передовая практика Java ,используемая каждым хорошим разработчиком Java, заключается в использовании расширенного цикла for вместо старого простого цикла For. Как правило, нет никакой разницы в использовании любого из них, но в некоторых случаях используемая переменная-счетчик может быть подвержена ошибкам. Переменная счетчика может измениться, она может использоваться позже в коде, или вы можете начать индекс с 1 вместо 0, что приведет к нарушению кода в нескольких местах. Чтобы устранить это, рекомендуется использовать расширенный цикл for.
Рассмотрим следующий фрагмент кода:
1. String[] names = {"Sam", "Mike", "John"};
2. for (int i = 0; i < names.length; i++) {
3. method1(names[i]);
4. }
Здесь переменная «I» используется как счетчик для цикла, а также как индекс для имен массивов. Позже в коде могут возникнуть коллизии, поэтому мы можем избежать потенциальных проблем, используя расширенный цикл for, как показано ниже:
1. For (String Name1 : names) {
2. Method1(Name1);
3. }
8) Правильная обработка исключений
Null Pointer Exceptions очень распространенная ошибка в Java. Это исключение возникает в результате попытки вызвать метод для нулевой ссылки на объект. Например, см. строку кода, указанную ниже,
int noOfEmployees = office.listEmployees().count;
В этой строке нет ошибок, но если либо объект «office», либо метод «listEmployees()» имеет значение Null, тогда код выдаст Null Pointer Exceptions.
Во-первых, важно проверять Null Pointer Exceptions , чтобы их можно было устранить, и изменить код, чтобы код обрабатывал их.
Улучшенная версия кода показана ниже:
1. private int getListOfEmployees(File[] files) {
2. if (files == null)
3. throw new NullPointerException("File list cannot be null");
9) Float or Double: правильный выбор?
Часто неопытные разработчики не могут отличить Double от Float, они знают основы, но когда дело доходит до их использования, они обычно выбирают один и тот же тип в каждом случае.
В Java рекомендуется использовать float и Double в соответствии с решаем Float and double , но double обеспечивает гораздо большую точность, чем Float, поэтому рекомендуется использовать Double, когда точность вычисляемых значений важна, в другом случае вы можете использовать Float, поскольку для этого требуется вполовину меньше памяти, чем для double.
10) Использование одинарных и двойных кавычек
Мы все знаем, что двойные кавычки используются для объявления строк, а одинарные — для символов, но в некоторых уникальных случаях это может быть не так. Когда требуется объединить символы для создания строки, рекомендуется использовать двойные кавычки для символов, которые мы собираемся объединить. Причина этого в том, что если будут использоваться двойные кавычки, символы будут рассматриваться как простая строка, и проблем не будет, но если вы используете одинарные кавычки, целочисленные значения символов будут учитываться из-за процесса, называемого расширением примитивного преобразования.
1. public class classA {
2. public static void main(String args[]) {
3. System.out.print("A" + "B");
4. System.out.print('C' + 'D');
5. }
6. }
вывод на экране должен быть ABCD , но вы увидите AB135, потому что C и D заключены в одинарные кавычки, их значения ASCIIи складываются вместе из-за оператора «+», что выводит AB135 на экране.
11) Предотвращение утечек памяти
Java разработчики не имеют большого контроля над управлением памятью, поскольку Java управляет памятью автоматически. Тем не менее, есть некоторые передовые методы Java, используемые ведущими разработчиками для предотвращения утечек памяти, поскольку утечка может привести к значительному снижению производительности. Всегда завершать соединения с базой данных после того, как ваш запрос выполнен, Использование блока finally как можно чаще и удаления переменных, хранящихся в статических таблицах, — это некоторые из методов программирования на Java, которые вы должны адаптировать, чтобы предотвратить утечку памяти.
12) Возвращайте Optional вместо null
При каждом добавлении в базу кода нового метода, способного вернуть пустое значение, вам следует стараться использовать вместо null тип Optional. Этот тип был введен, чтобы разработчики могли указывать на возможность возвращения методом пустого значения, что ранее было недоступно (либо требовало аннотаций, о которых я расскажу ниже). Существует два вида объектов Optional
: содержащие значение и без него. Первые изначально создаются со значением, вторые же просто являются статическими одиночками. Структурно типичный случай их использования выглядит так:
if (checkSomeCondition(someValue)){
return Optional.of(someValue);
} else {
return Optional.empty();
}
Чтобы увидеть, почему сигнатура с Optional
предпочтительней, представьте следующий метод:
public Admission getAdmission(Patient patient)
Из этой сигнатуры понятно лишь, что она возвращает объект типа Admission
. При этом, как уже говорилось, null
соответствует любому типу. У нас нет иной возможности узнать, возвращает ли данный метод null
, кроме как проанализировать его реализацию. В противоположность приведенному примеру, метод, возвращающий Optional
, будет выглядеть так:
public Optional<Admission> getAdmission(Patient patient)
Поскольку смысл использовать Optional
есть только в случае возможного возврата пустого значения, значит метод такое значение вернуть может. Поэтому при каждом возвращении Optional
методом вы знаете, что есть вероятность получения пустого значения и его нужно должным образом обработать.
Возвращаемое значение Optional
обрабатывается аналогично null
: вы проверяете, передано ли значение, и, если да — выполняете с ним определенные действия. Вызов метода, возвращающего null
, как правило, выглядит так:
Admission admission = getAdmission(Patient patient);
if (admission != null) {
// действия с данными о госпитализации
}
Со значением Optional
соответствующий код будет выглядеть так:
Optional<Admission> admissionOptional = getAdmission(Patient patient);
if (admissionOptional.isPresent()) {
Admission admission = admissionOptional.get();
// действия с данными о госпитализации
}
13) Эффективное использование строк c indexOf и lastIndexOf
В класс String включена поддержка поиска определенного символа или подстроки, для этого в нем имеются два метода — indexOf и lastIndexOf. Каждый из этих методов возвращает индекс того символа, который вы хотели найти, либо индекс начала искомой подстроки. В любом случае, если поиск оказался неудачным методы возвращают значение -1. В очередном примере показано, как пользоваться различными вариантами этих методов поиска.
class indexOfDemo {
public static void main(String args[]) {
String s = “Now is the time for all good men ” +
“to come to the aid of their country “ +
“and pay their due taxes.”;
System.out.println(s);
System.out.println(“indexOf(t) = ” + s.indexOf(‘f’));
System.out.println(“lastlndexOf(t) = ” + s.lastlndexOf(‘f’));
System.out.println(“indexOf(the) = ” + s.indexOf(“the”));
System.out.println(“lastlndexOf(the) = ” + s.lastlndexOf(“the”));
System.out.println(“indexOf(t, 10) = ” + s.indexOf(‘f’ , 10));
System.out.println(“lastlndexOf(t, 50) = ” + s.lastlndexOf(‘f’ , 50));
System.out.println(“indexOf(the, 10) = ” + s.indexOf(“the”, 10));
System.out.println(“lastlndexOf(the, 50) = ” + s.lastlndexOf(“the”, 50));
} }
Ниже приведен результат работы этой программы. Обратите внимание на то, что индексы в строках начинаются с нуля.
С:> java indexOfDemo
Now is the time for all good men to come to the aid of their country
and pay their due taxes.
indexOf(t) = 7
lastlndexOf(t) = 87
indexOf(the) = 7
lastlndexOf(the) = 77
index0f(t, 10) = 11
lastlndex0f(t, 50) = 44
index0f(the, 10) = 44
lastlndex0f(the, 50) = 44
14 )Избегайте Создание ненужных объектов
Создание объекта — одна из операций, потребляющих больше всего памяти, поэтому лучшая практика Java — избегать создания ненужных объектов, и их следует создавать только тогда, когда это необходимо.
Некоторые объекты являются более дорогостоящими, чем создание других объектов. Если вы хотите использовать такой «дорогой объект», рекомендуется кэшировать его для повторного использования. К сожалению, это не всегда интуитивно при создании такого объекта. Предположим, вы хотите, чтобы написать метод, чтобы определить, является ли строка действительный номер Roman. Ниже это самый простой способ, чтобы завершить эту работу, используя регулярное выражение:
// производительность может быть значительно улучшена!static boolean isRomanNumeral(String s) { return s.matches("^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");}
Эта проблема реализации является то, что она опирается на метод string.matches. Хотя string.matches это самый простой способ проверить, является ли строка согласуется с регулярными выражениями, он не подходит для повторного использования в случае критической. Проблема заключается в том, что он создает экземпляр шаблона внутри регулярных выражений и использует только один раз, а затем он имеет право на сбор мусора. Создание экземпляра шаблона является дорогостоящим, поскольку она должна составлять регулярные выражения в конечные автоматы.
Для того, чтобы повысить производительность, как часть инициализации класса, регулярное выражение явно компилировать в экземпляр шаблона (не переменный), кэш, и повторить тот же экземпляр в каждом вызове метода ISROMANNUMERAL:
// поступает дорогой объект для повышения производительностиpublic class RomanNumerals { private static final Pattern ROMAN = Pattern.compile( "^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$"); static boolean isRomanNumeral(String s) { return ROMAN.matcher(s).matches(); }
15) Правильное комментирование
Поскольку ваш код будут читать разные люди с разным знанием Java, следует использовать надлежащие комментарии, чтобы предоставить дополнительную информацию, которую невозможно понять из самого кода. Комментарии должны описывать работу вашего кода, чтобы их могли прочитать программист , тестировщик или вы сами. Здесь можно почитаь про все виды комментирования в java: https://upread.ru/art.php?id=28