Статьи

Типичные ошибки в коде Java

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

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

Все перечисленные ошибки относятся к объектно-ориентированному программированию в целом и к Java в частности.

Имена классов

Прочитайте этот короткий «Что такое объект?» статья. Ваш класс должен быть абстракцией реальной сущности без «валидаторов», «контроллеров», «менеджеров» и т. Д. Если имя вашего класса заканчивается на «-er» — это плохой дизайн .

И, конечно, служебные классы являются анти-шаблонами, такими как StringUtils , FileUtils и IOUtils от Apache. Выше приведены прекрасные примеры ужасных замыслов. Прочитайте этот пост: OOP Альтернатива служебным классам .

Конечно, никогда не добавляйте суффиксы или префиксы, чтобы различать интерфейсы и классы . Например, все эти имена ужасно неправильны: IRecord , IfaceEmployee или RecordInterface . Обычно имя интерфейса — это имя реальной сущности, а имя класса должно объяснять детали его реализации. Если ничего не скажешь о реализации, назовите ее Default, Simple или что-то подобное. Например:

1
2
3
4
class SimpleUser implements User {};
class DefaultRecord implements Record {};
class Suffixed implements Name {};
class Validated implements Content {};

Имена методов

Методы могут либо возвращать что-то, либо возвращать void . Если метод возвращает что-то, его имя должно объяснять, например, что он возвращает (никогда не используйте префикс get ):

1
2
3
boolean isValid(String name);
String content();
int ageOf(File file);

Если он возвращает void, то его имя должно объяснять, что он делает . Например:

1
2
3
void save(File file);
void process(Work work);
void append(File file, String line);

Существует только одно исключение из только что упомянутого правила — методы тестирования для JUnit. Они объяснены ниже.

Имена методов испытаний

Имена методов в тестах JUnit должны создаваться как английские предложения без пробелов. Это проще объяснить на примере:

1
2
3
4
5
6
/**
 * HttpRequest can return its content in Unicode.
 * @throws Exception If test fails
 */
public void returnsItsContentInUnicode() throws Exception {
}

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

Имя метода будет точно таким же, но без субъекта. Если я добавлю тему в начале имени метода, я получу полное английское предложение, как в примере выше: «HttpRequest возвращает свое содержимое в юникоде».

Обратите внимание, что тестовый метод не начинается с can Только комментарии JavaDoc начинаются с can. Кроме того, имена методов не должны начинаться с глагола.

Рекомендуется всегда объявлять методы тестирования как Exception .

Имена переменных

Избегайте составных имен переменных, таких как timeOfDay , firstItem или httpRequest . Я имею в виду как переменные класса, так и переменные внутри метода. Имя переменной должно быть достаточно длинным, чтобы избежать двусмысленности в его видимости, но не слишком длинным, если это возможно. Имя должно быть существительным в единственном или множественном числе или соответствующей аббревиатурой. Например:

1
2
3
4
List<String> names;
void sendThroughProxy(File file, Protocol proto);
private File content;
public HttpRequest request;

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

Другой пример:

1
2
3
4
5
6
public class Message {
  private String recipient;
  public Message(String rcpt) {
    this.recipient = rcpt;
  }
}

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

1
2
3
File file;
User user;
Branch branch;

Однако никогда не делайте то же самое для примитивных типов, таких как Integer number или String string .

Вы также можете использовать прилагательное, когда есть несколько переменных с разными характеристиками. Например:

1
String contact(String left, String right);

Конструкторы

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

1
2
3
4
5
6
7
8
9
public class Server {
  private String address;
  public Server(String uri) {
    this.address = uri;
  }
  public Server(URI uri) {
    this(uri.toString());
  }
}

Одноразовые переменные

Избегайте одноразовых переменных любой ценой. Под «одноразовыми» я подразумеваю переменные, которые используются только один раз. Как в этом примере:

1
2
String name = "data.txt";
return new File(name);

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

1
return new File("data.txt");

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

Исключения

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

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

1
2
3
4
5
6
int size;
try {
  size = this.fileSize();
} catch (IOException ex) {
  size = 0;
}

Серьезно, что, если это IOException говорит «диск заполнен»? Будете ли вы по-прежнему считать, что размер файла равен нулю, и двигаться дальше?

вдавливание

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

1
2
final File file = new File(directory,
  "file.txt");

Правильный отступ должен выглядеть так:

01
02
03
04
05
06
07
08
09
10
StringUtils.join(
  Arrays.asList(
    "first line",
    "second line",
    StringUtils.join(
      Arrays.asList("a", "b")
    )
  ),
  "separator"
);

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

1
2
3
4
5
6
7
StringUtils.join(
  Arrays.asList(
    "first line", "second line",
    StringUtils.join(Arrays.asList("a", "b"))
  ),
  "separator"
);

Избыточные Константы

Константы класса следует использовать, когда вы хотите обмениваться информацией между методами класса, и эта информация является характеристикой (!) Вашего класса. Не используйте константы в качестве замены строковых или числовых литералов — очень плохая практика, которая приводит к загрязнению кода. Константы (как и любой объект в ООП) должны иметь значение в реальном мире. Какое значение имеют эти константы в реальном мире:

1
2
3
4
class Document {
  private static final String D_LETTER = "D"; // bad practice
  private static final String EXTENSION = ".doc"; // good practice
}

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

Используйте новые тексты и цифры в каждом новом методе испытаний. Они независимы. Итак, почему они должны использовать одни и те же входные константы?

Тестирование связи данных

Это пример связи данных в тестовом методе:

1
2
3
User user = new User("Jeff");
// maybe some other code here
MatcherAssert.assertThat(user.name(), Matchers.equalTo("Jeff"));

В последней строке мы связываем "Jeff" с тем же строковым литералом из первой строки. Если спустя несколько месяцев кто-то захочет изменить значение в третьей строке, ему / ей придется потратить дополнительное время на поиск, где еще "Jeff" используется в том же методе.

Чтобы избежать этой связи данных, вы должны ввести переменную.

Похожие сообщения

Вы также можете найти эти сообщения интересными:

Ссылка: Типичные ошибки в Java-коде от нашего партнера по JCG Егора Бугаенко в блоге About Programming .