Рубрики
Без рубрики

Краткий справочник по лучшим практикам Java

Разработчики несут большую ответственность за то, чтобы каждый день принимать правильные решения и лучше всего помогать… С пометкой java, лучшие практики, технические достижения.

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

Принципы программирования

Не пишите код, который работает только . Стремитесь написать код, который может поддерживаться — не только вами, но и кем-либо еще, кто может в конечном итоге работать над программным обеспечением в какой-то момент в будущем.

80% времени разработчик читает код и 20% пишет и тестирует код. Поэтому, пожалуйста, сосредоточьтесь на написании читаемого кода!

Ваш код не должен нуждаться в комментариях, чтобы понять, что он делает!

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

  • ПОЦЕЛУЙ — Это расшифровывается как “Будь проще, глупый”. Вы можете заметить, что разработчики в начале своего пути пытаются реализовать сложный, неоднозначный дизайн.
  • СУХОЙ — “Не Повторяйся”. Старайтесь избегать любых дубликатов, вместо этого вы помещаете их в одну часть системы или метода.
  • ЯГНИ — “Тебе Это Не Понадобится”. Если вы столкнетесь с ситуацией, когда вы спрашиваете себя: “Как насчет добавления дополнительных (функций, кода и т. Д.)? “, вам, вероятно, нужно это переосмыслить.
  • Чистый код поверх умного кода — Говоря о чистом коде, оставьте свое эго за дверью и забудьте о написании умного кода .
  • Избегайте преждевременной оптимизации — Проблема с преждевременной оптимизацией заключается в том, что вы никогда не сможете по-настоящему узнать, где будут узкие места программы, до тех пор, пока это не произойдет.
  • Единая ответственность — Каждый класс или модуль в программе должен заботиться только о предоставлении одного бита определенной функциональности.
  • Композиция вместо наследования — Объекты со сложным поведением должны содержать экземпляры объектов с индивидуальным поведением, а не наследовать класс и добавлять новые модели поведения.
  • Объектная гимнастика — Объектная гимнастика – это упражнения по программированию , формализованные в виде набора 9 правил
  • Терпеть неудачу быстро, терпеть тяжелую неудачу Принцип отказоустойчивости означает остановку текущей операции, как только возникает какая-либо непредвиденная ошибка. Соблюдение этого принципа обычно приводит к более стабильному решению

Пакеты

  1. Отдавайте предпочтение структурированию пакетов по проблемам домена , а не по техническим уровням.
  2. Отдавайте предпочтение макетам, которые способствуют инкапсуляции и скрытию информации для защиты от неправильного использования, а не организации классов по техническим причинам.
  3. Рассматривайте пакеты как предоставляющие строгий API—интерфейс – не раскрывайте внутреннюю работу (классы), которые предназначены только для внутренней обработки.
  4. Не общедоступная область доступа для классов, которые должны использоваться только внутри пакета.

Занятия

Статический

  1. Не разрешайте создание экземпляра статического класса. Всегда создавайте частный конструктор.
  2. Статические классы должны быть без состояния, неизменяемыми, не допускать создания подклассов и потокобезопасными.
  3. Статические классы должны быть свободны от побочных эффектов и предоставляться в виде утилит, таких как фильтрация списка.

Наследование

  1. Предпочитайте композицию наследованию.
  2. Не открывайте защищенные поля. Вместо этого предоставьте защищенный средство доступа.
  3. Если переменная класса может быть помечена как окончательная , сделайте это.
  4. Если наследование не ожидается, сделайте класс окончательным .
  5. Отметьте метод final , если только подклассам не будет разрешено его переопределять.
  6. Если конструктор не требуется, не создавайте конструктор по умолчанию без логики реализации. Java автоматически предоставит конструктор по умолчанию, если он не указан.

Интерфейсы

  1. Не используйте шаблон интерфейса констант (интерфейс констант), поскольку он позволяет классам реализовывать и загрязнять API. Вместо этого используйте статический класс. Это дает дополнительное преимущество, позволяя выполнять более сложную инициализацию объекта в статическом блоке (например, заполнение коллекции).
  2. Избегайте чрезмерного использования интерфейса
  3. Наличие одного и только одного класса, реализующего интерфейс, может привести к чрезмерному использованию интерфейсов, и это приносит больше вреда, чем пользы. Больше
  4. “Программа для интерфейса, а не для реализации” не означает, что вы должны сопоставлять каждый из ваших классов домена с более или менее идентичным интерфейсом, делая это, вы нарушаете ЯГНИК
  5. Всегда делайте интерфейсы небольшими и специфичными, чтобы клиенты могли знать только о тех методах, которые их интересуют. Проверьте интернет-провайдера из SOLID.

