Стиль кода Java AOSP для участников

Стили кода на этой странице представляют собой строгие правила добавления кода Java в проект Android с открытым исходным кодом (AOSP). Вклады в развитие платформы Android, не соответствующие этим правилам, как правило, не принимаются . Мы понимаем, что не весь существующий код соответствует этим правилам, но мы ожидаем, что весь новый код будет им соответствовать. См. «Кодирование» с примерами терминологии, которую следует использовать и которой следует избегать для создания более инклюзивной экосистемы.

Быть последовательным

Одно из самых простых правил — БУДЬТЕ ПОСЛЕДОВАТЕЛЬНЫ. Если вы редактируете код, потратьте несколько минут на то, чтобы просмотреть окружающий его код и определить его стиль. Если в этом коде используются пробелы вокруг предложений if , вам тоже следует это сделать. Если комментарии к коду окружены маленькими квадратиками звездочек, сделайте так, чтобы ваши комментарии тоже были окружены маленькими квадратиками звездочек.

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

Правила языка Java

Android следует стандартным соглашениям по кодированию Java с дополнительными правилами, описанными ниже.

Не игнорируйте исключения

Может возникнуть соблазн написать код, игнорирующий исключение, например:

  void setServerPort(String value) {
      try {
          serverPort = Integer.parseInt(value);
      } catch (NumberFormatException e) { }
  }

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

«Каждый раз, когда у кого-то есть пустое предложение catch, у него должно возникнуть жуткое чувство. Определенно бывают случаи, когда это действительно правильно, но, по крайней мере, вы должны об этом подумать. В Java вы не можете избежать жуткого чувства. » — Джеймс Гослинг

Приемлемые альтернативы (в порядке предпочтения):

  • Выдайте исключение вызывающему объекту вашего метода.
      void setServerPort(String value) throws NumberFormatException {
          serverPort = Integer.parseInt(value);
      }
    
  • Создайте новое исключение, соответствующее вашему уровню абстракции.
      void setServerPort(String value) throws ConfigurationException {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new ConfigurationException("Port " + value + " is not valid.");
        }
      }
    
  • Обработайте ошибку корректно и подставьте подходящее значение в блок catch {} .
      /** Set port. If value is not a valid number, 80 is substituted. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            serverPort = 80;  // default port for server
        }
      }
    
  • Перехватите исключение и создайте новый экземпляр RuntimeException . Это опасно, поэтому делайте это только в том случае, если вы уверены, что в случае возникновения этой ошибки правильным решением будет сбой.
      /** Set port. If value is not a valid number, die. */
    
      void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            throw new RuntimeException("port " + value " is invalid, ", e);
        }
      }
    
  • В крайнем случае, если вы уверены, что игнорирование исключения уместно, вы можете его игнорировать, но вы также должны прокомментировать, почему, с указанием веской причины.
    /** If value is not a valid number, original port number is used. */
    
    void setServerPort(String value) {
        try {
            serverPort = Integer.parseInt(value);
        } catch (NumberFormatException e) {
            // Method is documented to just ignore invalid user input.
            // serverPort will just be unchanged.
        }
    }
    

Не перехватывать общие исключения

Может возникнуть соблазн полениться при перехвате исключений и сделать что-то вроде этого:

  try {
      someComplicatedIOFunction();        // may throw IOException
      someComplicatedParsingFunction();   // may throw ParsingException
      someComplicatedSecurityFunction();  // may throw SecurityException
      // phew, made it all the way
  } catch (Exception e) {                 // I'll just catch all exceptions
      handleError();                      // with one generic handler!
  }

