sonyps4.ru

Битовые операции java. Основные операторы Java

Как известно, в Java нет беззнаковых типов. Если в Си вы могли написать unsigned int (char , long), то в Java так не получится. Однако нередко возникает необходимость в выполнении арифметических операций именно с числами без знака. На первый взгляд кажется, что беззнаковые типы в принципе-то и не особо нужны (подумаешь, MaxInt для чисел со знаком меньше в два раза, если нужны числа больше, я просто возьму long и далее BigInteger). Но основное различие на самом деле не в том, сколько различных неотрицательных чисел можно положить в signed или unsigned int, а в том, как над ними производятся арифметические операции и сравнения. Если вы работаете с бинарными протоколами или с двоичной арифметикой, где важен каждый используемый бит, нужно уметь выполнять все основные операции в беззнаковом режиме. Рассмотрим эти операции по порядку:

Преобразование byte в short (int, long)

Обычный каст (int) myByte выполнит расширение до 32 бит со знаком - это означает, что если старший бит байта был установлен в 1, то результатом будет то же самое отрицательное число, но записанное в 32-битном формате:

0xff -> 0xffffffff (-1)

Часто это не то, чего бы мы хотели. Для того, чтобы выполнить расширение до 32 бит без знака и получить 0x000000ff , в Java можно записать:

Int myInt = myByte & 0xff; short myShort = myByte & 0xff;

Сравнение без учёта знака

Для беззнакового сравнения есть лаконичная формула:

Int compareUnsigned(int a, int b) { return Integer.compare(a ^ 0x80000000, b ^ 0x80000000); }
Для byte, short и long, соответственно, константы будут 0x80 , 0x8000 и 0x8000000000000000L .

Сложение, вычитание и умножение

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

Деление

Деление -256 на 256 даст нам -1. А нам бы хотелось, чтобы 0xffffff00 / 0x100 давало 0x00ffffff , а не 0xffffffff (-1) . Для byte , short и int решением будет переход к числам большей разрядности:

Int a = 0xffffff00; int b = 0x100; int c = (int) ((a & 0xffffffffL) / b); // convert a to long before division
Но что делать с long ? Переходить на BigInteger в таких случаях обычно не вариант - слишком медленно. Остаётся только брать всё в свои руки и реализовывать деление вручную. К счастью, всё уже украдено до нас - в Google Guava есть реализация беззнакового деления для long , причём довольно шустрая. Если вы не используете эту библиотеку, проще всего выдрать кусок кода прямо из файла :