Финализаторы

  1. Объект#finalize() следует использовать разумно и только в качестве средства защиты от сбоев для очистки ресурсов (например, закрытия файла). Всегда предоставляйте явный метод очистки (например, close()).
  2. В иерархии наследования всегда вызывайте функцию finalize() родителя в блоке try . Очистка класса должна быть в блоке finally .
  3. Если явный метод очистки не был вызван и завершитель закрыл ресурсы, запишите эту ошибку в журнал.
  4. Если регистратор недоступен, используйте обработчик исключений потока (который в конечном итоге делегирует стандартную ошибку , которая фиксируется в журналах).

Общие

Утверждения

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

Состояние объекта:

  • Объект никогда не должен создаваться или переходить в недопустимое состояние.
  • Что касается конструкторов и методов, всегда описывайте и применяйте контракт с помощью проверок.
  • Следует избегать ключевого слова Java assert , так как оно может быть отключено и, как правило, является хрупкой конструкцией.
  • Используйте служебный класс Утверждения , чтобы избежать подробных условий if-else для проверки предварительных условий.

Дженерики

Полное, чрезвычайно подробное объяснение доступно в Java Generics FAQ . Ниже приведены общие сценарии, о которых должны знать разработчики.

  1. Когда это возможно, предпочитайте использовать вывод типа, а не возвращать базовый класс/интерфейс:
// MySpecialObject o = MyObjectFactory.getMyObject();
public  T getMyObject(int type) { 
 return (T) factory.create(type);
}
  1. Если тип не может быть выведен автоматически, встроите его.
public class MySpecialObject extends MyObject {
 public MySpecialObject() {
 super(Collections.emptyList());   // This is ugly, as we loose type
 super(Collections.EMPTY_LIST();    // This is just dumb
 // But this is beauty
 super(new ArrayList()); 
 super(Collections.emptyList());
 }
}
  1. Подстановочные знаки: Используйте расширенный подстановочный знак, когда вы получаете значения только из структуры, используйте супер подстановочный знак, когда вы помещаете значения только в структуру, и не используйте подстановочный знак, когда вы делаете и то, и другое.

  2. Все любят ГРУДНЫЕ мышцы ! ( Производитель – расширяет, Потребитель – супер)

  3. Использовать Foo расширяет T> для производителя T. расширяет T>

  4. для производителя T. Использовать Foo супер T>

Одиночки

Синглтон никогда не должен быть написан в классическом стиле Шаблоны проектирования , что вполне допустимо в C++ (как это было написано), но неуместно в Java.

  1. При правильной потокобезопасности никогда не выполняйте следующее. (Это было узким местом в производительности!)
public final class MySingleton {
 private static MySingleton instance;
 private MySingleton() {
   // singleton
 }

 public static synchronized MySingleton getInstance() {
 if (instance == null) {
   instance = new MySingleton();
 }
   return instance;
 }
}
  1. Если ленивая инициализация действительно желательна, то комбинация двух подходов выполнит эту работу!
public final class MySingleton {
 private MySingleton() {
   // singleton
 }
 private static final class MySingletonHolder {
   static final MySingleton instance = new MySingleton();
 } 

 public static MySingleton getInstance() {
   return MySingletonHolder.instance;
 }
}
  1. Весна: По умолчанию компонент зарегистрирован с одноэлементной областью, что означает, что контейнер создаст только один экземпляр и подключит его ко всем потребителям. Это обеспечивает ту же семантику, что и обычный синглтон, без ограничений производительности или связи.

Исключения

  1. Используйте проверенные исключения для восстанавливаемых условий и исключения во время выполнения для ошибок программирования. Пример: Получение целого числа из строки.
  • Плохой: Исключение NumberFormatException расширяет исключение RuntimeException, поэтому оно предназначено для указания на ошибки программирования.
  • Не делайте следующих действий:
// String str = input string
Integer value = null;
try {
 value = Integer.valueOf(str);
} catch (NumberFormatException e) {
  // non-numeric string
}
if (value == null) {
  // handle bad string
} else {
   // business logic
}
  • Правильное использование:
// String str = input string

// Numeric string with at least one digit and optional leading negative sign
if ( (str != null) && str.matches("-?\\d++") ) { 
 Integer value = Integer.valueOf(str);
 // business logic
} else {
  // handle bad string
}
  1. Вы должны обрабатывать исключения в нужном месте, нужное место находится на уровне домена.
  • НЕПРАВИЛЬНЫЙ ПУТЬ — Слой данных объектов не знает, что делать, когда возникает исключение базы данных.
class UserDAO{
 public List getUsers(){
 try{
   ps = conn.prepareStatement("SELECT * from users");
   rs = ps.executeQuery();
   //return result
 }catch(Exception e){
   log.error("exception")
   return null
   }finally{
     //release resources
   }
  }
 }
  • РЕКОМЕНДУЕМЫЙ СПОСОБ — Уровень данных должен просто повторно создать исключение и передать ответственность за обработку исключения или нет на нужный уровень.
class UserDAO{
 public List getUsers(){

 try{
   ps = conn.prepareStatement("SELECT * from users");
   rs = ps.executeQuery();
   //return result
   }catch(Exception e){
         throw new DataLayerException(e);
     }finally{
       //release resources
    }
  }
}
  1. Исключения, как правило, должны регистрироваться НЕ в момент их возникновения, а в момент их фактической обработки. Регистрация исключений, когда они выбрасываются или повторно отбрасываются, имеет тенденцию заполнять файлы журналов шумом. Также обратите внимание, что трассировка стека исключений фиксирует, где исключение было сгенерировано в любом случае.

