Инкремент ++ увеличивает переменную на единицу, декремент -- уменьшает. У обоих есть две формы: префиксная (++x) сначала меняет переменную и возвращает новое значение, постфиксная (x++) возвращает старое значение и только потом меняет переменную. Отдельной строкой обе формы работают одинаково, разница видна только внутри выражения: при x = 10 выражение 13 - x++ дает 3, а 13 - ++x дает 2, при этом x в обоих случаях станет 11 (так это описывает и учебник Oracle). Ниже разберем обе формы на примерах, типичные ошибки и случаи, где инкремент уместен. Наверняка вы уже сталкивались с этими i++ и ++i в циклах. И если честно, первый раз видя это, многие думают примерно так: "Ну ладно, i++ понятно... а вот ++i это что вообще такое? И зачем?". Давайте разбираться. Тема на самом деле простая, но есть один нюанс, который часто ставит новичков в тупик.Абстрактная заставка: строки кода на фоне цветных вертикальных полос от синего к красному

Что такое инкремент и декремент простыми словами

Начнем с базы. Инкремент это когда вы увеличиваете переменную на единицу. Пишется как два плюса: ++. Декремент, соответственно, это когда уменьшаете на единицу. Два минуса: --. Можно представить это так: вы охранник на входе в клуб и считаете гостей. Зашел один человек: щелк на счетчике, плюс один. Это инкремент. Вышел кто-то: минус один, декремент. Все просто, правда? Вот как это выглядит в коде:
int visitors = 10;
visitors++;  // теперь visitors = 11
visitors--;  // снова visitors = 10

System.out.println(visitors);  // выведет: 10
Да, можно было бы написать visitors = visitors + 1. Но согласитесь, visitors++ выглядит куда лаконичнее. Зачем усложнять жизнь?

Префиксная и постфиксная формы: вот тут и начинается веселье

А теперь держитесь! У инкремента и декремента есть две формы записи, и именно здесь обычно начинаются вопросы. Префиксная форма (prefix) это когда оператор стоит перед переменной: ++x или --x. Постфиксная форма (postfix) это когда оператор идет после: x++ или x--. Смотрите. Если вы просто увеличиваете переменную, без всяких вычислений, то абсолютно без разницы, какую форму использовать:
int x = 10;
int y = 10;

x++;   // x теперь 11
++y;   // y тоже 11

System.out.println("x: " + x + ", y: " + y);  // выведет: x: 11, y: 11
Результат один и тот же. Но! Когда вы начинаете использовать инкремент прямо внутри какого-то выражения, вот тут начинается интересное.

Постфиксная форма: используй сейчас, увеличь потом

Постфиксная форма работает по принципу "сначала отдай то, что есть, потом измени". Помните нашего охранника с табличкой? Представьте: заходит посетитель, второй охранник спрашивает "сколько народу?". Вы показываете табличку с текущей цифрой (допустим, 10), он записывает это число. И только после этого вы меняете цифру на табличке на 11.
int visitors = 10;
int reportedNumber = visitors++;

System.out.println("Сообщили: " + reportedNumber);  // выведет: 10
System.out.println("Реально в зале: " + visitors);  // выведет: 11
Видите? Сначала значение visitors (которое было 10) ушло в reportedNumber. И только потом visitors увеличился до 11.

Префиксная форма: измени сразу, потом используй

С префиксом все наоборот. Тут правило "сначала измени, а потом работай с новым значением". Та же ситуация с охранником, но теперь вы сначала меняете цифру на табличке (10 становится 11), а уже потом показываете ее коллеге. Он и записывает 11.
int visitors = 10;
int reportedNumber = ++visitors;

System.out.println("Сообщили: " + reportedNumber);  // выведет: 11
System.out.println("Реально в зале: " + visitors);  // выведет: 11

Разбираем на живых примерах

Ладно, теория это хорошо. Но давайте посмотрим, как это реально работает в коде с вычислениями:
int x = 10;
int c = 10;

