Операторы в Java: для чего нужны и какие бывают
2026-02-21 17:48 Diff

#статьи

  • 17 фев 2023
  • 0

Знакомимся с основными инструментами языка и учимся работать с ними на практике.

Иллюстрация: Оля Ежак для Skillbox Media

Программист, музыкант. Знает Java, C# и Unity3D, но не собирается останавливаться на достигнутом.

Все мы со школы знакомы с математическими знаками + (плюс),  (минус), = (равно) и так далее. В Java и других языках программирования эти символы называются операторами. Они нужны для того, чтобы сообщить компилятору, какое действие совершать с операндами — числами, строками, объектами и другими значениями по обе стороны оператора.

Скриншот: Лев Сергеев для Skillbox Media

Например, на картинке выше оператор + говорит компилятору: «Сложи два числа». Но на практике чисел в выражении может быть разное количество — и одно, и два, и три. В этих случаях и операторы будут называться по-разному: унарные — что-то делают с одним операндом, бинарные — с двумя, и тернарные — с тремя.

В этой статье рассмотрим не только эти, но все основные виды операторов в Java:

Ещё поговорим о приоритетах операторов — разберёмся, в каком порядке они выполняются. Если вы хотите просто освежить в памяти суть работы разных операторов, можете сразу перейти к шпаргалке в конце статьи.

Оператор присваивания в Java — это знак = (равно). Его задача — переложить значение переменной справа в переменную слева:

Скриншот: Лев Сергеев для Skillbox Media

Вот как это выглядит в коде (в комментариях — разбор синтаксиса):

int x = 1; // Объявляем числовую переменную 'x' со значением 1 boolean a, b; // Объявляем логические переменные 'a' и 'b' a = b = true;

Что происходит: значение true присваивается переменной b. В свою очередь, значение переменной b присваивается переменной a.

Эти операторы выполняют простые арифметические действия: сложение, вычитание, умножение, деление и деление с остатком. Все арифметические операторы являются бинарными — то есть работают только с двумя значениями.

Что делает: складывает два операнда.

Ещё этот оператор применяется при соединении двух строк в одну. Например, в Java можно выполнить выражение «Я люблю» + «Москву» и получить «Я люблю Москву». Такая операция в программировании называется конкатенацией.

// Сложение чисел System.out.println(10 + 2); // Вывод: 12 // Конкатенация строк System.out.println("A" + "B"); // Вывод: AB // Сложение двух переменных типа 'char' System.out.println('a' + 'b'); // Вывод: 195

В Java типы данных char, byte и short при вычислениях неявно приводятся к типу int (целое число), поэтому с ними можно работать как с обычными числами. При этом тип данных char в Java отвечает за символы Unicode — каждый символ обозначает какое-то число. Например, символ a равен числу 97, а b — 98, поэтому в нашем примере и получилось значение 195.

Что делает: вычитает из левого операнда правый.

// Вычитание чисел System.out.println(10 - 2); // Вывод: 8

Что делает: возвращает произведение операндов.

// Умножение чисел System.out.println(10 * 2); // Вывод: 20

Что делает: делит левый операнд на правый.

// Деление без остатка System.out.println(10 / 2); // Вывод: 5 System.out.println(11 / 2); // Вывод: 5 // Деление на 'double' System.out.println(11 / 2d); // Вывод: 5.5

А вот что будет, если попробовать поделить на ноль в Java:

System.out.println(10 / 0); // Ошибка Вывод: Exception in thread "main" java.lang.ArithmeticException: / by zero

Делить число на ноль нельзя! Программа завершится, выбросив исключение. Но если привести выражение к типу 'double' (число с плавающей запятой), то вы получите «бесконечность»:

System.out.println(10 / 0d); // Вывод: Infinity

Что делает: возвращает остаток от деления.

// Деление чисел по модулю (получение остатка от деления) System.out.println(10 % 2); // Вывод: 0 System.out.println(11 % 2); // Вывод: 1