Не делай этого. Почти во всех случаях нецелесообразно перехватывать общие Exception или Throwable (предпочтительно не Throwable , поскольку они включают исключения Error ). Это опасно, поскольку означает, что исключения, которые вы никогда не ожидали (включая исключения во время выполнения, такие как ClassCastException ), попадают в обработку ошибок на уровне приложения. Он скрывает свойства вашего кода по обработке ошибок, то есть, если кто-то добавит новый тип исключения в код, который вы вызываете, компилятор не укажет, что вам нужно обработать ошибку по-другому. В большинстве случаев не следует обрабатывать разные типы исключений одинаково.

Редким исключением из этого правила является тестовый код и код верхнего уровня, в которых вы хотите отловить все виды ошибок (чтобы предотвратить их появление в пользовательском интерфейсе или обеспечить выполнение пакетного задания). В этих случаях вы можете перехватить общее Exception (или Throwable ) и соответствующим образом обработать ошибку. Однако тщательно подумайте, прежде чем делать это, и оставьте комментарии, объясняющие, почему это безопасно в данном контексте.

Альтернативы перехвату общих исключений:

  • Перехватывайте каждое исключение отдельно как часть блока multi-catch, например:
    try {
        ...
    } catch (ClassNotFoundException | NoSuchMethodException e) {
        ...
    }
  • Выполните рефакторинг вашего кода, чтобы обеспечить более детальную обработку ошибок с помощью нескольких блоков try. Отделите ввод-вывод от синтаксического анализа и обрабатывайте ошибки отдельно в каждом случае.
  • Повторно создайте исключение. Во многих случаях вам все равно не нужно перехватывать исключение на этом уровне, просто позвольте методу его сгенерировать.

Помните, что исключения — ваши друзья! Если компилятор жалуется, что вы не перехватываете исключение, не хмурьтесь. Улыбка! Компилятор просто облегчил вам обнаружение проблем во время выполнения вашего кода.

Не используйте финализаторы

Финализаторы — это способ выполнения фрагмента кода при сборке мусора. Хотя финализаторы могут быть полезны для очистки (особенно внешних ресурсов), нет никаких гарантий относительно того, когда финализатор будет вызван (или даже что он будет вызван вообще).

Android не использует финализаторы. В большинстве случаев вместо этого вы можете использовать хорошую обработку исключений. Если вам абсолютно необходим финализатор, определите метод close() (или что-то подобное) и задокументируйте, когда именно этот метод необходимо вызвать (пример см. в разделе InputStream ). В этом случае уместно, но не обязательно, вывести короткое сообщение журнала из финализатора, если не предполагается, что оно приведет к переполнению журналов.

Полностью квалифицированный импорт

Если вы хотите использовать класс Bar из пакета foo , существует два возможных способа его импорта:

  • import foo.*;

    Потенциально уменьшает количество операторов импорта.

  • import foo.Bar;

    Делает очевидным, какие классы используются, и код становится более читабельным для сопровождающих.

Используйте import foo.Bar; для импорта всего кода Android. Явное исключение сделано для стандартных библиотек Java ( java.util.* , java.io.* и т. д.) и кода модульного теста ( junit.framework.* ).

Правила библиотеки Java

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

Правила стиля Java

Используйте стандартные комментарии Javadoc.

Вверху каждого файла должно быть указание об авторских правах, за которым следуют заявления о пакете и импорте (каждый блок разделен пустой строкой) и, наконец, объявление класса или интерфейса. В комментариях Javadoc опишите, что делает класс или интерфейс.

/*
 * Copyright 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.foo;

import android.os.Blah;
import android.view.Yada;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Does X and Y and provides an abstraction for Z.
 */

public class Foo {
    ...
}

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

Примеры

/** Returns the correctly rounded positive square root of a double value. */

static double sqrt(double a) {
    ...
}

или

/**
 * Constructs a new String by converting the specified array of
 * bytes using the platform's default character encoding.
 */
public String(byte[] bytes) {
    ...
}

Вам не нужно писать Javadoc для тривиальных методов получения и установки, таких как setFoo() если все, что ваш Javadoc говорит, это «устанавливает Foo». Если метод выполняет что-то более сложное (например, обеспечивает соблюдение ограничения или имеет важный побочный эффект), вы должны это задокументировать. Если неясно, что означает свойство «Foo», вам следует это задокументировать.