int y = 13 - x++;  // постфиксная форма
int z = 13 - ++c;  // префиксная форма

System.out.println("y: " + y + ", z: " + z);  // выведет: y: 3, z: 2
System.out.println("x: " + x + ", c: " + c);  // выведет: x: 11, c: 11
Стоп. И x, и c изначально равны 10. Выражения вроде одинаковые. Но результаты разные: y = 3, а z = 2! Как так? Давайте по шагам разберем каждый случай. Постфиксная форма (x++):
// x = 10
// Шаг 1: берем текущее значение x (оно пока 10) и используем в вычислении
y = 13 - 10;  // получается y = 3
// Шаг 2: и только сейчас x увеличивается
x = 11;
Префиксная форма (++c):
// c = 10
// Шаг 1: сразу увеличиваем c
c = 11;
// Шаг 2: теперь используем уже новое значение (11) в вычислении
z = 13 - 11;  // получается z = 2
Вот и вся разница! В первом случае мы взяли старое значение для расчета, во втором сначала изменили, потом посчитали.

Все четыре формы в одной таблице

Для наглядности сведем обе формы инкремента и декремента вместе (во всех примерах x изначально равен 10):
Форма Что возвращает в выражении Когда меняется переменная Пример Итог
x++ (постфикс) старое значение после того, как значение использовано y = 13 - x++ y = 3, x = 11
++x (префикс) новое значение до использования z = 13 - ++x z = 2, x = 11
x-- (постфикс) старое значение после того, как значение использовано y = 13 - x-- y = 3, x = 9
--x (префикс) новое значение до использования z = 13 - --x z = 4, x = 9

Типичные ошибки (и как не наступать на эти грабли)

Ошибка 1: "Почему i++ не возвращает увеличенное значение?"

Классика! Многие ожидают, что i++ сразу вернет новое значение. Но нет:
int i = 5;
System.out.println(i++);  // выведет 5, а не 6!
System.out.println(i);    // а вот теперь будет 6
Если нужно именно увеличенное значение сразу, используйте префикс:
int i = 5;
System.out.println(++i);  // выведет 6

Ошибка 2: Сложные выражения с кучей инкрементов

Вот это вообще не делайте, пожалуйста:
int x = 5;
int result = x++ + ++x + x++;  // кто вообще это придумал?!
Серьезно, даже опытные разработчики будут минут 10 разбираться, что тут происходит. А еще легко ошибиться. Для справки: операнды вычисляются строго слева направо (JLS, раздел 15.7), так что при x = 5 получится 5 + 7 + 7 = 19, а x станет 8. Если такое вычисление действительно нужно, лучше расписать его по шагам:
int x = 5;
int first = x;    // x++ отдает старое значение 5...
x++;              // ...и только потом x = 6
x++;              // ++x: сначала x = 7...
int second = x;   // ...потом отдает новое значение 7
int third = x;    // x++ отдает 7...
x++;              // ...потом x = 8
int result = first + second + third;  // 19, как и в одной строке
Да, получилось длиннее. Зато через месяц, когда вы вернетесь к этому коду, сразу поймете, что к чему.

Ошибка 3: Инкремент в индексах массивов

Еще одна засада: инкремент прямо в индексе массива:
int[] numbers = {10, 20, 30};
int i = 0;

// Что тут вообще произойдет?
numbers[i] = numbers[i++];
Тут можно такого наворотить... Формально в Java (в отличие от C) порядок здесь определен: сначала вычисляется левая часть, то есть индекс i (пока 0), потом правая, numbers[0], и только затем i становится 1 (JLS, раздел 15.26.1). В итоге numbers[0] = 10, i = 1. Но держать это в голове не нужно. Лучше не рисковать и писать явно:
numbers[i] = numbers[i];
i++;
Скучно? Может быть. Зато работает предсказуемо.

А где это вообще применяется?

