Правила форматирования кода в Java (ч. 5.1)

Введение

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

1. Для чего нужно форматирование

Открою вам секрет, что среднестатистический программист проводит гораздо больше времени за чтением кода, чем за его написанием. Читать код сложнее, чем писать, особенно чужой. Но наличие общего стиля оформления программ и следование ему, облегчает понимание исходного кода, написанного разными программистами
Стандарт оформления кода в каждой компании свой, но большая часть правил обычно одинакова, отличия не такие сильные. Когда в рамках команды все пишут в одном стиле, а не кто как хочет, то это значительно повышает читабельность кода, что в свою способствует снижению когнитивной нагрузки (мозги программиста меньше напрягаются).
Давайте рассмотрим некоторые из правил оформления кода, которые были использованы при реализации класса MyFirstApp:
1
между ключевыми (зарезервированными языком Java) словами ставится один пробел, например, static void. Вам никогда не придется ставить два или более пробелов. И написать слитно, например, publicstatic по понятным причинам мы не можем, получится абракадабра, которая не заработает
2
между именем класса и фигурной скобкой нужен пробел: MyFirstApp {.
Это правило относится ко всем открывающимся фигурным скобкам, где бы они не стояли. А вот такой вариант уже выглядит небрежно: MyFirstApp{
3
метод main() размещается внутри класса MyFirstApp, что говорит о его вложенности, которую необходимо визуально отображать. Ее необходимо визуально как-то отображать. Для этого требуется сдвинуть метод целиком вправо на 4 пробела или один Tab
4
код внутри метода, в свою очередь, вложен и в класс, и в метод main(). Значит, его нужно сдвинуть вправо на 4 пробела дважды: относительно начала класса и начала метода. Итого получается 8 пробелов от левого края редактора или два Tab
5
между именем метода и круглой скобкой пробел не ставится: main(). А такой вариант будет уже неверным: main ()
6
фигурная скобка } должна размещаться строго под началом конструкции, к которой она принадлежит: не правее и не левее
«Действительно, соотношение времени, затрачиваемого на чтение и написание кода, значительно превышает 10 к 1. Мы постоянно читаем старый код, пытаясь написать новый код. …[Поэтому] чем легче читать, тем легче писать».
Роберт Мартин ("Чистый код")
Чтобы на первых порах вам было проще видеть, какой размер отступа используется в коде, ранее мы настроили Sublime Text на отображение пробелов в виде точек.
Чтобы было еще понятнее, начертим стрелки, показывающие направление сдвига кода относительно начала блока (вертикальных линий, которые отображает редактор).
Также необходимо понимать, что несоблюдение правил форматирования никак не влияет на запуск программы, т. к. они не важны для виртуальной машины. Они имеют значения для людей, читающих код.
Давайте напишем тот же самый код, но уже с ошибками форматирования, и попробуем его запустить.
Программа запустилась без каких-либо ошибок.
> java MyFirstApp.java
Написано однажды, работает везде
Можно записать весь код хоть в одну строку, но его никто не поймет. Многие начинающие разработчики не обращают внимание на внешний вид своего кода. Их цель — написать программу. Но есть хорошее выражение:
Любой дурак может написать код, понятный компьютеру. Хороший программист пишет код, понятный человеку
Мартин Фаулер

1.1 Статический анализатор кода

Запомнить сразу все правила, которые используются для форматирования кода — не так просто. Для облегчения этого процесса используются статические анализаторы, проверяющие код на несоответствие установленным стандартам. Настройка (обязательная!) одной из таких утилит описана в другой статье.

2. Правила форматирования Java-кода

В этом разделе собрано большое количество правил оформления кода, которые вы можете применять при написании своих программ на Java. Ранее мы уже публиковали (1, 2, 3) похожие правила от компании Google, но их отличие от представленных в этой статье в том, что они не такие детальные и подробные. Правила из этой статьи больше рассчитаны для начинающих программистов: они описывают каждую мелочь, каждую типичную ситуацию.
Применение данных техник сделает ваш код чистым, более читаемым и понятным, а также повысит его поддерживаемость.
Поддерживаемость кода (code maintainability) — одна из метрик (субъективная) оценки качества кода, измеряющая скорость (трудозатраты) внесения в него изменений (добавление новых фич, исправление багов). Если ваш код логичный и легко читаем, то его поддерживаемость возрастает. В плохо написанном коде (где не соблюдаются практики, нацеленные на повышение качества) тяжелее разобраться, а значит сложнее его модифицировать, что напрямую влияет на снижение maintainability.
Не пренебрегайте предлагаемыми техниками (изучайте их), способствующими повысить качество, а значит и поддерживаемость вашего кода!

2.1. Общие правила

1
Перед и после операторов =, +, -, /, *, %, ==, !=, <, <=, >, >= требуется пробел
неправильно:
System.out.println("дата="+m+"."+d+"."+y);
int length=5;
boolean isEqual=num1/10==num2/10;
правильно:
System.out.println("дата = " + m + "." + d + "." + y);
int length = 5;
boolean isEqual = num1 / 10 == num2 / 10;
2
Между ){ скобками требуется пробел
неправильно:
- public Storage(){
- public static void main(String[] args){
- for (int i = 10; i > 0; i--){
- if (a > 10){
правильно:
- public Storage() {
- public static void main(String[] args) {
- for (int i = 10; i > 0; i--) {
- if (a > 10) {
3
После ( и перед ) скобками пробел не требуется
неправильно:
for ( int i = 0; i < 21; i++ )
compareNumbers( int number )
System.out.println( "Hello" )
if ( answer.equals("yes") )
правильно:
for (int i = 0; i < 21; i++)
compareNumbers(int number)
System.out.println("Hello")
if (answer.equals("yes"))
4
Код, вложенный в класс, метод, цикл и другие блочные конструкции, которые имеют явные или опущенные { }, необходимо сдвигать вправо на 4 пробела (один Tab) относительно начала этой конструкции
неправильно:
public class Person {

    int age = 25;

    public static void main(String[] args) {
    if (age > 20) {
    System.out.println("Человек старше 20 лет");
    }
    }
}
правильно:
public class Person {

    int age = 25;

    public static void main(String[] args) {
        if (age > 20) {
            System.out.println("Человек старше 20 лет");
        }
    }
}
5
При переносе строки каждую следующую ее подстроку необходимо смещать вправо на 8 пробелов относительно первой строки. При этом перенос следует выполнять (где это возможно) исходя из целостности и связанности переносимой информации, а не по остаточному признаку (что не влезает, то и переносим) — старайтесь, чтобы на каждой строке было примерно равное количество аргументов
неправильно:
// 1
System.out.println("Characteristics:\n" +
"age = " + age + "\n" + "name = " +
name + "\n" + "color = " +
color);

// 2
System.out.println("Characteristics:\n" +
                   "age = " + age + "\n" +
                   "name = " + name + "\n" +
                   "color = " + color);

// 3
System.out.println("Средний балл оценок по предметам: " + (historyGrade + 
        csGrade) / 2f);

// 4
boolean isEqual = a > 0 && a < 10 ||
    b > 100 && b < 110
правильно:
// 1, 2
System.out.println("Characteristics:\n" +
        "age = " + age + "\n" +
        "name = " + name + "\n" +
        "color = " + color);

// 3
System.out.println("Средний балл оценок по предметам: " +
        (historyGrade + csGrade) / 2f);

// 4
boolean isEqual = a > 0 && a < 10 ||
        b > 100 && b < 110
6
Для переноса курсора на новую строку, вместо System.out.println(); используйте \n (где это возможно) — это уменьшит количество строк кода
избыточно:
System.out.println();
System.out.println("2. Поиск максимального числа");
предпочтительней:
System.out.println("\n2. Поиск максимального числа");
Если требуется больше переносов, то используйте нужное количество \n
System.out.println("\n\n1. Расчет стоимости\n\n");
7
В некоторых случаях использование \n неуместно. Вместо этого следует применить System.out.println()
Не используйте
System.out.print("\n");
или
System.out.printf("\n");
или
System.out.printf("%n"); 

вместо System.out.println();
Вместо
System.out.print("число\n");
или
System.out.print(number + "\n");

пишите System.out.println(number);
8
Если управляющий символ \n размещается в конце выводимого сообщения, а перед ним идет строка, то совмещайте его с данной строкой, а не конкатенируйте
Вместо:
System.out.println("Мама мыла раму" + '\n');

Пишите:
System.out.println("Мама мыла раму\n");
9
Размещайте комментарии над комментируемым кодом, а не справа от него. Комментарий должен начинаться с той же позиции, что и код. При этом перед комментарием необходима пустая строка (кроме комментария, который идет сразу после объявления метода). Между // и текстом комментария используйте только один пробел
неправильно:
int sumOdd = 10; //            сумма нечетных чисел

    // запуск игрового процесса
game.play();

//     получить все резюме 
    getAllResume();

public static void main(String[] args) {

    // комментарий
правильно:
// сумма нечетных чисел
int sumOdd = 10;

// запуск игрового процесса
game.play();

// получить все резюме 
getAllResume();

public static void main(String[] args) {
    // комментарий
10
Отдавайте предпочтение использованию сокращенной формы записи для арифметических операций
избыточно:
num1 = num1 - num2;
num1 = num1 ^ num2
правильно:
num1 -= num2;
num1 ^= num2

2.2. Правила для переменных

1
Давайте переменным осмысленные и понятные имена, глядя на которые, любому читающему код было бы ясно, какие значения они хранят. Они должны быть не слишком длинными, но ёмкими. Обычно имена из одной буквы — запрещены
неправильно:
char a = '+';
int b = 10;
String с = "yes";
правильно:
char sign = '+';
int size = 10;
String answer = "yes";
2
Объявляйте переменные максимально близко к месту их первого использования, чтобы сузить область их видимости. Не группируйте их в начале метода
3
В одной строке объявляйте не более одной переменной
неправильно:
int a, b, swap;
String name, letter;
правильно:
int a;
int b;
int swap;
String name
String letter;
4
Имя переменной не должно быть глаголом или содержать глагол
неправильно:
Resume createResume = storage.getResume(id);
int multiplyNumbers = 7 * 7;
правильно:
Resume resume = storage.getResume(id);
или
Resume newResume = storage.getResume(id);

int productNumbers = 7 * 7;
или
int result = 7 * 7;
5
Именуйте переменные, используя только английский язык
неправильно:
proizvedenie
procent
rezultat
правильно:
product (multiplication)
percent
result
6
Имена переменных начинайте с маленькой буквы
неправильно:
int Age;
int Title;
int FullName;
правильно:
int age;
int title;
int fullName;
7
Перед переменной при приведении типа требуется пробел
неправильно:
int code = (char)i;
double avgPercent = ((double)historyPercent + csPercent) / 2;
return (int)uuid;
правильно:
int code = (char) i;
double avgPercent = ((double) historyPercent + csPercent) / 2;
return (int) uuid;
8
При наличии аббревиатур в именах переменных, прописные буквы — не используются. Применяется обычный camelCase, если аббревиатура является частью многословного имени
неправильно:
int RAM;
long freqCPU;
String OS;
правильно:
int ram;
long freqCpu;
String os;
9
При использовании сокращенной формы инкремента и декремента, после имени переменной пробел не ставится
неправильно:
number ++
attempt --
правильно:
number++
attempt--
10
Если имя переменной состоит из более, чем одного слова, то используется camelCase. Это стиль именования переменных, когда все слова пишутся слитно, при этом каждое из них начинается с прописной буквы
неправильно:
int playeranswer
int counttwos;
String malegender
boolean is_deleted;
char first_letter;
правильно:
int playerAnswer
int countTwos;
String maleGender
boolean isDeleted;
char firstLetter;
11
В именах переменных (не констант), состоящих из двух и более слов, нижнее подчеркивание _ не используется. Вместо него применяется стиль camelCase
неправильно:
int hidden_number;
String player_answer;
boolean is_Equal_Hundreds;
правильно:
int hiddenNumber;
String playerAnswer;
boolean isEqualHundreds
12
Инициализируйте переменные (если это возможно) в строке их объявления, а не где-то ниже в коде
неправильно:
double num1;
char sign;
num1 = 4;
sign = '*';
правильно:
double num1 = 4;
char sign = '*';
13
Не придумывайте свои сокращения для переменных. Нельзя просто взять и сократить имя так, как хочется. Высока вероятность, что программисту, читающему ваш код, их назначение будет непонятно. Кроме того, в больших программах возникнет сложность с поиском нужных переменных, т. к. никто не будет помнить, как именно были сокращены их имена. Можно использовать (и то аккуратно) только распространенные сокращения: tmp, num, nums, src, dest, hh, mm, ss, len и т. д.
неправильно:
double disc = 0.11;
int histPers = 100;
double pr;
правильно:
double discount = 0.11;
int historyPersent = 100;
double price = 17;
14
В рамках одной программы не используйте одновременно синонимы, сокращения и полные слова для именования переменных, которые хранят одни и те же по смыслу значения. Нельзя в одной строке написать имя целиком, где-то ниже полностью, а в другом классе использовать синоним. Все имена нужно именовать в одном стиле, используя одни и те же слова
неправильно:
int reverseNumber;
int sumRevNum
int numb;
String invertString;
правильно:
int reverseNum;
int sumReverseNum;
int num;
String reverseString;

или

int reverseNumber;
int sumReverseNumber;
int number;
15
Не используйте при именовании переменных абстрактные слова (без реальной необходимости), которые ничего не означают, например, value, var и т. д. Если переменная хранит число, то именуйте ее number, num или даже a, b, c — если это какие-то математические вычисления однобуквенные имена допустимы только для локальных переменных. Но и в этом случае нужно сто раз подумать). Но даже в этом случае следует все взвесить, т. к. числа имеют разное назначение и свой смысл. При именовании переменных задавайте себе вопрос: выбранные имена максимально точно отражают хранимые данные?
неправильно:
int var = 123;
char value = 'A';
int number = 3,1415;

// никогда не используйте при именовании слово variable
String variable = "Max";
правильно:
int originNumber = 123;
char symbol = 'A';
int pi = 3,1415;
String name = "Max";
16
Имена boolean-переменных должны быть прилагательными. Либо они должны начинаться с префикса is/has/can (что больше подходит по смыслу). Префикс are — не используется. При использовании второго способа именования, после префикса должно идти прилагательное. Если прилагательного нет, то после префикса может идти существительное
неправильно:
boolean security
boolean answer
boolean continue
boolean error;

boolean isEmailValid
boolean hasDigitsEqual
boolean isNumberUnique
правильно:
boolean active
boolean current
boolean alive

boolean isValidEmail
boolean hasEqualDigits
boolean isUniqueNumber
boolean hasError;
boolean isDigit

2.3. Правила для if-else

1
Между if и ( необходим пробел
допустимо:
if(a > b) {
    // some code
}
if(playerNum != secretNum) {
    // some code
}
предпочтительно:
if (a > b) {
    // some code
}
if (playerNum != secretNum) {
    // some code
}
2
Даже если тело if-else состоит из одного выражения, всегда используйте { }. Это поможет избежать трудноуловимых багов. В качестве исключения допустимо использовать однострочники без фигурных скобок, когда имеется очень короткое выражение
неправильно:
if (doc.important)
    send(doc);
else
    System.out.println("Throw out the trash");
    delete(doc);

if (player.hasAttempt()) {
    // some code
} else System.out.println("Попытки закончились");
правильно:
if (doc.important) {
    send(doc);
} else {
    System.out.println("Throw out the trash");
    delete(doc);
}

if (player.hasAttempt()) {
    // some code
} else {
    System.out.println("Попытки закончились");
}
допустимо:
if (a > b) continue;

if (isExist(resume)) {
    System.out.println("Такое резюме уже есть в базе");
    return null;
} else return resume;
3
Слово else и else-if оставляйте на той же строке, что и }, отделив их друг от друга пробелом
неправильно:
}
else {
    System.out.println("Exit");
}

}
else if (size < storage.length) {
правильно:
} else {
    System.out.println("Exit");
}

} else if (size < storage.length) {
4
Не отделяйте if от else новой строкой (где это возможно)
плохо (зачем тратить лишнюю строку под if):
} else {
    if (number > randomNumber) {
правильно:
} else if (number > randomNumber) {
5
Не оставляйте новый блок с if на одной строке с предыдущим if. Переносите его на следующую строку
неправильно:
if (num1 == num2) {
    // some code
} if (num3 == num4) {
    // some code
}
правильно:
if (num1 == num2) {
    // some code
}
if (num3 == num4) {
    // some code
}
6
При проверке boolean-значений не указывайте явно true или false
избыточно:
if (male == false) {

if (isExist(uuid) != true) {
правильно:
if (!male) {

if (!isExist(uuid)) {

2.4. Правила для циклов

1
Между for и (, между while и ( требуется пробел
допустимо:
for(int i = 0; i < 10; i++) {

for(int i = 10; i > 0; i--) {
    for(int j = 5; j > 0; j--) {
        // some code
    }
}

do {
    // some code
} while(true);

while(true) {
предпочтительно:
for (int i = 0; i < 10; i++) {

for (int i = 10; i > 0; i--) {
    for (int j = 5; j > 0; j--) {
        // some code
    }
}

do {
    // some code
} while (true);

while (true) {
2
do-while оставляйте на той же строке, что и }, отделив их друг от друга пробелом
неправильно:
do {
    // some code
}
while ("yes".equals(answer));
правильно:
do {
    // some code
} while ("yes".equals(answer));
3
Стандартное именование счетчиков в цикле for — это i, j, k. Последние два используются для вложенных циклов
неправильно:
for (int a = 0; a < 3; a++) {
    // some code
}

for (int d = 0; d < 10; d++) {
    for (int c = 0; c < 4; c++) {    
        // some code
    }
}
правильно:
for (int i = 0; i < 3; i++) {
    // some code
}

for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 4; j++) {    
        // some code
    }
}
4
Имена переменных i, j, k допустимо использовать в качестве счетчика только для for. Для других циклов придумывайте более осмысленные имена
неправильно:
j = 5;
while (j > 1) {
    // some code
}

i = 2;
do {
    // some code
} while (i > 0);
правильно:
row = 5;
while (row > 1) {
    // some code
}

attempt = 3;
do {
    // some code
} while (attempt > 0);
5
В рамках одного метода, при использовании не вложенных циклов, не меняйте имена счетчиков
неправильно:
for (int i = 0; i < 10; i++) {
    // some code
}

for (int j = 10; j > 0; j--) {
    // some code
}

for (int k = 3; k > 0; k--) {
    // some code
}
правильно:
for (int i = 0; i < 10; i++) {

for (int i = 10; i > 0; i--) {

for (int i = 3; i > 0; i--) {
6
Если тело цикла состоит из одного выражения, всегда используйте { }. Это поможет избежать трудноуловимых багов
неправильно:
while (number < 5)
    System.out.println("number less");
    number++;

for (int i = 0; i < 10; i++)
    System.out.print("Hello, ");
    System.out.println("Mike!");
правильно:
while (number < 5) {
    System.out.println("number less");
    number++;
}

for (int i = 0; i < 10; i++) {
    System.out.print("Hello, ");
    System.out.println("Mike!");
}
7
При проверке boolean-значений не указывайте явно true или false
избыточно:
while (isUnique() == true) {
правильно:
while (isUnique()) {
Автор: Чимаев Максим
Оцените статью, если она вам понравилась!