Статьи

Применимость функций Java

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

Обратите внимание, что я сосредоточен только на Java, JVM и JDK. Я не предлагаю какие рамки и насколько вероятно, вы должны использовать. Кроме того, я предполагаю, что типичное серверное приложение для бизнеса.

Дня в день

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

  • классы, интерфейсы, пакеты — серьезно. Поместите свой код в классы. Вы помните из университета, что класс представляет собой инкапсулированные данные + методы, действующие на эти данные? Класс с единственным состоянием — это просто структура. Класс только с методами — это просто пространство имен, включающее функции. Также используйте интерфейсы всякий раз, когда это необходимо. Но подумайте дважды, прежде чем создавать интерфейс только с одной реализацией. Может быть, вам не нужен посредник? Тем не менее, поместите все в пакеты, следуя хорошо установленному соглашению об именах .
  • статические методы — не бойся их. Но используйте их только для утилит без использования состояния. Никогда не кодируйте бизнес-логику внутри static метода.
  • ExecutorService — пулы потоков — создание и эффективное использование пулов потоков, понимание того, как работает организация очередей и Future<T> является обязательным. Не переопределяйте пулы потоков, думайте о них каждый раз, когда кто-то говорит производитель-потребитель .
  • Atomic — * семейство — не используйте synchronized чтобы только прочитать / обновить некоторый счетчик или ссылку атомарно. Семейство классов Atomic — * использует эффективные низкоуровневые инструкции сравнения и обмена, чтобы быть удивительно эффективными. Убедитесь, что вы понимаете гарантии, предоставляемые этими классами.
  • шаблоны проектирования — не технически часть языка Java, но существенная. Вы должны знать, понимать и использовать их охотно, но экономно. Так же, как с интерфейсами — не переусердствуйте. Шаблоны GoF или даже EI часто должны встречаться в базе кода. Но позвольте шаблонам появляться во время вашего мыслительного процесса, а не позволять вашему мыслительному процессу руководствоваться шаблонами.
  • встроенные коллекции, в том числе параллельные — вы обязательно должны знать и использовать встроенные коллекции, понимая различия между List , Map и Set . Использование многопоточных коллекций не должно быть проблемой для вас. Понимать характеристики производительности и иметь общий обзор реализации за ними. Это действительно просто. Также знайте и используйте различные реализации BlockingQueue . Параллелизм сложен, не усложняйте его, выполняя некоторые из этих вещей самостоятельно.
  • Встроенные аннотации — аннотации здесь, чтобы остаться, научиться использовать @Override@Deprecated в некоторой степени) каждый день последовательно.
  • исключения — используйте непроверенные исключения, чтобы сигнализировать о ненормальном, исключительном сбое, требующем принятия мер. Учитесь жить с проверенными исключениями. Учитесь читать следы стека.
  • попробуй с ресурсами — познакомься с этой сказочной языковой конструкцией. AutoCloseable если ваш класс требует какой-либо очистки.
  • Блокировка ввода-вывода — с помощью Reader / Writer классы InputStream / OutputStream — это то, с чем вы должны быть действительно знакомы. Поймите разницу между ними, используя буферизацию и другие декораторы без страха.

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

Время от времени

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

  • наследование и абстрактные классы — действительно, оказывается, я не использую наследование так часто, и я действительно не скучаю по нему. Полиморфизм, управляемый интерфейсами, гораздо более гибок, особенно при болезненном отсутствии черт в Java. Также предпочитаю композицию наследству . Слишком много уровней наследования приводят к очень не поддерживаемому коду.
  • регулярные выраженияНекоторые люди, сталкиваясь с проблемой, думают: «Я знаю, я буду использовать регулярные выражения». Теперь у них две проблемы. , Мир без регулярных выражений был бы гораздо более скучным и громоздким. Они прекрасно подходят для синтаксического анализа обычных языков (но не HTML ), но их слишком легко переусердствовать. Если вы обнаруживаете, что разрабатываете, тестируете, исправляете и работаете целый день перед регулярными выражениями, вы, вероятно, используете для своей работы не тот инструмент. Мой любимый на все времена:
    1
    2
    3
    public static boolean isNegative(int x) {
        return Integer.toString(x).matches('-[0-9]+');
    }
  • Semaphore , CountDownLatch , CyclicBarrier и другие — все они чрезвычайно полезны на порядок лучше, чем парная очередь wait() / notify notify() . Но даже они не защитят вас от ошибок параллелизма при злоупотреблении. Рассматривайте поточно-ориентированные коллекции или некоторые фреймворки, когда вы слишком часто видите механизм синхронизации.
  • универсальные типы в пользовательском коде — использование встроенных коллекций и других классов, имеющих универсальные типы, должно быть не только повседневной практикой, но и очевидным для вас. Но я имею в виду разработку кода самостоятельно, принимая или возвращая универсальные типы. Что-то вроде этого:
    1
    public <T, F> ContractValidator<T extends Contract> T validate(Validator<T>, F object)

    Иногда необходимо использовать дженерики в вашем собственном коде, но не слишком мета . Конечно, статическая типизация и безопасность типов должны быть вашим приоритетом, но, может быть, вы можете избежать слишком большого количества общих, сложных типов?

  • Язык сценариев в JVM — знаете ли вы, что JDK имеет встроенный интерпретатор JavaScript? И что вы можете подключить практически любой другой язык, как Groovy или JRuby? Иногда проще встроить небольшой скрипт в ваше приложение, которое может изменить даже клиент. Это не часто, но на очень быстро меняющихся рынках передислокация может быть невозможной. Просто помните, что если общее количество строк скриптового кода превышает 1% от общего объема вашего кода, вам следует начать беспокоиться о поддержке.
  • Java NIO — трудно понять это правильно, и еще сложнее извлечь из этого пользу. Но в редких случаях вам действительно нужно использовать NIO, чтобы максимально снизить производительность и масштабируемость. Однако предпочитайте библиотеки, которые могут сделать это за вас. Также в обычных условиях блокировка ввода-вывода обычно достаточно.
  • synchronized ключевое слово — вы не должны использовать его слишком часто по простой причине. Чем чаще он используется, тем чаще он выполняется, что влияет на производительность. Вместо этого рассмотрим поточно-ориентированные коллекции и атомарные примитивные оболочки. Также убедитесь, что вы всегда понимаете, какой объект используется в качестве мьютекса.

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