Каждый метод, который вы пишете, общедоступный или любой другой, выиграет от Javadoc. Открытые методы являются частью API и поэтому требуют Javadoc. Android не требует определенного стиля написания комментариев Javadoc, но вам следует следовать инструкциям в разделе «Как писать комментарии к документу для инструмента Javadoc» .

Напишите короткие методы

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

Определите поля в стандартных местах

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

Ограничить область действия переменной

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

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

Исключением являются операторы try-catch. Если переменная инициализируется возвращаемым значением метода, который генерирует проверенное исключение, ее необходимо инициализировать внутри блока try. Если значение необходимо использовать вне блока try, его необходимо объявить перед блоком try, где его еще нельзя разумно инициализировать:

// Instantiate class cl, which represents some sort of Set

Set s = null;
try {
    s = (Set) cl.newInstance();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable");
}

// Exercise the set
s.addAll(Arrays.asList(args));

Однако этого случая можно даже избежать, инкапсулировав блок try-catch в метод:

Set createSet(Class cl) {
    // Instantiate class cl, which represents some sort of Set
    try {
        return (Set) cl.newInstance();
    } catch(IllegalAccessException e) {
        throw new IllegalArgumentException(cl + " not accessible");
    } catch(InstantiationException e) {
        throw new IllegalArgumentException(cl + " not instantiable");
    }
}

...

// Exercise the set
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

Объявляйте переменные цикла в самом операторе for, если нет веской причины поступить иначе:

for (int i = 0; i < n; i++) {
    doSomething(i);
}

и

for (Iterator i = c.iterator(); i.hasNext(); ) {
    doSomethingElse(i.next());
}

Заказать выписки по импорту

Порядок операторов импорта следующий:

  1. Импорт Android
  2. Импорт от третьих лиц ( com , junit , net , org )
  3. java и javax

Чтобы точно соответствовать настройкам IDE, импорт должен быть:

  • В алфавитном порядке внутри каждой группы, с заглавными буквами перед строчными буквами (например, Z перед а).
  • Разделяются пустой строкой между каждой основной группой ( android , com , junit , net , org , java , javax ).

Первоначально не было никаких требований к стилю порядка, то есть IDE либо всегда меняли порядок, либо разработчикам IDE приходилось отключать функции автоматического управления импортом и поддерживать импорт вручную. Это считалось плохим. Когда был задан вопрос о стиле Java, предпочтительные стили сильно различались, и все сводилось к тому, что Android нужно было просто «выбрать порядок и быть последовательным». Поэтому мы выбрали стиль, обновили руководство по стилю и заставили IDE подчиняться ему. Мы ожидаем, что по мере работы над кодом пользователей IDE импорт во всех пакетах будет соответствовать этому шаблону без дополнительных инженерных усилий.

Мы выбрали этот стиль так, чтобы:

  • Импорт, на который люди хотят обратить внимание в первую очередь, обычно находится вверху ( android ).
  • Импорт, который люди хотят хотя бы просмотреть, обычно находится внизу ( java ).
  • Люди могут легко следовать этому стилю.
  • IDE могут следовать этому стилю.

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

Используйте пробелы для отступов

Мы используем четыре (4) пробела для блоков и никогда не используем табуляцию. В случае сомнений соблюдайте соответствие окружающему коду.

Мы используем восемь (8) отступов пробелов для переноса строк, включая вызовы функций и присваивания.

рекомендуемые

Instrument i =
        someLongExpression(that, wouldNotFit, on, one, line);

Не рекомендуется

Instrument i =
    someLongExpression(that, wouldNotFit, on, one, line);

