0Pricing
Java Academy · Урок

Целочисленная арифметика и переполнение

Разберитесь в целочисленном делении, остатке, поведении при переполнении и способах его обнаружения.

«Целочисленная арифметика и переполнение» — бесплатный урок Java Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.

Целочисленная арифметика и переполнение

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

Диапазоны целых чисел

Каждый целочисленный тип имеет ограниченный диапазон, определяемый разрядностью:

  • byte: от -128 до 127
  • short: от -32,768 до 32,767
  • int: от -2,147,483,648 до 2,147,483,647
  • long: от -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^k
  • n >> k — знаковый сдвиг вправо: деление на 2^k
  • n >>> 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); // 4111111111111111

BigInteger для произвольной точности

Если значения выходят за пределы 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 — локальная установка не требуется.

Все уроки этого курса

  1. Основы класса Math
  2. Целочисленная арифметика и переполнение
  3. BigDecimal для финансовых вычислений
  4. NumberFormat и printf
← Назад к Java Academy