Нормальные варианты использования: 1. В циклах for, тут это классика жанра:
for (int i = 0; i < 10; i++) {
    System.out.println(i);
}
2. Счетчики:
int errorCount = 0;
if (hasError) {
    errorCount++;
}
3. Перебор элементов в цикле:
int index = 0;
while (index < array.length) {
    process(array[index++]);
}
Когда лучше НЕ использовать: - Если выражение получается сложным и непонятным, лучше разбить на несколько строк - Иногда просто += 1 будет читаться понятнее - В современном Java вообще часто можно обойтись forEach или Stream API вместо циклов со счетчиками

Коротко о главном

Давайте еще раз пробежимся по основным моментам: • ++ увеличивает на единицу, -- уменьшает • x++ (постфикс): используем старое значение, потом меняем переменную • ++x (префикс): сначала меняем переменную, потом используем новое значение • Если инкремент стоит отдельной строкой, без разницы, какую форму использовать • Не пишите сложные выражения с кучей инкрементов. Просто не надо. • Золотое правило: если сами не уверены, что код делает, упростите его Знаете, инкремент и декремент это штуки простые, но поначалу могут здорово запутать. Особенно эта разница между префиксом и постфиксом. Не переживайте, если с первого раза не все понятно: это нормально! Лучший способ разобраться: просто больше практиковаться. Попробуйте написать несколько примеров самостоятельно, поэкспериментируйте с разными вариантами. Запустите код, посмотрите что выводится. Очень скоро вы будете использовать эти операторы автоматически, даже не задумываясь. Удачи в изучении Java!

Вопросы и ответы

Чем ++i отличается от i++?

Обе записи увеличивают i на единицу. Разница только в том, что возвращает выражение: ++i (префикс) возвращает уже увеличенное значение, i++ (постфикс) возвращает старое, а переменную увеличивает после. В отдельной строке (i++; или ++i;) разницы нет, она проявляется внутри выражения: при i = 5 вызов System.out.println(i++) печатает 5, а System.out.println(++i) печатает 6.

Что быстрее: i++ или ++i?

В Java для примитивов разницы нет. Когда результат выражения не используется, javac компилирует обе записи в одинаковый байткод (для локальной переменной int это одна инструкция iinc). Выбирайте форму по смыслу, а не ради скорости; в циклах for принято писать i++.

К каким типам можно применять ++ и --?

К любой переменной числового типа: byte, short, char, int, long, float, double, а также к их оберткам вроде Integer (с распаковкой и обратной упаковкой). К boolean, String и final-переменным нельзя: будет ошибка компиляции. Результат приводится к типу переменной, поэтому byte b = 127; b++; даст -128 (JLS, раздел 15.14.2).

Чем x++ отличается от x += 1 и x = x + 1?

Как отдельная инструкция все три увеличивают x на единицу. Отличия два. x++ и ++x внутри выражения возвращают старое или новое значение, x += 1 всегда новое. И x++ с x += 1 неявно приводят результат к типу переменной, а x = x + 1 нет: для byte b запись b = b + 1 не скомпилируется без приведения, потому что b + 1 это уже int, а b++ и b += 1 скомпилируются (JLS, раздел 15.26.2). Подробнее об операциях над числами есть отдельная статья.

Что будет, если увеличить Integer.MAX_VALUE на единицу?

Переполнение без исключения: int x = Integer.MAX_VALUE; x++; даст -2147483648, то есть Integer.MIN_VALUE. Java не проверяет переполнение ни при ++, ни при обычной арифметике. Если переполнение нужно поймать, используйте Math.incrementExact(x): он бросит ArithmeticException.

Почему x++ + ++x + x++ при x = 5 дает 19?

Операнды вычисляются строго слева направо (JLS, раздел 15.7). Первый x++ возвращает 5 и делает x = 6; ++x делает x = 7 и возвращает 7; второй x++ возвращает 7 и делает x = 8. Итого 5 + 7 + 7 = 19, а x равен 8. Сама спецификация советует не опираться на это правило: в выражении должен быть максимум один побочный эффект.

Читайте также