Эти операторы в Java тоже можно отнести к арифметическим, но есть нюанс — они работают только с одним операндом. Поэтому их и называют унарными.

Что делают: меняют значение числа на положительное или отрицательное.

int positive = 1; int negative = -2; // Унарный минус System.out.println(-positive); // Вывод: -1 // Унарный плюс System.out.println(+negative); // Вывод: 2

Что делают: инкремент — увеличивает значение переменной на единицу, а декремент — уменьшает.

В свою очередь, у декремента и инкремента есть две формы: префиксная и постфиксная. Звучит сложно, но на деле всё просто:

  • Префиксные операторы (++x) сразу меняют значение переменной и подставляют его в выражение.
  • Постфиксные операторы (x++) делают наоборот — сначала используют старое значение переменной и только потом подставляют новое.
int a = 0, b = 0, c = 0, d = 0; // Префиксный инкремент/декремент System.out.println(++a); // Вывод: 1 System.out.println(--b); // Вывод: -1 // Постфиксный (значение переменных изменится после вывода в консоль) System.out.println(c++); // Вывод: 0 System.out.println(d--); // Вывод: 0

Инкременты и декременты часто используют в циклах в качестве счётчика, когда нужно по очереди вывести все числа в каком-то диапазоне:

/* Префиксный инкремент в цикле. Сначала переменная i увеличивается на 1, а потом подставляется в цикл */ int i = 0; while (++i < 3){ System.out.print(i); } Вывод: 12/* Постфиксный инкремент в цикле. Сперва выполняется цикл c начальным значением i, и только потом i увеличивается на 1:*/ int i = 0; while (i++ < 3){ System.out.print(i); } Вывод: 123

Подробнее о декрементах и инкрементах можно почитать в нашей статье — рассказываем, как решать сложные выражения с этими операторами.

В Java есть ещё два унарных оператора: ! — логическое отрицание, и ~ — побитовое отрицание, но их мы рассмотрим чуть позже, когда будем разбираться с логическими и побитовыми операторами.

Что делают: сравнивают два операнда и выясняют отношения между ними — что больше, что меньше, что чему равняется. При вычислении такие операторы возвращают значение типа boolean:

  • true (правда);
  • false (ложь).

Всего в Java шесть операторов сравнения:

ОператорЧто означает==Равно>Больше, чем<Меньше, чем>=Больше или равно<=Меньше или равно!=Не равно

Давайте посмотрим, как выглядят операторы сравнения в коде — попробуем сравнить два целых числа и вывести результаты на экран:

// В этом примере мы спрашиваем, равен ли ноль единице: System.out.println(1 == 0); // Вывод: false (ложь) // А здесь — больше ли единица, чем ноль: System.out.println(1 > 0); // Вывод: true (правда) // Единица меньше или равна нулю? System.out.println(1 <= 0); // Вывод: false (ложь)

Ещё операторы сравнения часто используют в условных конструкциях. Это когда, в зависимости от условий, выполняется какой-то один блок кода. В этом случае, помимо операторов сравнения, нам понадобятся операторы ветвления: if-else, switch и так далее. Например, здесь мы просим Java напечатать слово true, если 1 не равно 0:

// Если '1' не равно '0', то напечатать "true" if(1 != 0) System.out.println("true"); // Вывод: true

Подробнее об операторах сравнения и условных конструкциях можно почитать в этой статье.

Что делают: комбинируют логические значения типа true и false.

В отличие от операторов сравнения, логические операторы работают не с отдельными числами, а с результатами выражений. Их часто используют в условных конструкциях — например, можно поставить рядом два выражения и выполнить какой-то блок кода, если одно из них возвращает true.

Всего в Java шесть логических операторов, но чаще всего используют эти четыре:

СимволЛогический операторЧто означает&&И (AND)Возвращает true, когда оба операнда true.||ИЛИ (OR)Возвращает true, когда хотя бы один операнд — true.^ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR)Возвращает true, когда один операнд true, а второй — false.!НЕ (NOT)Инвертирует true в false и наоборот. Это унарный оператор — он работает только с каким-то одним выражением.