  2. Одобряйте использование стандартных исключений

  3. Используйте исключения, а не коды возврата.

Равно и хэш-код

Существует ряд проблем, о которых следует знать при написании надлежащих методов эквивалентности объектов и хэш-кода. Чтобы упростить использование, используйте java.util. Объекты’ равны и хэш .

public final class User {
 private final String firstName;
 private final String lastName;
 private final int age;
 ...
 public boolean equals(Object o) {

 if (this == o) {
   return true;
 } else if (!(o instanceof User)) {
   return false;
 }
 User user = (User) o;
 return Objects.equals(getFirstName(), user.getFirstName()) && 
 Objects.equals(getLastName(),user.getLastName()) &&
 Objects.equals(getAge(), user.getAge());
 }

 public int hashCode() {
   return Objects.hash(getFirstName(),getLastName(),getAge());
 }
}

Управление ресурсами

  1. Методы безопасного высвобождения ресурсов:
  2. Инструкция try-with-resources гарантирует, что каждый ресурс будет закрыт в конце инструкции. Любой объект, реализующий java.lang. Автоматически закрываемый, который включает в себя все объекты, реализующие java.io .Закрываемый, может быть использован в качестве ресурса.
private doSomething() {

try (BufferedReader br = new BufferedReader(new FileReader(path))) {

 try {
   // business logic
 }
}

Обеспечьте Крючки Отключения

Предоставьте перехват завершения работы , который будет вызван, если JVM будет корректно завершена. (Это не будет обрабатывать внезапные отключения, например, из-за отключения электроэнергии)

Это рекомендуемая альтернатива вместо объявления метода finalize() , который будет выполняться только в том случае, если System.runFinalizersOnExit() имеет значение true (по умолчанию равно false).

public final class SomeObject {
 var distributedLock = new ExpiringGeneralLock ("SomeObject", "shared");
 public SomeObject() {

 Runtime
  .getRuntime()
 .addShutdownHook(new Thread(new LockShutdown(distributedLock)));
 }

 /** Code may have acquired lock across servers */
 ...

 /** Safely releases the distributed lock. */

 private static final class LockShutdown implements Runnable {

 private final ExpiringGeneralLock distributedLock;

 public LockShutdown(ExpiringGeneralLock distributedLock) {
 if (distributedLock == null) {
   throw new IllegalArgumentException("ExpiringGeneralLock is null");
 }
   this.distributedLock = distributedLock;
 }
 public void run() {
  if (isLockAlive()) {
   distributedLock.release();
  }
 }
 /** @return True if the lock is acquired and has not expired yet. */

 private boolean isLockAlive() {
   return distributedLock.getExpirationTimeMillis() > System.currentTimeMillis();
 }
 }
}

Разрешить ресурсам истекать (а также быть возобновляемыми) совместно используется серверами. (Это позволяет восстанавливаться после внезапного прерывания, такого как перебои в подаче электроэнергии).

Смотрите пример кода выше, в котором используется Общая блокировка с истекающим сроком действия (блокировка, которая является общей для всех систем).

Дата-Время

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

Совпадение

Общие

  1. Остерегайтесь следующих библиотек, которые на удивление не являются потокобезопасными. Всегда синхронизируйте с объектами, если они совместно используются несколькими потоками.
  2. Дата ( не неизменяемая ) — Используйте новый потокобезопасный API даты и времени;
  3. SimpleDateFormat — Используйте новый потокобезопасный API даты и времени;
  4. Предпочитаю использовать классы java.util.concurrent.atomic , а не создавать переменные volatile .
  5. Поведение атомарных классов более очевидно для среднего разработчика, тогда как volatile требует понимания модели памяти Java.
  6. Атомарные классы обертывают переменные volatile в более удобный интерфейс.
  7. Поймите варианты использования, в которых volatile подходит. (см. статью )
  8. Используйте вызываемый, когда требуется проверяемое исключение, но нет возвращаемого типа. Поскольку экземпляр Void не может быть создан, это сообщает о намерении и может безопасно возвращать null .

Нити

  1. java.язык. Поток следует считать амортизированным. Хотя официально это не так, почти во всех случаях пакет java.util.concurrent обеспечивает более чистое решение проблемы.
  2. Считается плохой практикой расширять java.язык. Поток — вместо реализации Запускаемый и создайте новый поток с экземпляром в конструкторе (правило композиции над наследованием).
  3. Предпочитайте исполнителей и потоки, когда требуется параллельная обработка
  4. Всегда рекомендуется указать собственную фабрику пользовательских потоков для управления конфигурацией создаваемых потоков. Больше
  5. Используйте DaemonThreadFactory в исполнителях для некритических потоков, чтобы пул потоков можно было немедленно отключить при завершении работы сервера. больше
this.executor = Executors.newCachedThreadPool((Runnable runnable) -> {

 Thread thread = Executors.defaultThreadFactory().newThread(runnable);
 thread.setDaemon(true);
 return thread;
});
  1. Синхронизация Java больше не медленная (55-110 нс). Не избегайте этого, используя сломанные трюки, такие как двойная блокировка .
  2. Предпочитайте синхронизацию с внутренним объектом, а не с классом, так как пользователи могут синхронизироваться с вашим классом/экземпляром.
  3. Всегда синхронизируйте несколько объектов в одном и том же порядке, чтобы избежать взаимоблокировок.
  4. Синхронизация с классом по своей сути не блокирует доступ к его внутренним объектам. Всегда используйте одни и те же блокировки при доступе к ресурсу.
  5. Имейте в виду, что ключевое слово synchronized не считается частью сигнатуры метода и, следовательно, не будет унаследовано.
  6. Избегайте чрезмерной синхронизации, это может привести к снижению производительности и взаимоблокировке. Используйте ключевое слово synchronized строго для фрагмента кода, требующего синхронизации.

Коллекции

  1. Используйте параллельные коллекции Java-5, когда это возможно в многопоточном коде. Они безопасны и обладают превосходной производительностью.
  2. Используйте CopyOnWriteArrayList вместо synchronizedList, когда это необходимо
  3. Используйте Collections.unmodifiablelist(…) или скопируйте коллекцию при ее получении в качестве параметра новый список массивов(список) . Избегайте изменения локальных коллекций извне вашего класса.
  4. Всегда возвращайте копию своей коллекции, избегая изменения списка извне новый список массивов(список)
  5. Каждая коллекция должна быть заключена в свой собственный класс, так что теперь поведение, связанное с коллекцией, имеет дом (например, методы фильтрации, применение правила к каждому элементу).

Разнообразный

  1. Предпочитаете лямбды анонимным классам
  2. Предпочитаю ссылки на методы для лямбд
  3. Используйте перечисления вместо констант int.
  4. Избегайте использования float и double, если требуются точные ответы, вместо этого используйте BigDecimal, например, деньги
  5. Предпочитайте примитивные типы упакованным примитивам
  6. Следует избегать использования магических чисел в коде. Используйте константы
  7. Не возвращайте значение Null. Общайтесь с вашим клиентом по методу с помощью Необязательно . То же самое для коллекций — Возвращайте пустые массивы или коллекции, а не нули
  8. Избегайте создания ненужных объектов, повторного использования объектов и избегайте ненужной очистки GC

Отложенная инициализация

Отложенная инициализация – это оптимизация производительности. Он используется, когда данные по какой-либо причине считаются “дорогими”. В Java 8 мы должны использовать для этого функциональный интерфейс поставщика.

== Thread safe Lazy initialization ===

public final class Lazy {
 private volatile T value;
 public T getOrCompute(Supplier supplier) {
   final T result = value; // Just one volatile read
   return result == null ? maybeCompute(supplier) : result;
 }

 private synchronized T maybeCompute(Supplier supplier) {
 if (value == null) {
   value = supplier.get();
 }
   return value;
 }
}
Lazy lazyToString= new Lazy<>()
return lazyToString.getOrCompute( () -> "(" + x + ", " + y + ")");

На этом пока все, надеюсь, это было полезно!

Бесплатный Продвинутый курс Java

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

Лекции этого курса основаны на торговой системе, проекте с открытым исходным кодом, размещенном на моем Github .

Оригинал: “https://dev.to/apssouza22/java-best-practices-quick-reference-55hh”