Операторы и выражения
Арифметические, логические, битовые операторы
Ресурсы
Основные понятия
Выражение (expression) - это конструкция, которая вычисляется и возвращает результат. Результатом может быть:
- Переменная (lvalue)
- Значение (value)
- Ничего (void - для методов без возвращаемого значения)
При вычислении выражение может завершиться:
- Нормально (normal completion) - если все шаги выполнены без исключений
- Резко (abrupt completion) - если возникло исключение
Типы выражений
Выражения классифицируются по синтаксическим формам:
- Имена выражений
- Первичные выражения (литералы, this, создание объектов)
- Унарные операторы
- Бинарные операторы
- Тернарный оператор
? : - Лямбда-выражения
- Switch-выражения
Порядок вычисления
Java гарантирует строгий порядок вычисления выражений:
Основные правила
- Левый операнд вычисляется первым - в бинарных операторах левый операнд всегда вычисляется до правого
- Операнды вычисляются до операции - все операнды вычисляются полностью перед выполнением операции
- Соблюдение скобок и приоритета - порядок определяется скобками и приоритетом операторов
- Аргументы слева направо - аргументы методов вычисляются слева направо
int i = 2;
int j = (i=3) * i; // j = 9, не 6
Литералы
Литерал - фиксированное неизменяемое значение.
Типы литералов:
- Целочисленные:
42,0xFF,0b1010,100L - Вещественные:
3.14,2.5f,1.0e-10 - Логические:
true,false - Символьные:
'a','\n','\u0041' - Строковые:
"Hello", текстовые блоки"""...""" - Null:
null
Первичные выражения
this и super
this- ссылка на текущий объектsuper- ссылка на родительский класс- Квалифицированный this:
ClassName.this
Литералы классов
Class<String> c1 = String.class;
Class<Integer> c2 = int.class;
Class<Void> c3 = void.class;
Создание объектов
new ClassName()
new ClassName(args)
new ClassName() { /* анонимный класс */ }
Унарные операторы
Инкремент и декремент
++x- префиксный инкремент (сначала увеличение, потом использование)x++- постфиксный инкремент (сначала использование, потом увеличение)--x- префиксный декрементx--- постфиксный декремент
Знаковые операторы
+x- унарный плюс-x- унарный минус (смена знака)
Логические и битовые
!x- логическое отрицание (НЕ)~x- побитовое отрицание (инверсия битов)
Арифметические операторы
Мультипликативные
*- умножение/- деление (целочисленное для int, обычное для float/double)%- остаток от деления
int a = 7 / 2; // 3
double b = 7.0 / 2; // 3.5
int c = 7 % 2; // 1
Важно: Деление на ноль для целых чисел выбрасывает ArithmeticException, для вещественных - возвращает Infinity или NaN.
Аддитивные
+- сложение или конкатенация строк-- вычитание
int sum = 5 + 3; // 8
String s = "Hello" + "World"; // "HelloWorld"
String s2 = "Value: " + 42; // "Value: 42"
Операторы сдвига
Работают только с целочисленными типами:
<<- сдвиг влево (умножение на 2^n)>>- арифметический сдвиг вправо (деление на 2^n, сохраняет знак)>>>- логический сдвиг вправо (заполняет нулями слева)
int x = 8;
x << 2; // 32 (8 * 4)
x >> 2; // 2 (8 / 4)
int y = -8;
y >> 2; // -2 (знак сохраняется)
y >>> 2; // 1073741822 (беззнаковый сдвиг)
Операторы сравнения
Числовые операторы сравнения
<- меньше<=- меньше или равно>- больше>=- больше или равно
Результат: boolean
instanceof
Проверяет принадлежность объекта к типу:
if (obj instanceof String) {
String s = (String) obj;
}
// С pattern matching (Java 16+)
if (obj instanceof String s) {
// s доступна здесь
}
Операторы равенства
Для примитивов
==- равенство значений!=- неравенство значений
Для ссылок
==- проверка идентичности (ссылаются ли на один объект)!=- проверка неидентичности
String s1 = new String("Hello");
String s2 = new String("Hello");
s1 == s2; // false (разные объекты)
s1.equals(s2); // true (одинаковое содержимое)
Битовые и логические операторы
Целочисленные битовые
&- побитовое И (AND)|- побитовое ИЛИ (OR)^- побитовое исключающее ИЛИ (XOR)
int a = 0b1100;
int b = 0b1010;
a & b; // 0b1000 (8)
a | b; // 0b1110 (14)
a ^ b; // 0b0110 (6)
Логические для boolean
&- логическое И (вычисляет оба операнда)|- логическое ИЛИ (вычисляет оба операнда)^- логическое XOR
Условные логические (короткое замыкание)
&&- условное И (если левый false, правый не вычисляется)||- условное ИЛИ (если левый true, правый не вычисляется)
if (obj != null && obj.isValid()) { // безопасно
// obj.isValid() не вызовется если obj == null
}
Тернарный оператор
Синтаксис: условие ? значение_если_true : значение_если_false
int max = (a > b) ? a : b;
String status = (age >= 18) ? "Взрослый" : "Ребёнок";
Типы результата:
- Если оба операнда числовые - выбирается общий числовой тип
- Если оба boolean - результат boolean
- Если ссылочные типы - выбирается общий родительский тип
Операторы присваивания
Простое присваивание
=- присваивание значения
int x = 5;
String s = "Hello";
Составные операторы присваивания
Комбинируют операцию с присваиванием:
+=- сложение с присваиванием-=- вычитание с присваиванием*=- умножение с присваиванием/=- деление с присваиванием%=- остаток с присваиванием&=- побитовое И с присваиванием|=- побитовое ИЛИ с присваиванием^=- побитовое XOR с присваиванием<<=- сдвиг влево с присваиванием>>=- сдвиг вправо с присваиванием>>>=- беззнаковый сдвиг с присваиванием
int x = 10;
x += 5; // эквивалентно x = x + 5; (x = 15)
x *= 2; // эквивалентно x = x * 2; (x = 30)
Важно: Составные операторы автоматически приводят результат к типу левого операнда:
byte b = 5;
b += 10; // ОК, эквивалентно b = (byte)(b + 10)
b = b + 10; // ОШИБКА компиляции! (требуется явное приведение)
Приоритет операторов
От высшего к низшему (сверху вниз):
- Постфиксные:
expr++,expr-- - Унарные:
++expr,--expr,+,-,~,! - Приведение типов:
(type) - Мультипликативные:
*,/,% - Аддитивные:
+,- - Сдвиг:
<<,>>,>>> - Сравнение:
<,>,<=,>=,instanceof - Равенство:
==,!= - Побитовое И:
& - Побитовое XOR:
^ - Побитовое ИЛИ:
| - Логическое И:
&& - Логическое ИЛИ:
|| - Тернарный:
? : - Присваивание:
=,+=,-=,*=,/=,%=,&=,^=,|=,<<=,>>=,>>>= - Лямбда:
->
int result = 2 + 3 * 4; // 14, не 20 (* выше +)
int result2 = (2 + 3) * 4; // 20 (скобки меняют порядок)
boolean b = x > 0 && y < 10; // сначала сравнения, потом &&
Особенности вещественных чисел
Java полностью поддерживает IEEE 754:
- Специальные значения:
Infinity,-Infinity,NaN - Операции с NaN всегда возвращают NaN
- Сравнения с NaN всегда false (кроме
!=, которое true)
double inf = 1.0 / 0.0; // Infinity
double nan = 0.0 / 0.0; // NaN
nan == nan; // false
Double.isNaN(nan); // true
Политики округления:
- Round to nearest (округление к ближайшему) - для большинства операций
- Round toward zero (округление к нулю) - при приведении к целому типу
Приведение типов (Cast)
Синтаксис: (type) expression
double d = 3.14;
int i = (int) d; // 3 (отбрасывается дробная часть)
Object obj = "Hello";
String s = (String) obj; // ОК
// Небезопасное приведение вызывает ClassCastException
Integer num = (Integer) obj; // Runtime error!
Создание массивов
С указанием размера
int[] arr1 = new int[10];
int[][] arr2 = new int[5][10];
int[][] arr3 = new int[5][]; // "рваный" массив
С инициализацией
int[] arr1 = {1, 2, 3, 4, 5};
int[][] arr2 = {{1, 2}, {3, 4}, {5, 6}};
String[] names = new String[] {"Alice", "Bob"};
Доступ к массиву
Синтаксис: array[index]
int[] arr = {10, 20, 30};
int x = arr[0]; // 10
arr[1] = 25; // изменение элемента
// Проверки времени выполнения
arr[5]; // ArrayIndexOutOfBoundsException
int[] nullArr = null;
nullArr[0]; // NullPointerException
Вызов методов
object.method()
object.method(arg1, arg2)
ClassName.staticMethod()
Перегрузка разрешается во время компиляции на основе типов аргументов.
Ссылки на методы (Method References)
Синтаксис для функционального программирования (Java 8+):
// Ссылка на статический метод
Function<String, Integer> parser = Integer::parseInt;
// Ссылка на метод экземпляра
List<String> list = Arrays.asList("a", "b", "c");
list.forEach(System.out::println);
// Ссылка на конструктор
Supplier<List<String>> listSupplier = ArrayList::new;
Лямбда-выражения
Синтаксис: (parameters) -> expression или (parameters) -> { statements }
// Без параметров
Runnable r = () -> System.out.println("Hello");
// С одним параметром (скобки необязательны)
Consumer<String> printer = s -> System.out.println(s);
Consumer<String> printer2 = (String s) -> System.out.println(s);
// С несколькими параметрами
Comparator<Integer> comp = (a, b) -> a.compareTo(b);
// С телом блока
BiFunction<Integer, Integer, Integer> sum = (a, b) -> {
int result = a + b;
return result;
};
Switch-выражения (Java 14+)
// Традиционный switch-statement
switch (day) {
case MONDAY:
case FRIDAY:
System.out.println("Work");
break;
case SATURDAY:
case SUNDAY:
System.out.println("Rest");
break;
}
// Switch-выражение (возвращает значение)
String activity = switch (day) {
case MONDAY, FRIDAY -> "Work";
case SATURDAY, SUNDAY -> "Rest";
default -> "Other";
};
// С блоком и yield
int numLetters = switch (day) {
case MONDAY, FRIDAY, SUNDAY -> 6;
case TUESDAY -> 7;
case THURSDAY, SATURDAY -> 8;
case WEDNESDAY -> {
System.out.println("Checking...");
yield 9;
}
};
Константные выражения
Константное выражение - это выражение, значение которого может быть вычислено на этапе компиляции.
Константными могут быть:
- Литералы примитивных типов и String
- Приведение типов к примитивам или String
- Унарные операторы:
+,-,~,! - Мультипликативные:
*,/,% - Аддитивные:
+,- - Сдвиг:
<<,>>,>>> - Сравнение:
<,<=,>,>= - Равенство:
==,!= - Побитовые и логические:
&,^,| - Условные:
&&,|| - Тернарный:
? : - Скобки:
( ) - Простые имена final переменных, инициализированных константными выражениями
final int MAX = 100;
final int MIN = 0;
final int RANGE = MAX - MIN; // константное выражение
// Используется в switch
switch (value) {
case MIN: // OK
case MAX: // OK
case RANGE: // OK
}
Исключения времени выполнения
Операторы могут вызывать исключения:
NullPointerException- операция с nullArithmeticException- деление на ноль (целые числа)ArrayIndexOutOfBoundsException- выход за границы массиваNegativeArraySizeException- отрицательный размер массиваClassCastException- недопустимое приведение типовArrayStoreException- несовместимый тип при записи в массивOutOfMemoryError- нехватка памяти
Автоупаковка и автораспаковка
Автоматическое преобразование между примитивами и обёртками:
// Автоупаковка (boxing)
Integer obj = 42; // эквивалентно Integer.valueOf(42)
// Автораспаковка (unboxing)
int x = obj; // эквивалентно obj.intValue()
// В выражениях
Integer a = 10;
Integer b = 20;
Integer sum = a + b; // автораспаковка, затем автоупаковка
// Осторожно с null!
Integer nullObj = null;
int y = nullObj; // NullPointerException!
Важные особенности и best practices
- Используйте скобки для ясности - не полагайтесь только на приоритет операторов
- Избегайте сложных выражений - разбивайте на несколько строк для читаемости
- Осторожно с автоупаковкой - может вызвать NullPointerException
- Используйте equals() для объектов - не == (если не проверяете идентичность)
- Проверяйте null перед && - используйте короткое замыкание
- Избегайте деления целых на ноль - проверяйте делитель
- Осторожно с NaN - проверяйте через Double.isNaN()
- Используйте составные операторы - они короче и автоматически приводят тип
// Хорошо
if (obj != null && obj.isValid()) { }
// Плохо
if (obj.isValid() && obj != null) { } // может быть NPE
// Хорошо
String.valueOf(obj).equals("value")
// Плохо
obj.toString().equals("value") // может быть NPE