Никогда (подумайте: только разработчики фреймворков и библиотек)

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

  • розетки — серьезно, розетки. Вы должны понимать, как работает стек TCP / IP, быть очень внимательным в отношении потоков, соблюдать осторожность при интерпретации данных, быть бдительными с потоками. Избегайте использования чистых сокетов, есть сотни библиотек, обертывающих их и предоставляющих абстракции более высокого уровня — HTTP, FTP, NTP, SMB, электронная почта… (например, см. Сеть Apache Commons ). Вы будете удивлены, насколько сложно написать достойный HTTP-клиент или сервер. И если вам нужно написать сервер для какого-то проприетарного протокола, обязательно рассмотрите Netty .
  • отражение — в бизнес-коде нет места для анализа классов и методов. Рамки не могут жить без отражения, я не могу жить с. Отражение делает ваш код медленным, небезопасным и безобразным. Обычно АОП достаточно. Я бы даже сказал, что передача экземпляров Class<T> вокруг — это запах кода.
  • динамические прокси и манипулирование байтовым кодомкласс Proxy великолепен, но, как и отражение, должен использоваться только теми фреймворками и библиотеками, которые поддерживают вас. Они являются основным строительным блоком легких АОП. Если ваше бизнес-приложение (не фреймворк или библиотека, даже Mockito использует эти методы!) Требует генерации или манипуляции с байтовым кодом (например, ASM или CGLIB ) — ты в глубоком дерьме Я буду молиться за вас.
  • загрузчики классов — все, что имеет отношение к загрузчикам классов. Вы должны понимать их, иерархию, байт-код и т. Д. Но если вы пишете свои собственные загрузчики классов, это дорога в ад. Не то чтобы это было так сложно, но это, вероятно, не нужно. Оставьте это серверам приложений.
  • Object.html#clone() — честно говоря, я не помню, использовал ли я этот метод всю свою жизнь (Java-разработчика). Я просто … не … И я не могу найти никаких оснований для его использования. У меня либо есть явный конструктор копирования, либо лучше использовать неизменяемые объекты. У вас есть законные варианты использования? Кажется, так 1990-е …
  • нативные методы — в JDK есть несколько, даже для таких небольших задач, как вычисление синусоидальной функции . Но Java больше не самый медленный ребенок в классе, на самом деле все наоборот. Кроме того, я не могу представить, какую логику вам нужно, чего нельзя достичь с помощью стандартной библиотеки или сторонних библиотек. Наконец, нативные методы довольно сложно понять, и вы можете ожидать низкоуровневых, неприятных ошибок, особенно в отношении управления памятью.
  • Пользовательские коллекции — реализовать новую коллекцию, следуя всем контрактам, определенным в оригинальном JavaDoc, на удивление сложно . Фреймворки, такие как Hibernate, используют специальные постоянные коллекции. Очень редко вам нужна коллекция, настолько специфичная для ваших требований, чтобы ни один из встроенных не был достаточно хорошим.
  • ThreadLocal — библиотеки и фреймворки довольно часто используют локальные потоки. Но вы никогда не должны пытаться использовать их по двум несвязанным причинам. Прежде всего, ThreadLocal часто является скрытым полуглобальным параметром, в который вы хотите проникнуть. Это делает ваш код труднее рассуждать и тестировать. Во-вторых, ThreadLocal s может легко вызывать утечки памяти, когда не очищается должным образом (см. Это , это , это и это и…)
  • WeakReference и SoftReference — эти классы довольно низкоуровневые и отлично подходят для реализации кешей, хорошо играющих со сборкой мусора. К счастью, существует множество кеширующих библиотек с открытым исходным кодом, поэтому вам не нужно писать их самостоятельно. Поймите, что делают эти классы, но не используйте их.
  • com.sun.* и sun.* , особенно sun.misc.Unsafe — держитесь подальше от этих пакетов, просто … не ходите туда. Нет никаких причин исследовать эти проприетарные, недокументированные и не гарантирующие сохранение классов обратной совместимости. Просто притворись, что их там нет. И почему вы используете Unsafe ?

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

Ссылка: Java показывает применимость от нашего партнера JCG Томаша Нуркевича из блога Java и соседей .