Код
#статьи

Операторы в Java: для чего нужны и какие бывают

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

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

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

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

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

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

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

Оператор присваивания в Java

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

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

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

int x = 1;    // Объявляем числовую переменную 'x' со значением 1

boolean a, b; // Объявляем логические переменные 'a' и 'b'
a = b = true;

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

Арифметические операторы в Java

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

Плюс: +

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

Ещё этот оператор применяется при соединении двух строк в одну. Например, в 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

Эти операторы в 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 есть ещё два унарных оператора: ! — логическое отрицание, и ~ — побитовое отрицание, но их мы рассмотрим чуть позже, когда будем разбираться с логическими и побитовыми операторами.

Операторы сравнения в 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

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

Логические операторы в Java: Boolean

Что делают: комбинируют логические значения типа 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.

Тернарный оператор в Java: можно, только осторожно

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

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

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

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

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

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

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

Подробно о том, как работать с тернарными операторами, мы рассказывали в статье: «Тип Boolean и операторы сравнения в Java».

Оператор instanceof в Java

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

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

Скриншот: Лев Сергеев для Skillbox Media
import 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 и смещение битов

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

В 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

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

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

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

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

int x = 1;
double y = 3.1415d;

x = x + y;        // Эта строка не скомпилируется
x += y;           // А здесь всё хорошо

// Оператор += в развёрнутом виде
x = (int)(x + y); 

Приоритеты операторов 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 — это вообще лучший способ освоить язык со всеми его тонкостями.

Жизнь можно сделать лучше!
Освойте востребованную профессию, зарабатывайте больше и получайте от работы удовольствие.
Каталог возможностей
Понравилась статья?
Да

Пользуясь нашим сайтом, вы соглашаетесь с тем, что мы используем cookies 🍪

Ссылка скопирована