Следуйте соглашениям об именах полей.

  • Имена закрытых, нестатических полей начинаются с m .
  • Имена статических полей начинаются с s .
  • Остальные поля начинаются со строчной буквы.
  • Статические конечные поля (константы, глубоко неизменяемые) — ALL_CAPS_WITH_UNDERSCORES .

Например:

public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}

Использовать стандартный стиль скобок

Поместите фигурные скобки на той же строке, что и код перед ними, а не на отдельной строке:

class MyClass {
    int func() {
        if (something) {
            // ...
        } else if (somethingElse) {
            // ...
        } else {
            // ...
        }
    }
}

Нам нужны фигурные скобки вокруг операторов условного выражения. Исключение: если весь условный оператор (условие и тело) умещается в одной строке, вы можете (но не обязаны) поместить его все в одну строку. Например, это приемлемо:

if (condition) {
    body();
}

и это приемлемо:

if (condition) body();

но это неприемлемо:

if (condition)
    body();  // bad!

Ограничить длину строки

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

  • Если строка комментария содержит пример команды или буквенный URL-адрес длиной более 100 символов, эта строка может быть длиннее 100 символов для удобства вырезания и вставки.
  • Строки импорта могут превышать лимит, поскольку люди редко их видят (это также упрощает написание инструментов).

Используйте стандартные аннотации Java

Аннотации должны предшествовать другим модификаторам того же языкового элемента. Простые аннотации маркеров (например, @Override ) можно перечислять в одной строке с языковым элементом. Если имеется несколько аннотаций или параметризованных аннотаций, перечисляйте их по одной в строке в алфавитном порядке.

Стандартные методы Android для трех предопределенных аннотаций в Java:

  • Используйте аннотацию @Deprecated всякий раз, когда использование аннотированного элемента не рекомендуется. Если вы используете аннотацию @Deprecated , у вас также должен быть тег @deprecated Javadoc, и он должен называть альтернативную реализацию. Кроме того, помните, что метод @Deprecated по-прежнему должен работать . Если вы видите старый код с тегом @deprecated Javadoc, добавьте аннотацию @Deprecated .
  • Используйте аннотацию @Override всякий раз, когда метод переопределяет объявление или реализацию суперкласса. Например, если вы используете тег @inheritdocs Javadoc и наследуете его от класса (а не от интерфейса), вы также должны указать, что метод переопределяет метод родительского класса.
  • Используйте аннотацию @SuppressWarnings только в тех случаях, когда невозможно устранить предупреждение. Если предупреждение проходит тест «невозможно устранить», необходимо использовать аннотацию @SuppressWarnings , чтобы гарантировать, что все предупреждения отражают реальные проблемы в коде.

    Если аннотация @SuppressWarnings необходима, перед ней должен быть комментарий TODO , объясняющий условие «невозможно устранить». Обычно это идентифицирует класс-нарушитель с неудобным интерфейсом. Например:

    // TODO: The third-party class com.third.useful.Utility.rotate() needs generics
    @SuppressWarnings("generic-cast")
    List<String> blix = Utility.rotate(blax);
    

    Если требуется аннотация @SuppressWarnings , выполните рефакторинг кода, чтобы изолировать элементы программного обеспечения, к которым применяется аннотация.

Относитесь к аббревиатурам как к словам

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

Хороший Плохой
XmlHttpRequest XMLHTTPRequest
получитьCustomerId getCustomerID
класс HTML класс HTML
Строковый URL Строковый URL-адрес
длинный идентификатор длинный идентификатор

Поскольку кодовые базы JDK и Android несовместимы в отношении акронимов, практически невозможно обеспечить согласованность с окружающим кодом. Поэтому всегда относитесь к аббревиатурам как к словам.

Используйте комментарии TODO

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

// TODO: Remove this code after the UrlTable2 has been checked in.

и

// TODO: Change this to use a flag instead of a constant.