/** * Returns dividend / divisor, where the dividend and divisor are treated as unsigned 64-bit * quantities. * * @param dividend the dividend (numerator) * @param divisor the divisor (denominator) * @throws ArithmeticException if divisor is 0 */ public static long divide(long dividend, long divisor) { if (divisor < 0) { // i.e., divisor >= 2^63: if (compare(dividend, divisor) < 0) { return 0; // dividend < divisor } else { return 1; // dividend >= divisor } } // Optimization - use signed division if dividend < 2^63 if (dividend >= 0) { return dividend / divisor; } /* * Otherwise, approximate the quotient, check, and correct if necessary. Our approximation is * guaranteed to be either exact or one less than the correct value. This follows from fact * that floor(floor(x)/i) == floor(x/i) for any real x and integer i != 0. The proof is not * quite trivial. */ long quotient = ((dividend >>> 1) / divisor) << 1; long rem = dividend - quotient * divisor; return quotient + (compare(rem, divisor) >= 0 ? 1: 0); }
Чтобы код компилировался, придётся также позаимствовать реализацию compare(long, long) :

/** * Compares the two specified {@code long} values, treating them as unsigned values between * {@code 0} and {@code 2^64 - 1} inclusive. * * @param a the first unsigned {@code long} to compare * @param b the second unsigned {@code long} to compare * @return a negative value if {@code a} is less than {@code b}; a positive value if {@code a} is * greater than {@code b}; or zero if they are equal */ public static int compare(long a, long b) { return Longs.compare(flip(a), flip(b)); }
и Longs.compare(long, long) + flip(long) :

/** * A (self-inverse) bijection which converts the ordering on unsigned longs to the ordering on * longs, that is, {@code a <= b} as unsigned longs if and only if {@code flip(a) <= flip(b)} * as signed longs. */ private static long flip(long a) { return a ^ Long.MIN_VALUE; } /** * Compares the two specified {@code long} values. The sign of the value * returned is the same as that of {@code ((Long) a).compareTo(b)}. * * @param a the first {@code long} to compare * @param b the second {@code long} to compare * @return a negative value if {@code a} is less than {@code b}; a positive * value if {@code a} is greater than {@code b}; or zero if they are equal */ public static int compare(long a, long b) { return (a < b) ? -1: ((a > b) ? 1: 0); }

Побитовые сдвиги

Чтобы окончательно покрыть тему о битовых операциях, вспомним также о сдвигах. В x86 ассемблере есть целая пачка различных команд, которые делают побитовые сдвиги - SHL, SHR, SAL, SAR, ROR, ROL, RCR, RCL. Последние 4 осуществляют циклические сдвиги, их эквивалентов в Java нет. А вот логические и арифметические сдвиги присутствуют. Логический сдвиг (не учитывает знака) - SHL (shift left) и SHR (shift right) - реализуется в Java операторами << и >>> соответственно. С помощью логических сдвигов можно быстро выполнять целочисленные умножение и деление на числа степени двойки. Арифметический сдвиг (учитывает знак) вправо - SAR - реализуется оператором >> . Арифметический сдвиг влево эквивалентен логическому, и поэтому специального оператора для него нет. Может показаться странным, что в ассемблере есть специальный опкод для этой операции, но на самом деле он делает то же самое, то есть SAL полностью повторяет поведение SHL, и об этом прямо говорит документация от Intel:
The shift arithmetic left (SAL) and shift logical left (SHL) instructions perform the same operation; they shift the bits in the destination operand to the left (toward more significant bit locations). For each shift count, the most significant bit of the destination operand is shifted into the CF flag, and the least significant bit is cleared (see Figure 7-7 in the Intel®64 and IA-32 Architectures Software Developer"sManual, Volume 1).

То есть SAL добавили просто для симметрии, с учётом того, что для сдвига вправо есть разделение на логический и арифметический. Ну а Гослинг решил не заморачиваться (и, думается, правильно сделал).

Итак, мы имеем следующее:

A << 1; // беззнаковый сдвиг влево, эквивалентно умножению на 2 a >> 1; // сдвиг вправо с учётом знака (эквивалентно делению на 2) a >>> 1; // сдвиг вправо без учёта знака (эквивалентно беззнаковому делению на 2)

  • При выполнении арифметических действий, которые могут привести к переполнению в выбранной разрядной сетке, нужно всегда точно представлять, какая область допустимых значений может быть у переменных, и отслеживать эти инварианты, расставляя утверждения (assertions). Например, очевидно, что при умножении двух произвольных 32-разрядных беззнаковых чисел результат может не поместиться в 32 бита, и если вам нужно избежать переполнения, нужно либо убедиться, что в этом месте никогда не будет ситуации, при которой произведение не влезает в 32 бита, либо необходимо предварительно сконвертировать оба операнда в long (выполнив a & 0xffffffffL). Здесь, кстати, можно легко допустить ошибку, сконвертировав только один из операндов. Нет, нужно сконвертировать в long оба, т.к. если второй операнд окажется отрицательным, он будет неявно преобразован в long с расширением знака, и результат умножения будет неправильным.
  • Щедро расставляйте скобки в выражениях, где используются побитовые операции. Дело в том, что приоритет побитовых операторов в Java несколько странный, и часто ведёт себя неочевидным образом. Лучше добавить пару скобок, чем потом несколько часов искать трудноуловимые ошибки.
  • Если вам нужна константа типа long , не забудьте добавить суффикс L в конец литерала константы. Если этого не сделать, это будет не long , а int , и при неявном приведении к long снова произойдёт неприятное нам расширение со знаком.

Сдвиг вправо без учета знака

Как было показано, при каждом выполнении операция » автоматически заполняет старший бит его предыдущим содержимым. В результате знак значения сохраняется. Однако иногда это нежелательно. Например, при выполнении сдвига вправо в каком-либо значении, которое не является числовым, использование дополнительных знаковых разрядов может быть нежелательным. Эта ситуация часто встречается при работе со значениями пикселей и графическими изображениями. Как правило, в этих случаях требуется сдвиг нуля в позицию старшего бита независимо от его первоначального значения. Такое действие называют сдвигом вправо без учета знака. Для его выполнения используют операцию сдвига вправо без учета знака Java, >>>, которая всегда вставляет ноль в позицию старшего бита.

Следующий фрагмент кода демонстрирует применение операции >>>. В этом примере значение переменной а установлено равным -1, все 32 бита двоичного представления которого равны 1. Затем в этом значении выполняется сдвиг вправо на 24 бита с заполнением старших 24 битов нулями и игнорированием обычно используемых дополнительных знаковых разрядов. В результате значение а становится равным 255.

int а = -1;
а = а >>> 24;

Часто операция >>> не столь полезна, как хотелось бы, поскольку она имеет смысл только для 32- и 64-разрядных значений. Помните, что в выражениях тип меньших значений автоматически повышается до int. Это означает применение дополнительных знаковых разрядов и выполнение сдвига по отношению к 32-разрядным, а не 8- или 16-разрядным значениям. То есть программист может подразумевать выполнение сдвига вправо без учета знака применительно к значению типа byte и заполнение нулями, начиная с бита 7.

Однако в действительности это не так, поскольку фактически сдвиг будет выполняться в 32-разрядном значении. Этот эффект демонстрирует следующая программа.

// Сдвиг без учета знака значения типа byte.
class ByteUShift {
static public void main(String args) {
char hex = {
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
byte b = (byte) 0xfl;
byte с = (byte) (b » 4);
byte d = (byte) (b >» 4) ;
byte e = (byte) ((b & 0xff) » 4) ;
System.out.println (" b = 0x" + hex [ (b » 4) & 0x0f] + hex ) ;
System, out .println (" b » 4 = 0x" + hex[ (c » 4) & 0x0f] + hex) ;
System, out .println (" b »> 4 = 0x" + hex[ (d » 4) & 0x0f] + hex) ;
System.out.println(" (b & 0xff) » 4 = 0x" + hex[(e » 4) S 0x0f] + hex) ;
}
}

Из следующего вывода этой программы видно, что операция »> не выполняет никаких действий по отношению к значениям типа byte. Для этого примера в качестве значения переменной b было выбрано произвольное отрицательное значение типа byte. Затем переменной с присваивается значение переменной b типа byte, сдвинутое вправо на четыре позиции:, которое в связи с применением дополнительных знаковых разрядов равно Oxff. Затем переменной d присваивается значение переменной b типа byte, сдвинутое вправо на четыре позиции без учета знака, которым должно было бы быть значение OxOf, но в действительности, из-за применения дополнительных знаковых разрядов во время повышения типа b до int перед выполнением сдвига, равное Oxff. Последнее выражение устанавливает значение переменной е равным значению типа byte переменной Ь, замаскированному до 8 бит с помощью операции AND и затем сдвинутому вправо на четыре позиции, что дает ожидаемое значение, равное OxOf. Обратите внимание, что операция сдвига вправо без учета знака не применялась к переменной d, поскольку состояние знакового бита после выполнения операции AND было известно.

Побитовые составные операции с присваиванием

Подобно алгебраическим операциям, все двоичные побитовые операции имеют составную форму, которая объединяет побитовую операцию с операцией присваивания. Например, следующие два оператора, выполняющие сдвиг вправо на четыре позиции в значении переменной а, эквивалентны:

Аналогично, эквивалентны и следующие два оператора, которые присваивают переменной а результат выполнения побитовой операции a OR b:

а = а | b;
а |= b;

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

class OpBitEquals public static void main(String args){ int a = 1;
int b = 2;
int c = 3;
a |= 4;
b >= 1;
c a ^= c;
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
}
}

Эта программа создает следующий вывод.

Конспект лекций по Java. Занятие 4

(none) (none) ::
(none)
( В.Фесюнов )

Операции (operators) в языке Java

Большинство операций Java просты и интуитивно понятны. Это такие операции, как +, -, *, /, <,> и др. Операции имеют свой порядок выполнения и приоритеты. Так в выражении

сначала выполняется умножение, а потом сложение, поскольку приоритет у операции умножения выше, чем у операции сложения. В выражении

сначала вычисляется a + b, а потом от результата вычитается c, поскольку порядок выполнения этих операций слева направо.

Но операции Java имеют и свои особенности. Не вдаваясь в детальное описание простейших операций, остановимся на особенностях.

Начнем с присваивания . В отличии от ряда других языков программирования в Java присваивание — это не оператор, а операция. Семантику этой операции можно описать так.

  • Операция присваивания обозначается символом "=". Она вычисляет значение своего правого операнда и присваивает его левому операнду, а также выдает в качестве результата присвоенное значение. Это значение может быть использовано другими операциями. Последовательность из нескольких операций присваивания выполняется справа налево.

В простейшем случае все выглядит как обычно.

Здесь происходит именно то, что мы интуитивно подразумеваем, — вычисляется сумма a и b, результат заносится в x. Но вот два других примера.

В первом сначала 1 заносится в b, результатом операции является 1, потом этот результат заносится в a. Во втором примере вычисляется сумма a и b и результат теряется. Это бессмысленно, но синтаксически допустимо.

Операции сравнения

Это операции >, <, >=, <=, != и ==. Следует обратить внимание, что сравнение на равенство обозначается двумя знаками "=". Операндами этих операций могут быть арифметические данные, результат — типа boolean.

Операции инкремента, декремента

Это операции ++ и --. Так y++ (инкремент) является сокращенной записью y = y +1, аналогично и с операцией декремента (--).

Но с этими операциями есть одна тонкость. Они существуют в двух формах - префиксной (++y) и постфиксной (y++). Действие этих операций одно и то же — они увеличивают (операции декремента — уменьшают) свой операнд на 1, а вот результат у них разный. Префиксная форма в качестве результата выдает уже измененное на 1 значение операнда, а постфиксна -значение операнда до изменения.

A = 5; x = a++; y = ++a;

В этом фрагменте x получит значение 5, а y — 7.

Операция целочисленного деления

Нужно учитывать, что деление одного целого на другое выдает целое, причем не округляет, а отбрасывает дробную часть.

Остаток от деления (значение по модулю)

В Java имеется операция %, которая обозначает остаток от деления.

Расширенные операции присваивания

Кроме обычной операции "=" в Java существуют операции +=, -=, *=, /= и др. Это сокращенные записи. Так a += b полностью эквивалентна a = a + b. Аналогично и с другими такими операциями.

Логические операции

! — отрицание && — логическое "и" || — логическое "или"

Операнды этих операций должны быть типа boolean, результат — boolean. Операции && и || имеют одну особенность — их правый операнд может и не вычислиться, если результат уже известен по левому операнду. Так, если левый операнд операции && — ложь (false), то правый операнд вычисляться не будет, т.к. результат все равно — ложь.

Это свойство нужно учитывать, особенно тогда, когда правый операнд содержит вызов некоторой функции.

Побитовые логические операции

Это операции

& — побитовое "и" | — побитовое "или" ^ — побитовое "исключающее или" ~ — побитовое отрицание

Они выполняются для каждой пары битов своих операндов.

Операции сдвига

<< — сдвиг влево >> — сдвиг вправо >>> — беззнаковый сдвиг вправо

Эти операции сдвигают значение своего левого операнда на число бит, заданное правым операндом.

Условная операци

Это единственная тернарная операция, т.е. операция, имеющая три операнда. Соответственно, для нее используется не один знак операции, а два.

<условие> ? <выражение1> : < выражение2>

Если <условие> истинно, то результатом будет < выражение1>, иначе < выражение2>.

Например, "a < b ? a: b" вычисляет минимум из a и b.

Операция приведения типов

Это очень важная операция. По умолчанию все преобразования, которые могут привести к проблемам, в Java запрещены. Так, нельзя long-значение присвоить int-операнду. В тех случаях, когда это все же необходимо, нужно поставить явное преобразование типа.

Например, пусть метод f(...) выдает long.

int x = (int)f(10);

Здесь (int) — это операция преобразования типа. Операция преобразования типа обозначается при помощи имени типа, взятого в скобки.

Эта операция применима не только к базовым типам, но и к классам. Мы разберем это подробнее, когда будем рассматривать наследование.

Литералы (константы)

Арифметические

Примеры арифметических констант

10 - 010 — это 8 - 0123 — это 83 (1*64 + 2*8 + 3) - 0x10 — это 16 - 0x123 — это 291 (1*256 + 2*16 +3) - 1e5 — это 100000 - 1.23e-3 — это 0.00123

Для указания типа константы применяются суффиксы: l (или L) — long, f (или F) — float, d (или D) — double. Например,

1L — единица, но типа long.

Логические литералы

Логические литералы — это true (истина) и false (ложь)

Строковые литералы

Записываются в двойных кавычках, например

"это строка"

Символьные литералы

Записываются в апострофах, например "F", "ш".

В строковых и символьных литералах есть правила для записи спец. символов. Во-первых, есть набор предопределенных спец. символов. Это

- "\n" — конец строки (перевод строки) - "\r" — возврат каретки - "\t" — табуляция

и ряд других.

Во-вторых, можно явно записать код символа (нужно только знать его). Запись кода обычно выполняется в восьмеричной системе: "\001" — символ с кодом 1 и т.п.

Операторы (statements)

Оператор — выражение

Синтаксис

<выражение>;

Такой оператор состоит из одного выражения, в конце стоит ";". Его действие состоит в вычислении выражения, значение, вычисленное данным выражением, теряется. Т.е. обычно такое выражение содержит операцию присваивания, хотя это по синтаксису не обязательно.

A = 0; x = (a > b ? a: b); cnt++;

Условный оператор (if)

Синтаксис

If (<условие>) <оператор1>

Здесь <условие> — это логическое выражение, т.е. выражение, возвращающее true или false. Как видно из синтаксиса, часть else вляется необязательной. После if и после else стоит по одному оператору. Если нужно поместить туда несколько операторов, то нужно поставить блок . Блок начинается с "{" и заканчивается "}".

В Java принято блок ставить всегда, даже если после if или else стоит один оператор.

If (a > b) { x = a; } else { x = b; } if (flag) { flag = false; init(); }

В последнем примере flag — логическая переменная или поле, init() -метод, вызываемый, если флаг равен true (говорят, "если flag установлен").

Оператор return (уже рассматривали)

Оператор цикла по предусловию (while)

Синтаксис

While (<условие>) <оператор>

Как и в случае оператора if, в Java принято <оператор> заключать в фигурные скобки.

Int i = 0; while (more) { x /= 2; more = (++i < 10); }

В этом примере more должна быть логической переменной или полем, x — некоторой арифметической переменной или полем. Цикл выполняется 10 раз.

Оператор цикла по постусловию (do while)

Синтаксис

Do <оператор> while (<условие>);

Оператор цикла по постусловию отличается от оператора цикла по предусловию только тем, что в нем виток цикла выполняется всегда как минимум один раз, в то время как в операторе по предусловию может не быть ни одного витка цикла (если условие сразу ложно).

Int i = 0; do { x \= 2; more = (++i < 10); } while (more);

Оператор цикла "со счетчиком" (for)

Синтаксис

For (<инициализация>; <условие>; <инкремент>) <оператор>

Заголовок такого цикла содержит три выражения (в простейшем случае). Из наименования оператора можно понять, что он служит для организации цикла со счетчиком. Поэтому выражение <инициализация> выполняется один раз перед первым витком цикла. После каждого витка цикла выполняется выражение <инкремент>, а потом выражение <условие>. Последнее выражение должно быть логическим и служит для задания условия продолжения цикла. Т.е. пока оно истинно, витки цикла будут продолжаться.

Для удобства составления операторов цикла со счетчиком в данной конструкции введено множество расширений.

  • <инициализация> может быть не выражением, а описанием с инициализацией типа "int i = 0".
  • <инициализация> может быть списком выражений через запятую, например, "i = 0, r = 1" .
  • <инкремент> тоже может быть списком выражений, например, "i++, r*=2"
  • Все составляющие (<инициализация>, <условие> и <инкремент>) являются необязательными. Для выражения <условие> это означает, что условие считается всегда истинным (т.е. выход из цикла должен быть организован какими-то средствами внутри самого цикла).

Т.е. допустим и такой цикл (бесконечный цикл):

For (;;) { . . . }

For (int i = 0; i < 10; i++) x /=2;

Это самая "экономичная" реализация цикла из предыдущих примеров.

Операторы break и continue

В Java нет операторов goto. Как известно, goto приводит к появлению неструктурированных программ. Операторы break и continue являются структурированными аналогами goto.

Они могут применяться в циклах, а break еще и в операторе выбора (switch). Выполнение оператора break приводит к немедленному завершению цикла. Оператор continue вызывает окончание текущего витка цикла и начало нового. Проверка условия в этом случае все же выполняется, так что continue может вызвать и окончание цикла.

For (int i = 0; ;i++) { if (oddOnly && i%2 == 0) continue; y = (x + 1)/x; if (y — x < 0.001) break; x = y; }

Здесь oddOnly — логическая переменная. Если она установлена, то все витки цикла с четными номерами пропускаются с использованием оператора continue;

Условие окончания цикла в данном примере проверяется в середине цикла и, если оно выполнено, то цикл прекращается при помощи оператора break.

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

Lbl: while (...) { . . . for (...) { . . . if (...) break lbl; } . . . }

Здесь оператор break вызовет прекращение как цикла for, так и while.

Оператор выбора (switch)

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

Синтаксис

Switch (<выражение>) { case <константа1>: <операторы1> case <константа2>: <операторы2> . . . }

Выражение должно выдавать целочисленное или символьное значение, константы должны быть того же типа, что и значение этого выражения.

Элементы "case <константа>:" являются метками перехода, если значение выражения совпадает с константой, то будет осуществлен переход на эту метку. Если значение выражения не совпадает ни с одной из констант, то все зависит от наличия фрагмента default. Если он есть, то переход происходит на метку default, если его нет, то весь оператор switch пропускается.

  • В операторе switch фрагменты case не образуют какие-либо блоки. Если после последнего оператора данного case-фрагмента стоит следующий case, то выполнение будет продолжено, начиная с первого оператора этого case-фрагмента.
  • В силу этого в операторе switch обычно применяется оператор break. Он ставится в конце каждого case-фрагмента.

Пример (файл SymbolTest.java)

Рассмотрим демонстрационную программу, в которой использованы операторы for и switch.

Эта программа генерирует случайным образом 100 символов латинского алфавита и классифицирует их как "гласные", "согласные" и "иногда гласные". В последнюю категорию отнесены символы "y" и "w".

Public class SymbolTest { public static void main(String args) { for (int i = 0; i < 100; i++) { char c = (char)(Math.random()*26 + "a"); System.out.print(c + ": "); switch (c) { case "a": case "e": case "i": case "o": case "u": System.out.println("гласная"); break; case "y": case "w": System.out.println("иногда гласная"); break; default: System.out.println("согласная"); } } } }

В данном примере есть несколько новых для нас элементов.

  • Используется метод random() класса Math. Посмотрим документацию по классу Math и разберемся, что он делает.
  • В операторе char c = (char)(Math.random()*26 + "a"); производится сложение арифметического значения с символом. При таком сложении в Java символ преобразуется в число, которое равно коду этого символа.
  • В операторе System.out.print(c + ": "); используется не println(...), а print(...). Метод print(...) отличается от println(...) только тем, что не переводит печать на новую строку, так что следующий оператор print(...) или println(...) продолжит печать в той же строке.

Следует также обратить внимание на фрагменты case. Формально здесь 7 таких фрагментов, но 5 из них не содержат никаких операторов. Так что, можно считать, что здесь 2 case-фрагмента, но каждый из них имеет несколько меток case.

Задание на дом

  • 1 Изменить SymbolTest.java так, чтобы количество генерируемых символов задавалось параметром вызова программы.
  • 2 Написать программу, которая в качестве параметров вызова принимает два числа - длины катетов прямоугольного треугольника, а в качестве результата печатает углы в градусах.


(none)

Java предоставляет богатый набор операторов для управления переменными. Все операторы Java можно разделить на следующие группы:

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

Арифметические операторы - используются в математических выражениях таким же образом, как они используются в алгебре. Предположим, целая переменная A равна 10, а переменная B равна 20. В следующей таблице перечислены арифметические операторы в Java:

Пример

Следующий простой пример показывает программно арифметические операторы. Скопируйте и вставьте следующий java-код в файл test.java, скомпилируйте и запустить эту программу:

Public class Test { public static void main(String args) { int a = 10; int b = 20; int c = 25; int d = 25; System.out.println("a + b = " + (a + b)); System.out.println("a - b = " + (a - b)); System.out.println("a * b = " + (a * b)); System.out.println("b / a = " + (b / a)); System.out.println("b % a = " + (b % a)); System.out.println("c % a = " + (c % a)); System.out.println("a++ = " + (a++)); System.out.println("b-- = " + (a--)); // Проверьте разницу в d++ и ++d System.out.println("d++ = " + (d++)); System.out.println("++d = " + (++d)); } }

A + b = 30 a - b = -10 a * b = 200 b / a = 2 b % a = 0 c % a = 5 a++ = 10 b-- = 11 d++ = 25 ++d = 27

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

Есть следующие операторы сравнения, поддерживаемые на языке Java. Предположим, переменная A равна 10, а переменная B равна 20. В следующей таблице перечислены реляционные операторы или операторы сравнения в Java:

Оператор Описание Пример
== Проверяет, равны или нет значения двух операндов, если да, то условие становится истинным (A == B) - не верны
!= Проверяет, равны или нет значения двух операндов, если значения не равны, то условие становится истинным (A != B) - значение истинна
> Проверяет, является ли значение левого операнда больше, чем значение правого операнда, если да, то условие становится истинным (A > B) - не верны
Проверяет, является ли значение левого операнда меньше, чем значение правого операнда, если да, то условие становится истинным (A
>= Проверяет, является ли значение левого операнда больше или равно значению правого операнда, если да, то условие становится истинным (A >= B) - значение не верны
Проверяет, если значение левого операнда меньше или равно значению правого операнда, если да, то условие становится истинным (A

Пример

Следующий простой пример показывает, программно операторы сравнения в Java. Скопируйте и вставьте следующий java-код в файл test.java, скомпилируйте и запустить эту программу:

Public class Test { public static void main(String args) { int a = 10; int b = 20; System.out.println("a == b = " + (a == b)); System.out.println("a != b = " + (a != b)); System.out.println("a > b = " + (a > b)); System.out.println("a = a = " + (b >= a)); System.out.println("b

A == b = false a != b = true a > b = false a = a = true b

Побитовые операторы

Java определяет несколько побитовых операторов, которые могут быть применены для целочисленных типов: int, long, short, char и byte. В Java побитовый оператор работает над битами и выполняет операцию бит за битом. Предположим, если a = 60; и b = 13; то в двоичном формате они будут следующие:

a = 0011 1100
b = 0000 1101
-----------------
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a = 1100 0011

Предположим целочисленные переменная A равна 60, а переменная B равна 13. В следующей таблице перечислены побитовые операторы в Java:

Оператор Описание Пример
& (побитовое и) Бинарный оператор AND копирует бит в результат, если он существует в обоих операндах. (A & B) даст 12, который является 0000 1100
| (побитовое или) Бинарный оператор OR копирует бит, если он существует в любом из операндов. (A | B) даст 61 который равен 0011 1101
^ (побитовое логическое или) Бинарный оператор XOR копирует бит, если он установлен в одном операнде, но не в обоих. (A ^ B) даст 49, которая является 0011 0001
~ (побитовое дополнение) Бинарный оператор дополнения и имеет эффект «отражения» бит. (~ A) даст -61, которая является формой дополнением 1100 0011 в двоичной записи
Бинарный оператор сдвига влево. Значение левых операндов перемещается влево на количество бит, заданных правым операндом. A
>> (сдвиг вправо) Бинарный оператор сдвига вправо. Значение правых операндов перемещается вправо на количество бит, заданных левых операндом. A >> 2 даст 15, который является 1111
>>> (нулевой сдвиг вправо) Нулевой оператор сдвига вправо. Значение левых операндов перемещается вправо на количество бит, заданных правым операндом, а сдвинутые значения заполняются нулями. A >>> 2 даст 15, который является 0000 1111

Пример

Следующий простой пример показывает, программно побитовые операторы в Java. Скопируйте и вставьте следующий java-код в файл test.java, скомпилируйте и запустить эту программу:

Public class Test { public static void main(String args) { int a = 60; /* 60 = 0011 1100 */ int b = 13; /* 13 = 0000 1101 */ int c = 0; c = a & b; /* 12 = 0000 1100 */ System.out.println("a & b = " + c); c = a | b; /* 61 = 0011 1101 */ System.out.println("a | b = " + c); c = a ^ b; /* 49 = 0011 0001 */ System.out.println("a ^ b = " + c); c = ~a; /*-61 = 1100 0011 */ System.out.println("~a = " + c); c = a > 2; /* 215 = 1111 */ System.out.println("a >> 2 = " + c); c = a >>> 2; /* 215 = 0000 1111 */ System.out.println("a >>> 2 = " + c); } }

Будет получен следующий результат:

A & b = 12 a | b = 61 a ^ b = 49 ~a = -61 a > 15 a >>> 15

Логические операторы

Предположим, логическая переменная A имеет значение true, а переменная B хранит false. В следующей таблице перечислены логические операторы в Java:

Пример

Public class Test { public static void main(String args) { boolean a = true; boolean b = false; System.out.println("a && b = " + (a&&b)); System.out.println("a || b = " + (a||b)); System.out.println("!(a && b) = " + !(a && b)); } }

Это произведет следующий результат:

A && b = false a || b = true !(a && b) = true

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

Существуют следующие операторы присваивания, поддерживаемые языком Java:

Оператор Описание Пример
= Простой оператор присваивания, присваивает значения из правой стороны операндов к левому операнду C = A + B, присвоит значение A + B в C
+= Оператор присваивания «Добавления», он присваивает левому операнду значения правого C += A, эквивалентно C = C + A
-= Оператор присваивания «Вычитания», он вычитает из правого операнда левый операнд C -= A, эквивалентно C = C - A
*= Оператор присваивания «Умножение», он умножает правый операнд на левый операнд C * = A эквивалентно C = C * A
/= Оператор присваивания «Деление», он делит левый операнд на правый операнд C /= A эквивалентно C = C / A
%= Оператор присваивания «Модуль», он принимает модуль, с помощью двух операндов и присваивает его результат левому операнду C %= A, эквивалентно C = C % A
Оператор присваивания «Сдвиг влево» C
>>= Оператор присваивания «Сдвиг вправо» C >>= 2, это как C = C >> 2
&= Оператор присваивания побитового «И» («AND») C &= 2, это как C = C & 2
^= Оператор присваивания побитового исключающего «ИЛИ» («XOR») C ^= 2, это как C = C ^ 2
|= Оператор присваивания побитового «ИЛИ» («OR») C |= 2, это как C = C | 2

Пример

Следующий простой пример показывает, программно логические операторы в Java. Скопируйте и вставьте следующий java-код в файл test.java, скомпилируйте и запустить эту программу:

Public class Test { public static void main(String args) { int a = 10; int b = 20; int c = 0; c = a + b; System.out.println("c = a + b = " + c); c += a ; System.out.println("c += a = " + c); c -= a ; System.out.println("c -= a = " + c); c *= a ; System.out.println("c *= a = " + c); a = 10; c = 15; c /= a ; System.out.println("c /= a = " + c); a = 10; c = 15; c %= a ; System.out.println("c %= a = " + c); c >= 2 ; System.out.println("c >>= 2 = " + c); c >>= 2 ; System.out.println("c >>= a = " + c); c &= a ; System.out.println("c &= 2 = " + c); c ^= a ; System.out.println("c ^= a = " + c); c |= a ; System.out.println("c |= a = " + c); } }

Будет получен следующий результат:

C = a + b = 30 c += a = 40 c -= a = 30 c *= a = 300 c /= a = 1 c %= a = 5 c >= 2 = 5 c >>= 2 = 1 c &= a = 0 c ^= a = 10 c |= a = 10

Прочие операторы

Есть несколько других операторов, поддерживаемых языком Java.

Тернарный оператор или условный оператор (?:)

Тернарный оператор - оператор, который состоит из трех операндов и используется для оценки выражений типа boolean. Тернарный оператор в Java также известен как условный оператор. Этот. Цель тернарного оператора или условного оператора заключается в том, чтобы решить, какое значение должно быть присвоено переменной. Оператор записывается в виде:

Переменная x = (выражение) ? значение if true: значение if false

Пример

Ниже приведен пример:

Public class Test { public static void main(String args){ int a , b; a = 10; b = (a == 1) ? 20: 30; System.out.println("Значение b: " + b); b = (a == 10) ? 20: 30; System.out.println("Значение b: " + b); } }

Будет получен следующий результат:

Значение b: 30 Значение b: 20

Оператор instanceof

Оператор instanceof - проверяет, является ли объект определенного типа (типа класса или типа интерфейса) и используется только для переменных ссылочного объекта. Оператор instanceof записывается в виде:

(Переменная ссылочного объекта) instanceof (класс/тип интерфейса)

Примеры

Если переменная ссылочного объекта в левой части оператора проходит проверку для класса/типа интерфейса на правой стороне, результатом будет значение true. Ниже приведен пример и описание оператора instanceof:

Public class Test { public static void main(String args){ String name = "Олег"; // Следующее вернётся верно, поскольку тип String boolean result = name instanceof String; System.out.println(result); } }

Будет получен следующий результат:

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

Class Vehicle {} public class Car extends Vehicle { public static void main(String args){ Vehicle a = new Car(); boolean result = a instanceof Car; System.out.println(result); } }

Будет получен следующий результат:

Приоритет операторов в Java

Приоритет операторов определяет группирование терминов в выражении. Это влияет как вычисляется выражение. Некоторые операторы имеют более высокий приоритет, чем другие; например оператор умножения имеет более высокий приоритет, чем оператор сложения:

Например, x = 7 + 3 * 2. Здесь x присваивается значение 13, не 20, потому что оператор «*» имеет более высокий приоритет, чем «+», так что сначала перемножается «3 * 2», а затем добавляется «7».

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

Категория Оператор Ассоциативность
Постфикс () . (точка) Слева направо
Унарный ++ - - ! ~ Справа налево
Мультипликативный * / % Слева направо
Аддитивный + - Слева направо
Сдвиг >> >>> Слева направо
Реляционный > >= Слева направо
Равенство == != Слева направо
Побитовое «И» («AND») & Слева направо
Побитовое исключающее «ИЛИ» («XOR») ^ Слева направо
Побитовое «ИЛИ» («OR») | Слева направо
Логическое «И» («AND») && Слева направо
Логическое «ИЛИ» («OR») || Слева направо
Условный ?: Справа налево
Присваивание = += -= *= /= %= >>= Справа налево
Запятая , Слева направо

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

Последнее обновление: 30.10.2018

Большинство операций в Java аналогичны тем, которые применяются в других си-подобных языках. Есть унарные операции (выполняются над одним операндом), бинарные - над двумя операндами, а также тернарные - выполняются над тремя операндами. Операндом является переменная или значение (например, число), участвующее в операции. Рассмотрим все виды операций.

В арифметических операциях участвуют числами. В Java есть бинарные арифметические операции (производятся над двумя операндами) и унарные (выполняются над одним операндом). К бинарным операциям относят следующие:

    операция сложения двух чисел:

    Int a = 10; int b = 7; int c = a + b; // 17 int d = 4 + b; // 11

    операция вычитания двух чисел:

    Int a = 10; int b = 7; int c = a - b; // 3 int d = 4 - a; // -6

    операция умножения двух чисел

    Int a = 10; int b = 7; int c = a * b; // 70 int d = b * 5; // 35

    операция деления двух чисел:

    Int a = 20; int b = 5; int c = a / b; // 4 double d = 22.5 / 4.5; // 5.0

    При делении стоит учитывать, так как если в операции участвуют два целых числа, то результат деления будет округляться до целого числа, даже если результат присваивается переменной float или double:

    Double k = 10 / 4; // 2 System.out.println(k);

    Чтобы результат представлял числос плавающей точкой, один из операндов также должен представлять число с плавающей точкой:

    Double k = 10.0 / 4; // 2.5 System.out.println(k);

    получение остатка от деления двух чисел:

    Int a = 33; int b = 5; int c = a % b; // 3 int d = 22 % 4; // 2 (22 - 4*5 = 2)

Также есть две унарные арифметические операции, которые производятся над одним числом: ++ (инкремент) и -- (декремент). Каждая из операций имеет две разновидности: префиксная и постфиксная:

    ++ (префиксный инкремент)

    Предполагает увеличение переменной на единицу, например, z=++y (вначале значение переменной y увеличивается на 1, а затем ее значение присваивается переменной z)

    Int a = 8; int b = ++a; System.out.println(a); // 9 System.out.println(b); // 9

    ++ (постфиксный инкремент)

    Также представляет увеличение переменной на единицу, например, z=y++ (вначале значение переменной y присваивается переменной z, а потом значение переменной y увеличивается на 1)

    Int a = 8; int b = a++; System.out.println(a); // 9 System.out.println(b); // 8

    -- (префиксный декремент)

    уменьшение переменной на единицу, например, z=--y (вначале значение переменной y уменьшается на 1, а потом ее значение присваивается переменной z)

    Int a = 8; int b = --a; System.out.println(a); // 7 System.out.println(b); // 7

    -- (постфиксный декремент)

    z=y-- (сначала значение переменной y присваивается переменной z, а затем значение переменной y уменьшается на 1)

    Int a = 8; int b = a--; System.out.println(a); // 7 System.out.println(b); // 8

Приоритет арифметических операций

Одни операции имеют больший приоритет чем другие и поэтому выполняются вначале. Операции в порядке уменьшения приоритета:

++ (инкремент), -- (декремент)

* (умножение), / (деление), % (остаток от деления)

+ (сложение), - (вычитание)

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

Int a = 8; int b = 7; int c = a + 5 * ++b; System.out.println(c); // 48

Вначале будет выполняться операция инкремента ++b , которая имеет больший приоритет - она увеличит значение переменной b и возвратит его в качестве результата. Затем выполняется умножение 5 * ++b , и только в последнюю очередь выполняется сложение a + 5 * ++b

Скобки позволяют переопределить порядок вычислений:

Int a = 8; int b = 7; int c = (a + 5) * ++b; System.out.println(c); // 104

Несмотря на то, что операция сложения имеет меньший приоритет, но вначале будет выполняться именно сложение, а не умножение, так как операция сложения заключена в скобки.

Ассоциативность операций

Кроме приоритета операции отличаются таким понятием как ассоциативность . Когда операции имеют один и тот же приоритет, порядок вычисления определяется ассоциативностью операторов. В зависимости от ассоциативности есть два типа операторов:

    Левоассоциативные операторы, которые выполняются слева направо

    Правоассоциативные операторы, которые выполняются справа налево

Так, некоторые операции, например, операции умножения и деления, имеют один и тот же приоритет. Какой же тогда будет результат в выражении:

Int x = 10 / 5 * 2;

Стоит нам трактовать это выражение как (10 / 5) * 2 или как 10 / (5 * 2) ? Ведь в зависимости от трактовки мы получим разные результаты.

Поскольку все арифметические операторы (кроме префиксного инкремента и декремента) являются левоассоциативными, то есть выполняются слева направо. Поэтому выражение 10 / 5 * 2 необходимо трактовать как (10 / 5) * 2 , то есть результатом будет 4.

Операции с числами с плавающей точкой

Следует отметить, что числа с плавающей точкой не подходят для финансовых и других вычислений, где ошибки при округлении могут быть критичными. Например:

Double d = 2.0 - 1.1; System.out.println(d);

В данном случае переменная d будет равна не 0.9, как можно было бы изначально предположить, а 0.8999999999999999. Подобные ошибки точности возникают из-за того, что на низком уровне для представления чисел с плавающей точкой применяется двоичная система, однако для числа 0.1 не существует двоичного представления, также как и для других дробных значений. Поэтому если в таких случаях обычно применяется класс BigDecimal, который позволяет обойти подобные сиуации.



Загрузка...