Операторы в Java: для чего нужны и какие бывают
Знакомимся с основными инструментами языка и учимся работать с ними на практике.
Иллюстрация: Оля Ежак для Skillbox Media
Все мы со школы знакомы с математическими знаками + (плюс), – (минус), = (равно) и так далее. В Java и других языках программирования эти символы называются операторами. Они нужны для того, чтобы сообщить компилятору, какое действие совершать с операндами — числами, строками, объектами и другими значениями по обе стороны оператора.
Например, на картинке выше оператор + говорит компилятору: «Сложи два числа». Но на практике чисел в выражении может быть разное количество — и одно, и два, и три. В этих случаях и операторы будут называться по-разному: унарные — что-то делают с одним операндом, бинарные — с двумя, и тернарные — с тремя.
В этой статье рассмотрим не только эти, но все основные виды операторов в Java:
- Присваивания
- Арифметические
- Унарные
- Сравнения
- Логические
- Тернарный
- Оператор instanceof
- Побитовые
- Составные операторы присваивания
Ещё поговорим о приоритетах операторов — разберёмся, в каком порядке они выполняются. Если вы хотите просто освежить в памяти суть работы разных операторов, можете сразу перейти к шпаргалке в конце статьи.
Оператор присваивания в Java
Оператор присваивания в Java — это знак = (равно). Его задача — переложить значение переменной справа в переменную слева:
Вот как это выглядит в коде (в комментариях — разбор синтаксиса):
Что происходит: значение true присваивается переменной b. В свою очередь, значение переменной b присваивается переменной a.
Арифметические операторы в Java
Эти операторы выполняют простые арифметические действия: сложение, вычитание, умножение, деление и деление с остатком. Все арифметические операторы являются бинарными — то есть работают только с двумя значениями.
Плюс: +
Что делает: складывает два операнда.
Ещё этот оператор применяется при соединении двух строк в одну. Например, в Java можно выполнить выражение «Я люблю» + «Москву» и получить «Я люблю Москву». Такая операция в программировании называется конкатенацией.
В Java типы данных char, byte и short при вычислениях неявно приводятся к типу int (целое число), поэтому с ними можно работать как с обычными числами. При этом тип данных char в Java отвечает за символы Unicode — каждый символ обозначает какое-то число. Например, символ a равен числу 97, а b — 98, поэтому в нашем примере и получилось значение 195.
Минус: −
Что делает: вычитает из левого операнда правый.
Умножение: *
Что делает: возвращает произведение операндов.
Деление: /
Что делает: делит левый операнд на правый.
А вот что будет, если попробовать поделить на ноль в Java:
Делить число на ноль нельзя! Программа завершится, выбросив исключение. Но если привести выражение к типу 'double' (число с плавающей запятой), то вы получите «бесконечность»:
Деление с остатком (деление по модулю): %
Что делает: возвращает остаток от деления.
Унарные операторы в Java
Эти операторы в Java тоже можно отнести к арифметическим, но есть нюанс — они работают только с одним операндом. Поэтому их и называют унарными.
Унарные плюс и минус: + и −
Что делают: меняют значение числа на положительное или отрицательное.
Инкремент и декремент: ++ и −−
Что делают: инкремент — увеличивает значение переменной на единицу, а декремент — уменьшает.
В свою очередь, у декремента и инкремента есть две формы: префиксная и постфиксная. Звучит сложно, но на деле всё просто:
- Префиксные операторы (++x) сразу меняют значение переменной и подставляют его в выражение.
- Постфиксные операторы (x++) делают наоборот — сначала используют старое значение переменной и только потом подставляют новое.
Инкременты и декременты часто используют в циклах в качестве счётчика, когда нужно по очереди вывести все числа в каком-то диапазоне:
Подробнее о декрементах и инкрементах можно почитать в нашей статье — рассказываем, как решать сложные выражения с этими операторами.
В Java есть ещё два унарных оператора: ! — логическое отрицание, и ~ — побитовое отрицание, но их мы рассмотрим чуть позже, когда будем разбираться с логическими и побитовыми операторами.
Операторы сравнения в Java
Что делают: сравнивают два операнда и выясняют отношения между ними — что больше, что меньше, что чему равняется. При вычислении такие операторы возвращают значение типа boolean:
- true (правда);
- false (ложь).
Всего в Java шесть операторов сравнения:
Оператор | Что означает |
---|---|
== | Равно |
> | Больше, чем |
< | Меньше, чем |
>= | Больше или равно |
<= | Меньше или равно |
!= | Не равно |
Давайте посмотрим, как выглядят операторы сравнения в коде — попробуем сравнить два целых числа и вывести результаты на экран:
Ещё операторы сравнения часто используют в условных конструкциях. Это когда, в зависимости от условий, выполняется какой-то один блок кода. В этом случае, помимо операторов сравнения, нам понадобятся операторы ветвления: if-else, switch и так далее. Например, здесь мы просим Java напечатать слово true, если 1 не равно 0:
Подробнее об операторах сравнения и условных конструкциях можно почитать в этой статье.
Логические операторы в Java: Boolean
Что делают: комбинируют логические значения типа true и false.
В отличие от операторов сравнения, логические операторы работают не с отдельными числами, а с результатами выражений. Их часто используют в условных конструкциях — например, можно поставить рядом два выражения и выполнить какой-то блок кода, если одно из них возвращает true.
Всего в Java шесть логических операторов, но чаще всего используют эти четыре:
Символ | Логический оператор | Что означает |
---|---|---|
&& | И (AND) | Возвращает true, когда оба операнда true. |
|| | ИЛИ (OR) | Возвращает true, когда хотя бы один операнд — true. |
^ | ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR) | Возвращает true, когда один операнд true, а второй — false. |
! | НЕ (NOT) | Инвертирует true в false и наоборот. Это унарный оператор — он работает только с каким-то одним выражением. |
В качестве примера сравним несколько выражений:
Как это работает. Допустим, у нас есть конструкция (6 > 5) && (7 > 4). Когда мы начнём её выполнять, компилятор сначала проверит условия первого выражения, затем — второго. При этом оператор && устроен так, что если оба выражения истинны, то он и сам вернёт true. А это как раз наш случай, потому что и 6 больше 5, и 7 больше 4.
Читайте также:
Тернарный оператор в Java: можно, только осторожно
Что делает: сокращает условную конструкцию if-else до одной строчки.
Тернарный оператор (от латинского слова ternarius — «тройной») — это языковая конструкция, которая состоит из трёх операндов: логического условия и двух выражений. Если результат логического условия будет true, выполнится первое выражение, если false — второе. Записываются тернарные операторы так:
Фишка в том, что таким образом мы можем записать конструкцию if-else всего одной строчкой. Допустим, нам нужно проверить булеву переменную condition — если она возвращает true, то переменная a = 100, а если false, то a = 200. Посмотрите, как легко это можно сделать с помощью тернарного оператора:
Кажется, что эти if-else теперь вообще больше не нужны. Мол, ставь везде тернарные операторы, и дело с концом. Однако в больших программах со сложной логикой это может повредить читаемости кода. Это в нашем примере выражение простое — а представьте, если в каждое выражение положить ещё по одному оператору. Получится неопрятно. Другому разработчику, который будет читать наш код, разобраться будет сложно.
Поэтому тернарные операторы рекомендуют использовать в тех случаях, когда условие простое и легко проверяется. А во всех остальных случаях прибегать к привычным «ифам» и «элсам».
Подробно о том, как работать с тернарными операторами, мы рассказывали в статье: «Тип Boolean и операторы сравнения в Java».
Оператор instanceof в Java
Что делает: проверяет, принадлежит ли объект к какому-то классу или интерфейсу, и возвращает булево значение — то есть true или false.
Использование instanceof актуально, когда нужно проверить родителя объекта прямо во время выполнения программы. Например, если объекты приходят в ваш код из базы данных или другого потока выполнения и вам нужно убедиться, что к ним можно применять методы какого-то класса. Записывается instanceof так:
Побитовые операторы в Java и смещение битов
Зачем нужны: чтобы писать алгоритмы, работающие с битами, — например, это полезно в криптографии и шифровании данных.
В Java существует семь побитовых операторов. Четыре из них отвечают за побитовые вычисления. Они похожи на логические операции, только вместо true и false вы имеете дело с нулями и единицами в двоичной системе счисления. Каждый разряд вычисляется поочерёдно — но в отличие от математики, когда складываются две единицы, результат не переносится на старший разряд и ответом будет 1. Это как если бы мы считали столбиком, а всё, что «в уме», — выкидывали.
Выглядит так:
Побитовые операторы вычисления бывают следующими:
Символ | Что означает |
---|---|
& | Побитовое И (AND) — умножение |
| | Побитовое ИЛИ (OR) — сложение |
^ | Побитовое ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR) — вычитание |
~ | Побитовое НЕ (NOT) — отрицание |
Чтобы посмотреть, как работают побитовые вычисления в Java, переведём несколько десятичных чисел в двоичную систему счисления:
Теперь попробуем выполнить с ними логические операции:
В последнем примере мы видим, что при побитовом отрицании числа 0, почему-то получается -1. Тут есть два нюанса:
- 0 в нашем примере имеет тип int и занимает в памяти 4 байта — то есть 32 бита.
- Самый старший бит переменной (первый слева) является знаковым. Если он равен 0, то число будет положительным, а если 1 — отрицательным.
Если разобраться в концепции старшего бита, можно без труда освоить три оставшихся побитовых оператора, которые называются операторами смещения битов:
Символ | Что означает |
---|---|
<< | Сдвиг битов влево |
>> | Сдвиг битов вправо |
>>> | Беззнаковый сдвиг битов вправо |
Операторы сдвига смещают все биты в левую или правую сторону, но делают это по-разному:
- >> не трогает старший бит, оставляя число с тем же знаком (отрицательным или положительным). Ячейки отрицательных чисел при >> заполняются единицами.
- >>> и << — затрагивают все биты. Освободившиеся ячейки справа заполняются нулями. Наполнение ячеек слева зависит от знака и оператора.
Звучит сложно, на деле — тоже сложно. Но попробуем разобраться на примере — разложим какое-то десятичное число на биты и посмотрим, как работает смещение битов.
В Java есть метод toBinaryString(), который показывает, как выглядят десятичные числа в двоичной системе. Но двоичные числа — это ещё не биты. Например, число 2 занимает 32 бита, но если обработать его методом toBinaryString(), на экране появятся только два из них: 1 и 0. Оставшиеся 30 нулей просто «обрезаются». Это происходит по той же причине, по которой мы, например, не пишем десятичное число 9 как 009.
Поэтому, чтобы лучше разобраться в работе побитовых операторов, лучше использовать модифицированный метод BinaryString, который раскладывает на биты уже так, как надо:
Сдвинув биты числа 1 два раза влево, мы получаем 4. А если потом сдвинуть их ещё 29 раз, получится минимальное значение типа int: –2 147 483 648.
Теперь попробуем сдвинуть наши биты уже в обратную сторону. Обратите внимание, как различается работа >> и >>>. В первом случае всё заполнится единицами и вы получите минусовое значение, а во втором на выходе будут просто нули.
Как это работает. С помощью операторов >> и >>> мы сдвигаем все биты числа вправо. Но при обычном сдвиге (>>) отрицательное число остаётся отрицательным, потому что мы не трогаем старший бит. При беззнаковом сдвиге (>>>) наоборот — отрицательное станет положительным, потому что мы затронем эту значимую единицу.
Составные операторы присваивания в Java
Зачем нужны: чтобы записывать выражения короче и автоматически приводить операнды к единому типу.
В Java есть сокращённые формы операторов присваивания — составные. Такие операторы выполняют действие между x и y, а получившееся значение помещают в x. Выглядят составные операторы так:
Плюс составных операторов в том, что они записываются короче и неявно приводят переменные к одному типу, если эти типы различаются. Например, в сокращённом варианте можно сложить дроби и числа без приведения, и нам за это ничего не будет. А в полной записи будет ошибка:
Приоритеты операторов Java
У каждого оператора Java есть свой приоритет. Чем он выше, тем раньше оператор выполнится в выражении. Бинарные и тернарный операторы (кроме присваивания) выполняются слева направо, а остальные (унарные и присваивания) — справа налево.
Приоритет (снизу вверх) | Группа | Операторы |
---|---|---|
13 | Постфиксные | x++ x–– |
12 | Унарные | ++x ––x +x –x ~x !x |
11 | Мультипликативные | * / % |
10 | Аддитивные | + - |
9 | Сдвига битов | << >> >>> |
8 | Сравнения | < > <= >= instanceof |
7 | Равенства | == != |
6 | Побитовое И | & |
5 | Побитовое исключающее ИЛИ | ^ |
4 | Побитовое ИЛИ | | |
3 | Логическое И | && |
2 | Логическое ИЛИ | || |
1 | Тернарный | ? : |
0 | Присваивания | = += -= *= /= %= &= ^= |= <<= >>= >>>= |
Резюме: что нужно запомнить
Мы рассмотрели все основные операторы в Java и их работу на примерах. Попробуем кратко резюмировать, что нужно вынести из этой статьи:
- Оператор — это языковая конструкция, которая выполняет действие над операндом.
- Операнд — это число, переменная, объект и так далее, с которыми оператор совершает какие-то действия (например, арифметическое и логическое).
- Операторы бывают унарные, бинарные и тернарные — это зависит от того, сколько операндов они обрабатывают.
- Арифметические операторы нужны для простых математических действий: сложения, вычитания, умножения, деления и деления с остатком.
- Унарные операторы работают только с одним операндом. Это унарные минус и плюс, инкремент, декремент, логическое и побитовое отрицание.
- Операторы сравнения сопоставляют значения двух операторов и возвращают ответ — true или false.
- Логические операторы заточены уже не на числа, а на целые выражения — и на их основе создают сложные условия, например, для работы в циклах.
- Тернарный оператор умеет работать сразу с тремя операндами: условием и двумя выражениями. То есть им вполне можно заменить ветвления типа if-else.
- Оператор instanceof определяет принадлежность объекта к классу.
- Побитовые операторы нужны для проведения операций с битами — если упростить, то с нулями и единицами в двоичной системе счисления.
- Составные операторы неявно приводят типы данных, если они разные.
- У каждого оператора есть свой приоритет.
Для более глубокого понимания темы можно почитать другие наши статьи: «Тип Boolean и операторы сравнения в Java» и «Логические операторы в Java». А если хотите совсем хорошо разобраться, почитайте официальную документацию Java — это вообще лучший способ освоить язык со всеми его тонкостями.