Если ваш TODO имеет вид «В будущем сделать что-нибудь», убедитесь, что вы включили либо конкретную дату («Исправить к ноябрю 2005 г.»), либо конкретное событие («Удалите этот код после того, как все производственные микшеры поймут протокол V7». ).

Ведите журнал экономно

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

  • ERROR : используйте, когда произошло что-то фатальное, то есть что-то будет иметь видимые пользователем последствия и не может быть восстановлено без удаления некоторых данных, удаления приложений, очистки разделов данных или перепрошивки всего устройства (или того хуже). Этот уровень всегда протоколируется. Проблемы, которые оправдывают некоторую регистрацию на уровне ERROR , являются хорошими кандидатами для сообщения на сервер сбора статистики.
  • WARNING : используйте, когда произошло что-то серьезное и неожиданное, то есть что-то, что будет иметь последствия, видимые пользователем, но, вероятно, будет восстановлено без потери данных путем выполнения какого-либо явного действия, начиная от ожидания или перезапуска приложения и заканчивая повторной загрузкой. новую версию приложения или перезагрузку устройства. Этот уровень всегда протоколируется. Проблемы, которые оправдывают ведение журнала на уровне WARNING , также могут рассматриваться для отправки отчетов на сервер сбора статистики.
  • INFORMATIVE : используется, чтобы отметить, что произошло что-то интересное, то есть когда обнаружена ситуация, которая может иметь широкомасштабные последствия, хотя и не обязательно является ошибкой. Такое условие должно регистрироваться только тем модулем, который считает, что он является наиболее авторитетным в этом домене (во избежание дублирования регистрации недоверенными компонентами). Этот уровень всегда протоколируется.
  • DEBUG : используйте для дальнейшего отслеживания того, что происходит на устройстве, что может быть полезно для исследования и устранения непредвиденного поведения. Регистрируйте только то, что необходимо для сбора достаточной информации о том, что происходит с вашим компонентом. Если ваши журналы отладки доминируют в журнале, вам следует использовать подробное журналирование.

    Этот уровень регистрируется даже в релизных сборках и должен быть окружен блоком if (LOCAL_LOG) или if LOCAL_LOGD) , где LOCAL_LOG[D] определен в вашем классе или подкомпоненте, чтобы была возможность отключить все такие журналы. . Следовательно, в блоке if (LOCAL_LOG) не должно быть активной логики. Все построение строк для журнала также необходимо разместить внутри блока if (LOCAL_LOG) . Не реорганизуйте вызов журнала в вызов метода, если это приведет к тому, что построение строки будет происходить за пределами блока if (LOCAL_LOG) .

    Есть код, который все еще говорит if (localLOGV) . Это тоже считается приемлемым, хотя имя нестандартное.

  • VERBOSE : Используйте для всего остального. Этот уровень регистрируется только в отладочных сборках и должен быть окружен блоком if (LOCAL_LOGV) (или его эквивалентом), чтобы его можно было скомпилировать по умолчанию. Любое построение строк удаляется из сборок выпуска и должно появляться внутри блока if (LOCAL_LOGV) .

