Целочисленная арифметика и переполнение
Разберитесь в целочисленном делении, остатке, поведении при переполнении и способах его обнаружения.
«Целочисленная арифметика и переполнение» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Целочисленная арифметика и переполнение
Целые числа Java имеют фиксированный размер. Если вычисление превышает максимальное или минимальное значение, оно незаметно переходит через границу диапазона — исключение не возникает. Понимание этого помогает предотвращать трудноуловимые ошибки.
Диапазоны целых чисел
Каждый целочисленный тип имеет ограниченный диапазон, определяемый разрядностью:
byte: от -128 до 127short: от -32,768 до 32,767int: от -2,147,483,648 до 2,147,483,647long: от -9.2 × 10^18 до 9.2 × 10^18
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MIN_VALUE); // -2147483648
System.out.println(Long.MAX_VALUE); // 9223372036854775807
System.out.println(Byte.MAX_VALUE); // 127Переполнение: циклический переход
Прибавление 1 к Integer.MAX_VALUE даёт Integer.MIN_VALUE. Это арифметика в дополнительном коде — Java не гарантирует особого поведения и не выбрасывает исключение.
int max = Integer.MAX_VALUE;
System.out.println(max + 1); // -2147483648 (overflow!)
byte b = 127;
b++; // wraps to -128
System.out.println(b); // -128
// Real bug: counting votes in a large election with int
int votes = Integer.MAX_VALUE;
votes += 100; // silently wrong
System.out.println(votes); // negative number!Обнаружение переполнения с помощью Math.addExact
В Java 8+ появились Math.addExact(), multiplyExact() и subtractExact(), которые при переполнении выбрасывают ArithmeticException, а не выполняют незаметный циклический переход.
try {
int result = Math.addExact(Integer.MAX_VALUE, 1);
} catch (ArithmeticException e) {
System.out.println("Overflow detected!"); // prints this
}
try {
long safe = Math.multiplyExact(100_000L, 100_000L);
System.out.println(safe); // 10000000000
} catch (ArithmeticException e) {
System.out.println("Multiply overflow");
}Целочисленное деление и остаток
Целочисленное деление отбрасывает дробную часть к нулю. Оператор % возвращает остаток со знаком делимого. Остерегайтесь деления на ноль — оно вызывает ArithmeticException.
System.out.println(10 / 3); // 3 (not 3.33)
System.out.println(10 % 3); // 1
System.out.println(-10 % 3); // -1 (sign follows dividend)
System.out.println(-10 % -3); // -1
try {
int x = 5 / 0; // ArithmeticException: / by zero
} catch (ArithmeticException e) {
System.out.println(e.getMessage()); // / by zero
}
// Float division by zero gives Infinity, not exception
System.out.println(5.0 / 0); // InfinityАрифметика с большими числами типа long
Используйте long, если значения могут выйти за диапазон int. Всегда добавляйте L к литералам типа long, чтобы предотвратить переполнение до присваивания.
// Bug: multiplication done as int, then widened
long wrong = 1_000_000 * 1_000_000; // overflows int!
System.out.println(wrong); // -727379968 (wrong!)
// Fix: one operand is long
long correct = 1_000_000L * 1_000_000L;
System.out.println(correct); // 1000000000000
// Or cast first
long alsOk = (long) 1_000_000 * 1_000_000;
System.out.println(alsOk); // 1000000000000Операторы побитового сдвига
Операторы побитового сдвига позволяют быстро умножать и делить на степени числа 2:
n << k— сдвиг влево: умножение на 2^kn >> k— знаковый сдвиг вправо: деление на 2^kn >>> k— беззнаковый сдвиг вправо: заполнение нулями
int n = 8;
System.out.println(n << 1); // 16 (8 * 2)
System.out.println(n << 2); // 32 (8 * 4)
System.out.println(n >> 1); // 4 (8 / 2)
System.out.println(n >> 2); // 2 (8 / 4)
// Check if number is power of 2
boolean isPow2 = n > 0 && (n & (n - 1)) == 0;
System.out.println(isPow2); // trueПобитовые AND, OR и XOR
Побитовые операторы работают с отдельными битами и используются для разрешений, флагов и низкоуровневых протоколов.
int a = 0b1010; // 10
int b = 0b1100; // 12
System.out.println(Integer.toBinaryString(a & b)); // 1000 (AND = 8)
System.out.println(Integer.toBinaryString(a | b)); // 1110 (OR = 14)
System.out.println(Integer.toBinaryString(a ^ b)); // 0110 (XOR = 6)
System.out.println(Integer.toBinaryString(~a)); // ...11110101 (NOT)
// Permission flags example
int READ = 0b001;
int WRITE = 0b010;
int EXEC = 0b100;
int perms = READ | WRITE; // user has read+write
System.out.println((perms & EXEC) != 0); // false — no execПодчёркивания в числовых литералах
Java 7+ разрешает использовать подчёркивания в числовых литералах для улучшения читаемости. Компилятор игнорирует их.
int million = 1_000_000;
long creditCard = 4_111_1111_1111_1111L;
double pi = 3.141_592_653_589_793;
int hex = 0xFF_EC_D1_2E;
int binary = 0b0001_0101_0110;
System.out.println(million); // 1000000
System.out.println(creditCard); // 4111111111111111BigInteger для произвольной точности
Если значения выходят за пределы long, используйте BigInteger. У него нет переполнения, но он работает медленнее примитивных типов. Используйте его для криптографических ключей, факториалов и астрономически больших чисел.
import java.math.BigInteger;
BigInteger factorial100 = BigInteger.ONE;
for (int i = 2; i <= 100; i++) {
factorial100 = factorial100.multiply(BigInteger.valueOf(i));
}
System.out.println(factorial100.toString().length() + " digits"); // 158 digits
BigInteger a = new BigInteger("999999999999999999999999999999");
BigInteger b = new BigInteger("1");
System.out.println(a.add(b)); // 1000000000000000000000000000000Практика: счётчик с защитой от переполнения
Шаблон реализации счётчика, который безопасно обрабатывает переполнение: он использует Math.addExact и при переполнении переходит к Long.MAX_VALUE.
class SafeCounter {
private long count = 0;
public void increment() {
try {
count = Math.addExact(count, 1L);
} catch (ArithmeticException e) {
count = Long.MAX_VALUE; // cap at max
}
}
public long get() { return count; }
}
SafeCounter sc = new SafeCounter();
sc.increment();
sc.increment();
System.out.println(sc.get()); // 2Быстрая проверка
Каково значение следующего выражения?
long result = 1_000_000 * 1_000_000; System.out.println(result);
Итоги: целочисленная арифметика и переполнение
Главные выводы:
- Целочисленное переполнение происходит незаметно и по умолчанию не вызывает исключение
- Используйте Math.addExact/multiplyExact/subtractExact, чтобы обнаруживать переполнение
- Целочисленное деление усекает результат к нулю; знак % определяется делимым
- Используйте литералы long (суффикс L), если промежуточные результаты могут выйти за пределы int
- BigInteger обрабатывает сколь угодно большие значения без переполнения
- Операторы побитового сдвига — быстрая альтернатива умножению и делению на степени двойки
Часто задаваемые вопросы
Урок «Целочисленная арифметика и переполнение» бесплатный?
Да — полный текст урока «Целочисленная арифметика и переполнение» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Целочисленная арифметика и переполнение»?
Разберитесь в целочисленном делении, остатке, поведении при переполнении и способах его обнаружения. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Целочисленная арифметика и переполнение»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы класса Math
- Целочисленная арифметика и переполнение
- BigDecimal для финансовых вычислений
- NumberFormat и printf