Слайд 1Дисциплина: «Язык программирования Java»
Преподаватель:
доцент кафедры математического моделирования и информационных систем,
к.т.н. Листопад Сергей Викторович
БФУ им. И.Канта
Калининград, 2013
Лекция 2:
«Лексика языка
Java»
Слайд 2Кодировка
Для записи текста программы применяется кодировка Unicode UTF-16. Unicode
UTF-16 представляет символы кодом из 2 байт, описывая 65535 символов.
Это позволяет поддерживать практически все распространенные языки мира. Первые 128 символов совпадают с набором ASCII. Однако понятно, что требуется некоторое специальное обозначение, чтобы иметь возможность задавать в программе любой символ Unicode, ведь никакая клавиатура не позволяет вводить более 65 тысяч различных знаков. Эта конструкция представляет символ Unicode, используя только символы ASCII. Например, если в программу нужно вставить знак с кодом 6917, необходимо его представить в шестнадцатеричном формате (1B05) и записать:
\u1B05,
причем буква u должна быть строчной, а шестнадцатеричные цифры A, B, C, D, E, F можно использовать произвольно, как заглавные, так и строчные. Таким образом можно закодировать все символы Unicode от \u0000 до \uFFFF. Буквы русского алфавита начинаются с \u0410 (только буква Ё имеет код \u0401 ) по \u044F (код буквы ё \u0451 ).
В версиях языка Java до 1.1 применялся Unicode версии 1.1.5, в последнем выпуске 1.4 используется 3.0. Таким образом, Java следит за развитием стандарта и базируется на современных версиях. Для любой JDK точную версию Unicode, используемую в ней, можно узнать из документации к классу Character.
Слайд 4Пробелы
Пробелами в данном случае называют все символы, разбивающие текст программы
на лексемы:
ASCII -символ SP, space, пробел, \u0020, десятичный код 32;
ASCII
-символ HT, horizontal tab, символ горизонтальной табуляции, \u0009, десятичный код 9;
ASCII -символ FF, form feed, символ перевода страницы (был введен для работы с принтером), \u000c, десятичный код 12;
завершение строки.
Завершением строки считается:
ASCII -символ LF, символ новой строки;
ASCII -символ CR, "возврат каретки";
символ CR, за которым сразу же следует символ LF.
Например, следующую часть программы (вычисление корней квадратного уравнения):
double a = 1, b = 1, c = 6;
double D = b * b - 4 * a * c;
if (D >= 0) {
double x1 = (-b + Math.sqrt (D)) / (2 * a);
double x2 = (-b - Math.sqrt (D)) / (2 * a);
}
можно записать и в таком виде:
double a=1,b=1,c=6;double D=b*b-4*a*c;if(D>=0){double x1=(-b+Math.sqrt(D))/(2*a);
double x2=(-b-Math.sqrt(D))/(2*a);}
Слайд 5Комментарии
В Java комментарии бывают двух видов:
строчные начинаются с символов //
и длятся до конца текущей строки, например:
int y=1970; // год
рождения
блочные комментарии располагаются между символами /* и */, могут занимать произвольное количество строк, например:
/*
Этот цикл не может начинаться с нуля
из-за особенностей алгоритма
*/
for (int i=1; i<10; i++) {
...
}
Часто блочные комментарии оформляют следующим образом:
/*
* Описание алгоритма работы
* следующего цикла while
*/
while (x > 0) {
...
}
Блочный комментарий не обязательно должен располагаться на нескольких строках, он может даже находиться в середине оператора:
float s = 2*Math.PI/*getRadius()*/;
// Закомментировано для отладки
Слайд 6Комментарии
Комментарии не могут находиться в символьных и строковых литералах, идентификаторах.
Следующий пример содержит случаи неправильного применения комментариев:
// В этом примере
текст /*…*/ станет просто
// частью строки s
String s = "text/*just text*/";
/*
Следующая строка станет причиной ошибки
при компиляции, так как комментарий разбил
имя метода getRadius()
*/
circle.get/*comment*/Radius();
А такой код допустим:
// Комментарий может разделять вызовы функций:
circle./*comment*/getRadius();
// Комментарий может заменять пробелы:
int/*comment*/x=1;
В последней строке между названием типа данных int и названием переменной x обязательно должен быть пробел или, как в данном примере, комментарий.
Слайд 7Комментарии
Комментарии не могут быть вложенными. Символы /*, */, // не
имеют никакого особенного значения внутри уже открытых комментариев, как строчных,
так и блочных:
/* начало комментария /* // /** завершение: */
/*
comment
/*
more comments
*/
finish
*/
Слайд 8Комментарии
Любые комментарии полностью удаляются из программы во время компиляции, поэтому
их можно использовать неограниченно, не опасаясь, что это повлияет на
бинарный код. Основное их предназначение - сделать программу простой для понимания, в том числе и для других разработчиков, которым придется в ней разбираться по какой-либо причине. Также комментарии зачастую используются для временного исключения частей кода, например:
int x = 2;
int y = 0;
/*
if (x > 0)
y = y + x*2;
else
y = -y - x*4;
*/
y = y*y;// + 2*x;
Слайд 9Комментарий разработчика
Комментарий разработчика записывается так же, как и блочный. Единственное
различие в начальной комбинации символов – для документации комментарий необходимо
начинать с /**. Например:
/**
* Вычисление модуля целого числа.
* Этот метод возвращает
* абсолютное значение аргумента x.
*/
int getAbs(int x) {
if (x>=0)
return x;
else
return -x;
}
Первое предложение должно содержать краткое резюме всего комментария. В дальнейшем оно будет использовано как пояснение этой функции в списке всех методов класса (ниже будут описаны все конструкции языка, для которых применяется комментарий разработчика).
Слайд 10Комментарий разработчика
Допускается применение тегов, таких как и . Однако
теги заголовков с по и использовать нельзя,
так как они активно применяются javadoc для создания структуры документации.
/**
* Первое предложение - краткое
* описание метода.
*
* Так оформляется пример кода:
*
*
* if (condition==true) {
* x = getWidth();
* y = x.getHeight();
* }
*
* А так описывается HTML-список:
*
* - Можно использовать наклонный шрифт
* курсив,
* - или жирный жирный.
*
*/
public void calculate (int x, int y) {
...
}
Первое предложение – краткое описание метода.
Так оформляется пример кода:
if (condition==true) {
x = getWidth();
y = x.getHeight();
}
А так описывается HTML-список:
* Можно использовать наклонный шрифт курсив,
* или жирный жирный.
Слайд 11Комментарий разработчика
Наконец, javadoc поддерживает специальные теги. Они начинаются с символа
@. Подробное описание этих тегов можно найти в документации. Например,
можно использовать тег @see, чтобы сослаться на другой класс, поле или метод, или даже на другой Internet-сайт.
/**
* Краткое описание.
*
* Развернутый комментарий.
*
* @see java.lang.String
* @see java.lang.Math#PI
* @see
Official
* Java site */
Комментарии разработчика могут быть записаны перед объявлением классов, интерфейсов, полей, методов и конструкторов. Если записать комментарий /** … */ в другой части кода, то ошибки не будет, но он не попадет в документацию, генерируемую javadoc. Кроме того, можно описать пакет. Для этого необходимо создать специальный файл package.html, сохранить в нем комментарий и поместить его в каталог пакета. HTML-текст, содержащийся между тегами и , будет помещен в документацию, а первое предложение будет использоваться для краткой характеристики этого пакета.
Слайд 12Лексемы
С точки зрения программиста пробелы и комментарии применяются для того,
чтобы сделать программу более читаемой и понятной для дальнейшего развития.
С точки зрения компилятора, основная роль пробелов и комментариев – служить разделителями между лексемами, причем сами разделители далее отбрасываются и на компилированный код не влияют. Например, все следующие примеры объявления переменной эквивалентны:
// Используем пробел в качестве разделителя.
int x = 3 ;
// здесь разделяем знаком табуляции
int x = 3 ;
/*
* Единственный принципиально необходимый
* разделитель между названием типа данных
* int и именем переменной x здесь описан
* комментарием блочного типа.
*/
int/**/x=3;
Ниже перечислены все виды лексем в Java:
идентификаторы (identifiers);
ключевые слова (key words);
литералы (literals);
разделители (separators);
операторы (operators).
Слайд 13Лексемы. Идентификаторы
Идентификаторы – это имена, которые даются различным элементам
языка для упрощения доступа к ним. Имена имеют пакеты, классы,
интерфейсы, поля, методы, аргументы и локальные переменные. Длина имени не ограничена.
Идентификатор состоит из букв и цифр. Имя не может начинаться с цифры. Java-буквы, используемые в идентификаторах, включают в себя ASCII -символы A-Z ( \u0041 - \u005a ), a-z ( \u0061 - \u007a ), а также знаки подчеркивания _ ( ASCII underscore, \u005f ) и доллара $ ( \u0024 ). Знак доллара используется только при автоматической генерации кода (чтобы исключить случайное совпадение имен), либо при использовании каких-либо старых библиотек, в которых допускались имена с этим символом. Java-цифры включают в себя обычные ASCII -цифры 0-9 ( \u0030 - \u0039 ).
Регулярное выражение идентификатора переменной:
[_a-zA-Z$][_a-zA-Z0-9$]*
Для идентификаторов не допускаются совпадения с зарезервированными словами (ключевые слова, булевские литералы true и false и null-литерал). Идентификаторы регистрозависимы.
В этой лекции уже применялись следующие идентификаторы:
Character, a, b, c, D, x1, x2, Math, sqrt, x, y, i, s, PI, getRadius, circle, getAbs, calculate, condition, getWidth, getHeight, java, lang, String
Также допустимыми являются идентификаторы:
Computer, COLOR_RED, _, aVeryLongNameOfTheMethod
Слайд 14Лексемы. Ключевые слова
Ключевые слова – это зарезервированные слова, состоящие
из ASCII -символов и выполняющие различные задачи языка. Вот их
полный список:
abstract double int strictfp
Boolean else interface super
break extends long switch
byte final native synchronized
case finally new this
catch float package throw
char for private throws
class goto protected transient
const if public try
continue implements return void
default import short volatile
do instanceof static while
Ключевые слова goto и const зарезервированы, но не используются. Оба булевских литерала true, false и null- литерал null часто считают ключевыми словами (возможно, потому, что многие средства разработки подсвечивают их таким же образом), однако это именно литералы.
Слайд 15Лексемы. Литералы
Литералы позволяют задать в программе значения для числовых,
символьных и строковых выражений, а также null- литералов.
Всего в
Java определено 6 видов литералов:
целочисленный (integer);
дробный (floating-point);
булевский (boolean);
символьный (character);
строковый (string);
null- литерал (null-literal).
Слайд 16Целочисленные литералы
Целочисленные литералы позволяют задавать целочисленные значения в десятичном, восьмеричном
и шестнадцатеричном виде. Десятичный формат традиционен и ничем не отличается
от правил, принятых в других языках. Значения в восьмеричном виде начинаются с нуля. Запись шестнадцатеричных чисел начинается с 0x или 0X (цифра 0 и латинская ASCII -буква X в произвольном регистре). Таким образом, ноль можно записать тремя различными способами:
0, 0X0, 0x0
Как обычно, для записи цифр 10 - 15 в шестнадцатеричном формате используются буквы A, B, C, D, E, F, прописные или строчные. Примеры таких литералов:
0xaBcDeF, 0xCafe, 0xDEC
Здесь необходимо упомянуть два целочисленных типа int и long длиной 4 и 8 байт, соответственно. Оба типа знаковые. Тип int хранит значения от -231 до 231-1
(-2.147.483.648 до 2.147.483.647). По умолчанию целочисленный литерал имеет тип int, т.е. допустимо использовать литералы от 0 до 2147483647, в восьмеричной записи от 00 до 017777777777 ( =231-1 ), с унарным оператором - допустимо -020000000000 ( = -231 ). Для шестнадцатеричного формата – от 0x0 до 0x7fffffff ( =231-1 ) и -0x80000000 ( = -231 ).
Тип long имеет длину 64 бита, позволяя хранить значения от -263 до 263-1. Чтобы ввести такой литерал, необходимо в конце поставить латинскую букву L или l. Аналогично можно выписать максимальные допустимые значения для них:
9223372036854775807L, 0777777777777777777777L, 0x7fffffffffffffffL
// наибольшие отрицательные значения:
-9223372036854775808L, -01000000000000000000000L, -0x8000000000000000L
Слайд 17Дробные литералы
Дробные литералы представляют собой числа с плавающей десятичной точкой.
Дробный литерал состоит из следующих составных частей:
целая часть;
десятичная точка (используется
ASCII -символ точка);
дробная часть;
порядок (состоит из латинской ASCII -буквы E в произвольном регистре и целого числа с опциональным знаком + или - );
окончание-указатель типа.
Целая и дробная части записываются десятичными цифрами, а указатель типа имеет два возможных значения – латинская буква D (для типа double ) или F (для типа float ) в произвольном регистре. Дробный литерал имеет тип float, если он заканчивается на латинскую букву F в произвольном регистре. В противном случае он рассматривается как значение типа double и может включать в себя окончание D или d, как признак типа double (используется только для наглядности).
Необходимыми частями являются:
хотя бы одна цифра в целой или дробной части;
десятичная точка или показатель степени, или указатель типа.
Примеры:
3.14, 3.14F, 2., .5, 7e10, 3.1E-20, 1f, 0f, 1e+5F, 0., 3.14d, 1e-4, 31.34E45D
Слайд 18Дробные литералы
В Java дробные числа float и double хранятся в
памяти в бинарном виде в формате, стандартизированном спецификацией IEEE 754.
В этой спецификации описаны не только конечные дробные величины, но и еще несколько особых значений, а именно:
положительная и отрицательная бесконечности (positive/negative infinity);
значение "не число", Not-a-Number, сокращенно NaN;
положительный и отрицательный нули.
Для этих значений нет специальных обозначений. Чтобы получить такие величины, необходимо либо произвести арифметическую операцию (например, результатом деления ноль на ноль 0.0/0.0 является NaN), либо обратиться к константам в классах Float и Double, а именно POSITIVE_INFINITY, NEGATIVE_INFINITY и NaN.
Типы данных накладывают ограничения на возможные значения литералов, как и для целочисленных типов. Попытка указать литерал со слишком большим абсолютным значением (например, 1e40F) приведет к ошибке компиляции. Аналогично, указание литерала со слишком малым ненулевым значением (например, 1e-350) также приводит к ошибке. Это значение должно быть округлено до нуля. Однако если округление приводит не к нулю, то компилятор произведет его сам:
// ошибка, выражение должно быть округлено до 0
0.00000000000000000000000000000000000000000001f
// ошибки нет, компилятор сам округляет до 1
1.00000000000000000000000000000000000000000001f
Слайд 19Логические литералы
Логические литералы имеют два возможных значения – true
и false. Эти два зарезервированных слова не являются ключевыми, но
также не могут использоваться в качестве идентификатора.
Слайд 20Символьные литералы
Символьные литералы описывают один символ из набора Unicode,
заключенный в одиночные кавычки, или апострофы ( ASCII -символ single
quote, \u0027 ). Например: 'a' , ' ‘, 'K' (греческая буква каппа). Также допускается специальная запись для описания символа через его код: '\u0041' (латинская буква A), '\u0410' (русская буква А), '\u0391' (греческая буква A).
Символьный литерал должен содержать строго один символ, или специальную последовательность, начинающуюся с \. Для записи специальных символов (неотображаемых и служебных, таких как ", ', \ ) используются следующие обозначения:
\b \u0008 backspace BS – забой
\t \u0009 horizontal tab HT – табуляция
\n \u000a linefeed LF – конец строки
\f \u000c form feed FF – конец страницы
\r \u000d carriage return CR – возврат каретки
\" \u0022 double quote " – двойная кавычка
\' \u0027 single quote ' – одинарная кавычка
\\ \u005c backslash \ – обратная косая черта
\шестнадцатеричный код от \u0000 до \u00ff.
Поддержка ввода символов через восьмеричный код обеспечивается для совместимости с С (можно символы от \u0000 до \u00ff, т.е. с кодом от 0 до 255). Например, '\101' (эквивалентно '\u0041‘). Поскольку обработка Unicode -последовательностей ( \uhhhh ) производится раньше лексического анализа, то следующий пример является ошибкой: '\u000a' (символ конца строки). Необходимо использовать специальную последовательность: '\n‘. Для символа \u000d необходимо использовать обозначение \r.
Слайд 21Строковые литералы
Строковые литералы состоят из набора символов и записываются
в двойных кавычках. Длина может быть нулевой или сколь угодно
большой. Любой символ может быть представлен специальной последовательностью, начинающейся с \:
"" // литерал нулевой длины
"\"" //литерал, состоящий из одного символа "
"Простой текст" //литерал длины 13
Строковый литерал нельзя разбивать на несколько строк в коде программы. Если требуется текстовое значение из нескольких строк, то необходимо воспользоваться символами \n и/или \r. Если текст слишком длинный, чтобы уместиться на одной строке кода, можно использовать оператор конкатенации +. Примеры строковых литералов:
"Длинный текст " +
"с переносом"
"Hello, world!\r\nHello!"
На строковые литералы распространяются те же правила, что и на символьные в отношении использования символов новой строки \u000a и \u000d. Каждый строковый литерал является экземпляром класса String.
Слайд 22Null-литерал
Null- литерал может принимать всего одно значение: null. Это
литерал ссылочного типа, причем эта ссылка никуда не ссылается, объект
отсутствует. Разумеется, его можно применять к ссылкам любого объектного типа данных.
Слайд 23Лексемы. Разделители
Разделители – это специальные символы, которые используются в
служебных целях языка. Назначение каждого из них будет рассмотрено по
ходу изложения курса. Вот их полный список:
( ) [ ] { } ; . ,
Слайд 24Лексемы. Операторы
Операторы используются в различных операциях – арифметических, логических, битовых,
операциях сравнения и присваивания. Следующие 37 лексем (все состоят только
из ASCII-символов) являются операторами языка Java:
= > < ! ~ ? :
== <= >= != && || ++ --
+ - * / & | ^ % << >> >>>
+= -= *= /= &= |= ^= %= <<= >>= >>>=
Большинство из них вполне очевидны и хорошо известны из других языков программирования, однако некоторые нюансы в работе с операторами в Java все же присутствуют.
Слайд 25Операторы присваивания и сравнения
Различаются оператор присваивания = и оператор
сравнения ==.
x = 1; // присваиваем переменной x значение 1
x
== 1 // сравниваем значение переменной x с единицей
Оператор сравнения всегда возвращает булевское значение true или false. Оператор присваивания возвращает значение правого операнда. Поэтому обычная опечатка в языке С, когда эти операторы путают:
// пример вызовет ошибку компилятора
if (x=0) { // здесь должен применяться оператор
// сравнения ==
...
}
в Java легко устраняется. Поскольку выражение x=0 имеет числовое значение 0, а не булевское (и тем более не воспринимается как всегда истинное), то компилятор сообщает об ошибке (необходимо писать x==0 ).
Условие "не равно" записывается как !=. Например:
if (x!=0) {
float f = 1./x;
}
Сочетание какого-либо оператора с оператором присваивания = используется при изменении значения переменной. Например, следующие две строки эквивалентны:
x = x + 1;
x += 1;
Слайд 26Арифметические операции
Наряду с четырьмя обычными арифметическими операциями +, -, *,
/, существует оператор получения остатка от деления %, который может
быть применен как к целочисленным аргументам, так и к дробным. Оператор деления целых чисел / всегда возвращает целое число:
9/5 возвращает 1 9/(-5) возвращает -1
(-9)/5 возвращает -1 (-9)/(-5) возвращает 1
Остаток может быть положительным, только если делимое было положительным. Соответственно, остаток может быть отрицательным только в случае отрицательного делимого. Попытка получить остаток от деления на 0 приводит к ошибке.
9%5 возвращает 4 9%(-5) возвращает 4
(-9)%5 возвращает -4 (-9)%(-5) возвращает -4
Деление с остатком для дробных чисел может быть произведено по двум различным алгоритмам. Один из них повторяет правила для целых чисел, и именно он представлен оператором %. Если в рассмотренном примере деления 9 на 5 перейти к дробным числам, значение остатка во всех вариантах не изменится:
9.0%5.0 возвращает 4.0 9.0%(-5.0) возвращает 4.0
(-9.0)%5.0 возвращает -4.0 (-9.0)%(-5.0) возвращает -4.0
Однако стандарт IEEE 754 определяет другие правила. Такой способ представлен методом стандартного класса Math.IEEEremainder(double f1, double f2). Результат этого метода – значение, которое равно f1-f2*n, где n – целое число, ближайшее к значению f1/f2, а если два целых числа одинаково близки к этому отношению, то выбирается четное:
Math.IEEEremainder(9.0, 5.0) возвращает -1.0 Math.IEEEremainder(9.0, -5.0) возвращает -1.0
Math.IEEEremainder(-9.0, 5.0) возвращает 1.0 Math.IEEEremainder(-9.0, -5.0) возвращает 1.0
Унарные операторы инкрементации ++ и декрементации - -, как обычно, можно использовать как справа, так и слева.
int x=1; int y=++x; int y=x++;
Слайд 27Логические операторы
Логические операторы & и | можно использовать в двух
вариантах. Первый вариант операторов ( &, | ) всегда вычисляет
оба операнда, второй же –
(&&, ||) не будет продолжать вычисления, если значение выражения уже очевидно:
int x=1;
(x>0) | calculate(x) // в таком выражении
// произойдет вызов
// calculate
(x>0) || calculate(x) // а в этом - нет
Логический оператор отрицания "не" записывается как !. Этот оператор меняет булевское значение на противоположное.
int x=1;
x>0 // выражение истинно
!(x>0) // выражение ложно
Оператор с условием ?: состоит из трех частей – условия и двух выражений. Сначала вычисляется условие (булевское выражение), а на основании результата значение всего оператора определяется первым выражением в случае получения истины и вторым – если условие ложно. Например, так можно вычислить модуль числа x:
x>0 ? x : -x
Слайд 28Битовые операции
Как известно, битовые операции "и", "или", "исключающее или" принимают
два аргумента и выполняют логическое действие попарно над соответствующими битами
аргументов. При этом используются те же обозначения, что и для логических операторов, но, конечно, только в первом (одиночном) варианте. Например, вычислим выражение 5&6:
00000101
& 00000110
-------------
00000100
// число 5 в двоичном виде
// число 6 в двоичном виде
//проделали операцию "и" попарно над битами
// в каждой позиции
То есть выражение 5&6 равно 4.
Исключение составляет лишь оператор "не" или "NOT", который для побитовых операций записывается как ~ (для логических было !). Этот оператор меняет каждый бит в числе на противоположный. Например, ~(-1)=0. Можно легко установить общее правило для получения битового представления отрицательных чисел:
Если n – целое положительное число, то -n в битовом представлении равняется ~(n-1).
Слайд 29Битовые операции
При сдвиге влево оператором
на указанное количество позиций влево, причем освободившиеся справа позиции заполняются
нулями. Эта операция аналогична умножению на 2n.
// Сдвиг влево для положительного числа 20
20 << 00 = 00000000000000000000000000010100 = 20
20 << 01 = 00000000000000000000000000101000 = 40
…
20 << 27 = 10100000000000000000000000000000 = -1610612736
20 << 28 = 01000000000000000000000000000000 = 1073741824
20 << 29 = 10000000000000000000000000000000 = -2147483648
20 << 30 = 00000000000000000000000000000000 = 0
20 << 31 = 00000000000000000000000000000000 = 0
// Сдвиг влево для отрицательного числа -21
-21 << 00 = 11111111111111111111111111101011 = -21
-21 << 01 = 11111111111111111111111111010110 = -42
-21 << 02 = 11111111111111111111111110101100 = -84
...
-21 << 25 = 11010110000000000000000000000000 = -704643072
-21 << 26 = 10101100000000000000000000000000 = -1409286144
-21 << 27 = 01011000000000000000000000000000 = 1476395008
-21 << 28 = 10110000000000000000000000000000 = -1342177280
-21 << 29 = 01100000000000000000000000000000 = 1610612736
-21 << 30 = 11000000000000000000000000000000 = -1073741824
-21 << 31 = 10000000000000000000000000000000 = -2147483648
Как видно из примера, неожиданности возникают тогда, когда значащие биты начинают занимать первую позицию и влиять на знак результата.
Слайд 30Битовые операции
При сдвиге вправо все биты аргумента смещаются на указанное
количество позиций, соответственно, вправо. Однако встает вопрос – каким значением
заполнять освобождающиеся позиции слева, в том числе и отвечающую за знак. Есть два варианта. Оператор >> использует для заполнения этих позиций значение знакового бита, то есть результат всегда имеет тот же знак, что и начальное значение. Второй оператор >>> заполняет их нулями, то есть результат всегда положительный.
// Сдвиг вправо для положительного числа 20
// Оператор >>
20 >> 00 = 00000000000000000000000000010100 = 20
20 >> 01 = 00000000000000000000000000001010 = 10
20 >> 02 = 00000000000000000000000000000101 = 5
20 >> 03 = 00000000000000000000000000000010 = 2
20 >> 04 = 00000000000000000000000000000001 = 1
20 >> 05 = 00000000000000000000000000000000 = 0
// Оператор >>>
20 >>> 00 = 00000000000000000000000000010100 = 20
20 >>> 01 = 00000000000000000000000000001010 = 10
20 >>> 02 = 00000000000000000000000000000101 = 5
20 >>> 03 = 00000000000000000000000000000010 = 2
20 >>> 04 = 00000000000000000000000000000001 = 1
20 >>> 05 = 00000000000000000000000000000000 = 0
Очевидно, что для положительного аргумента операторы >> и >>> работают совершенно одинаково. Дальнейший сдвиг на большее количество позиций будет также давать нулевой результат.
Слайд 31Битовые операции
// Сдвиг вправо для отрицательного числа -21
// Оператор >>
-21
>> 00 = 11111111111111111111111111101011 = -21
-21 >> 01 = 11111111111111111111111111110101
= -11
-21 >> 02 = 11111111111111111111111111111010 = -6
-21 >> 03 = 11111111111111111111111111111101 = -3
-21 >> 04 = 11111111111111111111111111111110 = -2
-21 >> 05 = 11111111111111111111111111111111 = -1
// Оператор >>>
-21 >>> 00 = 11111111111111111111111111101011 = -21
-21 >>> 01 = 01111111111111111111111111110101 = 2147483637
-21 >>> 02 = 00111111111111111111111111111010 = 1073741818
-21 >>> 03 = 00011111111111111111111111111101 = 536870909
-21 >>> 04 = 00001111111111111111111111111110 = 268435454
-21 >>> 05 = 00000111111111111111111111111111 = 134217727
...
-21 >>> 24 = 00000000000000000000000011111111 = 255
-21 >>> 25 = 00000000000000000000000001111111 = 127
-21 >>> 26 = 00000000000000000000000000111111 = 63
-21 >>> 27 = 00000000000000000000000000011111 = 31
-21 >>> 28 = 00000000000000000000000000001111 = 15
-21 >>> 29 = 00000000000000000000000000000111 = 7
-21 >>> 30 = 00000000000000000000000000000011 = 3
-21 >>> 31 = 00000000000000000000000000000001 = 1
Как видно из примеров, эти операции аналогичны делению на 2n. Причем, если для положительных аргументов с ростом n результат закономерно стремится к 0, то для отрицательных предельным значением является -1.