Примечания

  • В пределах данного модуля, за исключением уровня VERBOSE , об ошибке следует сообщать только один раз, если это возможно. В рамках одной цепочки вызовов функций внутри модуля только самая внутренняя функция должна возвращать ошибку, а вызывающие программы в том же модуле должны добавлять некоторую запись в журнал только в том случае, если это существенно помогает изолировать проблему.
  • В цепочке модулей, кроме уровня VERBOSE , когда модуль нижнего уровня обнаруживает неверные данные, поступающие от модуля более высокого уровня, модуль нижнего уровня должен регистрировать эту ситуацию только в журнале DEBUG , и только если регистрация обеспечивает информация, которая иначе недоступна вызывающему абоненту. В частности, нет необходимости регистрировать ситуации, когда генерируется исключение (исключение должно содержать всю соответствующую информацию) или когда единственная записываемая информация содержится в коде ошибки. Это особенно важно при взаимодействии между платформой и приложениями, а условия, вызванные сторонними приложениями, которые должным образом обрабатываются платформой, не должны вызывать ведение журнала выше уровня DEBUG . Единственные ситуации, которые должны запускать ведение журнала на INFORMATIVE уровне или выше, — это когда модуль или приложение обнаруживает ошибку на своем собственном уровне или исходящую с более низкого уровня.
  • Когда условие, которое обычно оправдывает ведение журнала, вероятно, произойдет много раз, может быть хорошей идеей реализовать некоторый механизм ограничения скорости, чтобы предотвратить переполнение журналов множеством дубликатов одной и той же (или очень похожей) информации.
  • Потери сетевого подключения считаются обычным явлением, вполне ожидаемы и не должны регистрироваться безосновательно. Потеря сетевого подключения, имеющая последствия внутри приложения, должна регистрироваться на уровне DEBUG или VERBOSE (в зависимости от того, являются ли последствия достаточно серьезными и неожиданными, чтобы их можно было зарегистрировать в сборке выпуска).
  • Наличие полной файловой системы в файловой системе, доступной для сторонних приложений или от их имени, не должно регистрироваться на уровне выше ИНФОРМАЦИОННОГО.
  • Недействительные данные, поступающие из любого ненадежного источника (включая любой файл в общем хранилище или данные, поступающие через сетевое соединение), считаются ожидаемыми и не должны вызывать ведение журнала на уровне выше, чем DEBUG , когда они обнаруживаются как недействительные (и даже тогда регистрация должно быть максимально ограничено).
  • При использовании с объектами String оператор + неявно создает экземпляр StringBuilder с размером буфера по умолчанию (16 символов) и, возможно, с другими временными объектами String . Таким образом, явное создание объектов StringBuilder не дороже, чем использование оператора + по умолчанию (и может быть намного эффективнее). Имейте в виду, что код, вызывающий Log.v() , компилируется и выполняется в сборках выпуска, включая построение строк, даже если журналы не читаются.
  • Любое журналирование, предназначенное для чтения другими людьми и доступное в сборках выпуска, должно быть кратким, но не загадочным и должно быть понятным. Это включает в себя всю регистрацию до уровня DEBUG .
  • По возможности продолжайте вести журнал в одну строку. Допустимая длина строки до 80 или 100 символов. По возможности избегайте длины, превышающей 130 или 160 символов (включая длину тега).
  • Если журналирование сообщает об успехе, никогда не используйте его на уровнях выше VERBOSE .
  • Если вы используете временное ведение журнала для диагностики проблемы, которую трудно воспроизвести, оставьте его на уровне DEBUG или VERBOSE и заключите его в блоки if, которые позволяют отключить его во время компиляции.
  • Будьте осторожны с утечками безопасности через журнал. Избегайте регистрации частной информации. В частности, избегайте регистрации информации о защищенном контенте. Это особенно важно при написании кода платформы, поскольку непросто заранее узнать, что будет, а что не будет частной информацией или защищенным контентом.
  • Никогда не используйте System.out.println() (или printf() для машинного кода). System.out и System.err перенаправляются на /dev/null , поэтому ваши операторы печати не имеют видимого эффекта. Однако все построение строк, происходящее для этих вызовов, по-прежнему выполняется.
  • Золотое правило ведения журналов заключается в том, что ваши журналы не могут без необходимости выталкивать другие журналы из буфера, так же как другие не могут выталкивать ваши.

Правила стиля Javatests

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

testMethod_specificCase1 testMethod_specificCase2

void testIsDistinguishable_protanopia() {
    ColorMatcher colorMatcher = new ColorMatcher(PROTANOPIA)
    assertFalse(colorMatcher.isDistinguishable(Color.RED, Color.BLACK))
    assertTrue(colorMatcher.isDistinguishable(Color.X, Color.Y))
}