1. Обзор
В этом руководстве мы рассмотрим составные операторы Java, их типы и то, как Java их оценивает.
Мы также объясним, как работает неявное приведение типов.
2. Составные операторы присваивания
Оператор присваивания — это бинарный оператор, который присваивает результат правой части переменной в левой части. Самым простым является оператор присваивания «=» :
int x = 5;
Этот оператор объявляет новую переменную x
, присваивает x
значение 5
и возвращает 5
.
Составные операторы присваивания — это более короткий способ применить арифметическую или побитовую операцию и присвоить значение операции переменной в левой части.
Например, следующие два оператора умножения эквивалентны, то есть a
и b
будут иметь одно и то же значение:
int a = 3, b = 3, c = -2;
a = a * c; // Simple assignment operator
b *= c; // Compound assignment operator
Важно отметить, что переменная слева от составного оператора присваивания должна быть уже объявлена. Другими словами, составные операторы нельзя использовать для объявления новой переменной.
Подобно оператору присваивания «=», составные операторы возвращают присвоенный результат выражения:
long x = 1;
long y = (x+=2);
И x
, и y
будут содержать значение 3
.
Присваивание (x+=2)
делает две вещи: во-первых, оно добавляет 2 к значению переменной x
, которое становится равным 3;
во-вторых, он возвращает значение присваивания, которое также равно 3
.
3. Типы составных операторов присваивания
Java поддерживает 11 составных операторов присваивания. Мы можем сгруппировать их в арифметические и побитовые операторы.
Давайте пройдемся по арифметическим операторам и операциям, которые они выполняют:
- Приращение:
+=
- Декрементация:
-=
- Умножение:
*=
- Раздел:
/=
- Модуль:
%=
Затем у нас также есть побитовые операторы:
- И, двоичный:
&=
- Исключающее ИЛИ, двоичное:
^=
- Включающее ИЛИ, двоичное:
|=
- Сдвиг влево, двоичный:
<<=
- Сдвиг вправо, двоичный:
>=
- Сдвиг вправо нулевой заливки:
>>>=
Рассмотрим несколько примеров таких операций:
// Simple assignment
int x = 5; // x is 5
// Incrementation
x += 5; // x is 10
// Decrementation
x -= 2; // x is 8
// Multiplication
x *= 2; // x is 16
// Modulus
x %= 3; // x is 1
// Binary AND
x &= 4; // x is 0
// Binary exclusive OR
x ^= 4; // x is 4
// Binary inclusive OR
x |= 8; // x is 12
Как мы видим здесь, синтаксис для использования этих операторов согласован.
4. Оценка сложных операций присваивания
Есть два способа, которыми Java оценивает составные операции.
Во- первых, когда левый операнд не является массивом, Java будет по порядку:
- Убедитесь, что операнд является объявленной переменной
- Сохраните значение левого операнда
- Оценить правый операнд
- Выполните бинарную операцию, как указано составным оператором
- Преобразование результата бинарной операции в тип левой переменной (неявное приведение)
- Назначьте преобразованный результат левой переменной
Далее, когда левый операнд является массивом, шаги немного отличаются:
- Проверьте выражение массива в левой части и сгенерируйте
исключение NullPointerException
илиArrayIndexOutOfBoundsException
, если оно неверно. - Сохраните элемент массива в индексе
- Оценить правый операнд
- Проверьте, является ли выбранный компонент массива примитивным или ссылочным типом, а затем выполните те же действия, что и в первом списке, как если бы левый операнд был переменной.
Если какой-либо шаг оценки завершается неудачно, Java не продолжает выполнять следующие шаги.
Приведем несколько примеров, связанных с вычислением этих операций над элементом массива:
int[] numbers = null;
// Trying Incrementation
numbers[2] += 5;
Как и следовало ожидать, это вызовет исключение NullPointerException
.
Однако, если мы присвоим начальное значение массиву:
int[] numbers = {0, 1};
// Trying Incrementation
numbers[2] += 5;
Мы бы избавились от исключения NullPointerException,
но все равно получили бы исключение ArrayIndexOutOfBoundsException
, так как используемый индекс неверен.
Если мы это исправим, операция завершится успешно:
int[] numbers = {0, 1};
// Incrementation
numbers[1] += 5; // x is now 6
Наконец, переменная x
будет равна 6
в конце присваивания.
5. Неявное приведение
Одна из причин, по которой составные операторы полезны, заключается в том, что они не только обеспечивают более короткий путь для операций, но и неявно приводят переменные.
Формально составное выражение присваивания вида:
Е1 оп= Е2
эквивалентно:
E1 - (T) (E1 или E2)
где T
— тип E1
.
Рассмотрим следующий пример:
long number = 10;
int i = number;
i = i * number; // Does not compile
Давайте рассмотрим, почему последняя строка не компилируется.
Java автоматически продвигает меньшие типы данных к большим, когда они находятся вместе в операции, но выдает ошибку при попытке преобразования из больших типов в меньшие .
Итак, сначала меня
повысят до длинной
, а затем умножение даст результат 10L.
Длинный результат будет присвоен i
, который является int
, и это вызовет ошибку.
Это можно исправить с помощью явного приведения:
i = (int) i * number;
Составные операторы присваивания Java идеально подходят в этом случае, потому что они выполняют неявное приведение типов:
i *= number;
Этот оператор прекрасно работает, приводя результат умножения к типу int
и присваивая значение левой переменной i
.
6. Заключение
В этой статье мы рассмотрели составные операторы в Java, приведя несколько примеров и их различные типы. Мы объяснили, как Java оценивает эти операции.
Наконец, мы также рассмотрели неявное приведение типов — одну из причин, по которой эти сокращенные операторы полезны.
Как всегда, все фрагменты кода, упомянутые в этой статье, можно найти в нашем репозитории GitHub .