В качестве примера сравним несколько выражений:

// AND — логическое умножение boolean a = (6 > 5) && (7 > 4); System.out.println(a); // Результат: true, так как оба выражения true // OR — логическое сложение boolean b = (6 > 5) || (7 > 4); System.out.println(b); // Результат: true, так как одно из выражений true // XOR — логическое вычитание boolean c = (6 > 8) ^ (6 > 7); System.out.println(c); // Результат: false, так как оба выражения — false // NOT — логическое отрицание boolean d = (6 > 5); System.out.println(!d); // Результат: false, так как изначальное выражение — true

Как это работает. Допустим, у нас есть конструкция (6 > 5) && (7 > 4). Когда мы начнём её выполнять, компилятор сначала проверит условия первого выражения, затем — второго. При этом оператор && устроен так, что если оба выражения истинны, то он и сам вернёт true. А это как раз наш случай, потому что и 6 больше 5, и 7 больше 4.

Что делает: сокращает условную конструкцию if-else до одной строчки.

Тернарный оператор (от латинского слова ternarius — «тройной») — это языковая конструкция, которая состоит из трёх операндов: логического условия и двух выражений. Если результат логического условия будет true, выполнится первое выражение, если false — второе. Записываются тернарные операторы так:

Скриншот: Лев Сергеев для Skillbox Media

Фишка в том, что таким образом мы можем записать конструкцию if-else всего одной строчкой. Допустим, нам нужно проверить булеву переменную condition — если она возвращает true, то переменная a = 100, а если false, то a = 200. Посмотрите, как легко это можно сделать с помощью тернарного оператора:

Слева — классическая конструкция if-else, справа — запись с помощью тернарного оператора
Скриншот: Лев Сергеев для Skillbox Media

Кажется, что эти if-else теперь вообще больше не нужны. Мол, ставь везде тернарные операторы, и дело с концом. Однако в больших программах со сложной логикой это может повредить читаемости кода. Это в нашем примере выражение простое — а представьте, если в каждое выражение положить ещё по одному оператору. Получится неопрятно. Другому разработчику, который будет читать наш код, разобраться будет сложно.

Поэтому тернарные операторы рекомендуют использовать в тех случаях, когда условие простое и легко проверяется. А во всех остальных случаях прибегать к привычным «ифам» и «элсам».

Что делает: проверяет, принадлежит ли объект к какому-то классу или интерфейсу, и возвращает булево значение — то есть true или false.

Использование instanceof актуально, когда нужно проверить родителя объекта прямо во время выполнения программы. Например, если объекты приходят в ваш код из базы данных или другого потока выполнения и вам нужно убедиться, что к ним можно применять методы какого-то класса. Записывается instanceof так:

Скриншот: Лев Сергеев для Skillbox Mediaimport java.io.Serializable; public class Main { public static void main(String[] args) { // Объявляем ссылку типа 'Object' и кладём объект типа 'String' Object object = new String(); // 'true', так как в 'object' лежит объект 'String' System.out.println(object instanceof String); // 'true', так как 'object' наследник 'Object' System.out.println(object instanceof Object); // 'false', так как 'object' не принадлежит классу 'Math' System.out.println(object instanceof Math); // 'true', так как в 'object' лежит объект 'String', // а 'String' реализует интерфейс 'Serializable' System.out.println(object instanceof Serializable); } }

Зачем нужны: чтобы писать алгоритмы, работающие с битами, — например, это полезно в криптографии и шифровании данных.

В Java существует семь побитовых операторов. Четыре из них отвечают за побитовые вычисления. Они похожи на логические операции, только вместо true и false вы имеете дело с нулями и единицами в двоичной системе счисления. Каждый разряд вычисляется поочерёдно — но в отличие от математики, когда складываются две единицы, результат не переносится на старший разряд и ответом будет 1. Это как если бы мы считали столбиком, а всё, что «в уме», — выкидывали.

Выглядит так:

Скриншот: Лев Сергеев для Skillbox Media

Побитовые операторы вычисления бывают следующими:

СимволЧто означает&Побитовое И (AND) — умножение|Побитовое ИЛИ (OR) — сложение^Побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR) — вычитание~Побитовое НЕ (NOT) — отрицание

Чтобы посмотреть, как работают побитовые вычисления в Java, переведём несколько десятичных чисел в двоичную систему счисления:

// Переводим числа от 1 до 8 в двоичную систему // 1 = 001 5 = 101 // 2 = 010 6 = 110 // 3 = 011 7 = 111 // 4 = 100 8 = 1000

Теперь попробуем выполнить с ними логические операции:

// 100 побитово умножить на 011 = 000 System.out.println(4 & 3); // Вывод: 0 // 010 побитово сложить с 011 = 011 System.out.println(2 | 3); // Вывод: 3 // из 010 побитово вычесть 001 = 011 System.out.println(2 ^ 1); // Вывод: 3 // 000 инвертировать в ... 111 ? System.out.println(~0); // Вывод: -1

В последнем примере мы видим, что при побитовом отрицании числа 0, почему-то получается -1. Тут есть два нюанса:

  • 0 в нашем примере имеет тип int и занимает в памяти 4 байта — то есть 32 бита.
  • Самый старший бит переменной (первый слева) является знаковым. Если он равен 0, то число будет положительным, а если 1 — отрицательным.
Скриншот: Лев Сергеев для Skillbox Media

Если разобраться в концепции старшего бита, можно без труда освоить три оставшихся побитовых оператора, которые называются операторами смещения битов:

СимволЧто означает<<Сдвиг битов влево>>Сдвиг битов вправо>>>Беззнаковый сдвиг битов вправо

Операторы сдвига смещают все биты в левую или правую сторону, но делают это по-разному:

  • >> не трогает старший бит, оставляя число с тем же знаком (отрицательным или положительным). Ячейки отрицательных чисел при >> заполняются единицами.
  • >>> и << — затрагивают все биты. Освободившиеся ячейки справа заполняются нулями. Наполнение ячеек слева зависит от знака и оператора.

Звучит сложно, на деле — тоже сложно. Но попробуем разобраться на примере — разложим какое-то десятичное число на биты и посмотрим, как работает смещение битов.

В Java есть метод toBinaryString(), который показывает, как выглядят десятичные числа в двоичной системе. Но двоичные числа — это ещё не биты. Например, число 2 занимает 32 бита, но если обработать его методом toBinaryString(), на экране появятся только два из них: 1 и 0. Оставшиеся 30 нулей просто «обрезаются». Это происходит по той же причине, по которой мы, например, не пишем десятичное число 9 как 009.

System.out.print(Integer.toBinaryString(2)); Вывод: 10

Поэтому, чтобы лучше разобраться в работе побитовых операторов, лучше использовать модифицированный метод BinaryString, который раскладывает на биты уже так, как надо:

// Метод добавляет к бинарному значению все недостающие биты и выводит их на экран public static void printBinaryString(int hexNumber){ String bits = Integer.toBinaryString(hexNumber); String allBits = "00000000000000000000000000000000" .substring(0, 32 - bits.length()) + bits; System.out.printf("%11d : %s\n", hexNumber, allBits); }

Сдвинув биты числа 1 два раза влево, мы получаем 4. А если потом сдвинуть их ещё 29 раз, получится минимальное значение типа int: –2 147 483 648.

printBinaryString(1); printBinaryString(1 << 2); // Смещаем биты числа '1' влево на 2 позиции printBinaryString(4 << 29); // Смещаем биты числа '4' влево на 29 позиций Вывод: 1 : 00000000000000000000000000000001 4 : 00000000000000000000000000000100 -2147483648 : 10000000000000000000000000000000

Теперь попробуем сдвинуть наши биты уже в обратную сторону. Обратите внимание, как различается работа >> и >>>. В первом случае всё заполнится единицами и вы получите минусовое значение, а во втором на выходе будут просто нули.

printBinaryString(Integer.MIN_VALUE); // Смещаем биты числа '-2147483648' вправо на 16 printBinaryString(Integer.MIN_VALUE >> 16); // Беззнаковое смещение битов числа '-2147483648' вправо на 16 printBinaryString(Integer.MIN_VALUE >>> 16); Вывод: -2147483648 : 10000000000000000000000000000000 -32768 : 11111111111111111000000000000000 32768 : 00000000000000001000000000000000

Как это работает. С помощью операторов >> и >>> мы сдвигаем все биты числа вправо. Но при обычном сдвиге (>>) отрицательное число остаётся отрицательным, потому что мы не трогаем старший бит. При беззнаковом сдвиге (>>>) наоборот — отрицательное станет положительным, потому что мы затронем эту значимую единицу.

Зачем нужны: чтобы записывать выражения короче и автоматически приводить операнды к единому типу.

В Java есть сокращённые формы операторов присваивания — составные. Такие операторы выполняют действие между x и y, а получившееся значение помещают в x. Выглядят составные операторы так:

Скриншот: Лев Сергеев для Skillbox Media

Плюс составных операторов в том, что они записываются короче и неявно приводят переменные к одному типу, если эти типы различаются. Например, в сокращённом варианте можно сложить дроби и числа без приведения, и нам за это ничего не будет. А в полной записи будет ошибка:

int x = 1; double y = 3.1415d; x = x + y; // Эта строка не скомпилируется x += y; // А здесь всё хорошо // Оператор += в развёрнутом виде x = (int)(x + y);

У каждого оператора Java есть свой приоритет. Чем он выше, тем раньше оператор выполнится в выражении. Бинарные и тернарный операторы (кроме присваивания) выполняются слева направо, а остальные (унарные и присваивания) — справа налево.

Приоритет
(снизу вверх)ГруппаОператоры13Постфиксныеx++ x––12Унарные++x ––x +x –x ~x !x11Мультипликативные* / %10Аддитивные+ -9Сдвига битов<< >> >>>8Сравнения< > <= >= instanceof7Равенства== !=6Побитовое И&5Побитовое исключающее ИЛИ^4Побитовое ИЛИ|3Логическое И&&2Логическое ИЛИ||1Тернарный? :0Присваивания= += -= *= /= %=
&= ^= |=
<<= >>= >>>=

Мы рассмотрели все основные операторы в Java и их работу на примерах. Попробуем кратко резюмировать, что нужно вынести из этой статьи:

  • Оператор — это языковая конструкция, которая выполняет действие над операндом.
  • Операнд — это число, переменная, объект и так далее, с которыми оператор совершает какие-то действия (например, арифметическое и логическое).
  • Операторы бывают унарные, бинарные и тернарные — это зависит от того, сколько операндов они обрабатывают.
  • Арифметические операторы нужны для простых математических действий: сложения, вычитания, умножения, деления и деления с остатком.
  • Унарные операторы работают только с одним операндом. Это унарные минус и плюс, инкремент, декремент, логическое и побитовое отрицание.
  • Операторы сравнения сопоставляют значения двух операторов и возвращают ответ — true или false.
  • Логические операторы заточены уже не на числа, а на целые выражения — и на их основе создают сложные условия, например, для работы в циклах.
  • Тернарный оператор умеет работать сразу с тремя операндами: условием и двумя выражениями. То есть им вполне можно заменить ветвления типа if-else.
  • Оператор instanceof определяет принадлежность объекта к классу.
  • Побитовые операторы нужны для проведения операций с битами — если упростить, то с нулями и единицами в двоичной системе счисления.
  • Составные операторы неявно приводят типы данных, если они разные.
  • У каждого оператора есть свой приоритет.

Для более глубокого понимания темы можно почитать другие наши статьи: «Тип Boolean и операторы сравнения в Java» и «Логические операторы в Java». А если хотите совсем хорошо разобраться, почитайте официальную документацию Java — это вообще лучший способ освоить язык со всеми его тонкостями.

Научитесь: Профессия Java-разработчик + ИИ Узнать больше