Показаны сообщения с ярлыком boolean. Показать все сообщения
Показаны сообщения с ярлыком boolean. Показать все сообщения

23 апр. 2015 г.

Преобразование примитивных типов в Java - Практика

Теперь немного практики чтобы закрепить предыдущую тему. Сперва простая программа неявного преорбазования примитивных типов Java.

С0004Тут все предельно просто. Единственное что стоит отметить, что я задал значение переменной i типа int в двоичной системе счисления, чтобы было явно видно, что количество значимых бит больше чем 23.
Тут же приведу и вывод этой программы:
С0005

Как  можно заметить, есть небольшая потеря точности при преобразовании целочисленного значения int к значению типа float.

Так же обратите внимание что значение переменной типа char спокойно преобразовалось в другие, более емкие, типы.

Теперь перейдем к примерам явного преобразования.

С0006[4][2]Тут все тоже достаточно просто. Единственное что может надо пояснить почему число 454.874 было преобразовано в –58. Казалось бы удивительный результат и почти магический, но магии тут ни какой нет, тут одна матчасть.

Достаточно просто посмотреть на двоичное представление числа 454 и отбросить все старшие биты, оставив только младшие восемь.
В результате в восьми младших битах получаем число 11000110, которое для типа int соответствует числу 198, а для типа byte числу –58, поскольку первый бит для него знаковый, поэтому данное число является отрицательным.

Если чуть более глубоко капнуть, то тоже самое получается если разделить число 454 по модулю на диапазон целевого типа, который в данном случае byte и его диапазон покрывает 256 значений.

454 % 256 = 198

Число 198 можно представить восемью битами, про это мы уже говорили выше. И то что для int 198, то для byte –58. Вот такая арифметика.

Далее у нас идет пример когда значение float слишком большое для значения int, в данном случае значение int получает свое максимальное или минимальное значение (с учетом знака).

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


Ну и затем совсем простой пример с классом Byte, который является оберткой для типа byte.

Теперь посмотрим на вывод этой программы:

21 апр. 2015 г.

Преобразование примитивных типов в Java

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

В Java возможны преобразования между целыми значениями и значениями с плавающей точкой. Кроме того, можно преобразовывать значения целых типов и типов с плавающей точкой в значения типа char и наоборот, поскольку каждый символ соответствует цифре в кодировке Unicode. Фактически тип boolean является единственным примитивным типом в Java, который нельзя преобразовать в другой примитивный тип. Кроме того, любой другой примитивный тип нельзя преобразовать в boolean.

Преобразование типов в Java бывает двух видов: неявное и явное.

Неявное преобразование типов выполняется в случае если выполняются условия:

  1. Оба типа совместимы
  2. Длина целевого типа больше или равна длине исходного типа

JavaBasics_ImplicitTypeCastingPrimitives

Во всех остальных случаях должно использоваться явное преобразование типов.

Так же существуют два типа преобразований:

  1. Расширяющее преобразование (widening conversion)
  2. Сужающее преобразование (narrowing conversion)

Расширяющее преобразование (widening conversion) происходит, если значение одного типа преобразовывается в более широкий тип, с большим диапазоном допустимых значений. Java выполняет расширяющие преобразования автоматически, например, если вы присвоили литерал типа int переменной типа double или значение пепременной типа char переменной типа int. Неявное преобразование всегда имеет расширяющий тип.

GrabliНо у тут могут быть свои небольшие грабельки. Например если преобразуется значение int в значение типа float. И у значения int в двоичном представлении больше чем 23 значащих бита, то возможна потеря точности, так как у типа float под целую часть отведено 23 бита. Все младшие биты значения int, которые не поместятся в 23 бита мантиссы float, будут отброшены, поэтому хотя порядок числа сохраниться, но точность будет утеряна. То же самое справедливо для преобразования типа long в тип double.

Расширяющее преобразование типов Java можно изобразить еще так:

JavaBasics_ImplicitTypeCastingPrimitivesDetailed

Сплошные линии обозначают преобразования, выполняемые без потери данных. Штриховые линии говорят о том, что при преобразовании может произойти потеря точности.

Стоит немного пояснить почему, к примеру тип byte не преобразуется автоматически (не явно) в тип char, хотя тип byte имеет ширину 8 бит, а char 16, тоже самое касается и преобразования типа short в char. Это происходит потому, что byte и short знаковые типы данных, а char без знаковый. Поэтому в данном случае требуется использовать явное приведение типов, поскольку компилятору надо явно указать что вы знаете чего хотите и как будет обрабатываться знаковый бит типов byte и short при преобразовании к типу char.

Поведение величины типа char в большинстве случаев совпадает с поведением величины целого типа, следовательно, значение типа char можно использовать везде, где требуются значения int или long. Однако напомним, что тип char не имеет знака, поэтому он ведет себя отлично от типа short, несмотря на то что диапазон обоих типов равен 16 бит.

short s = ( short) 0xffff; // Данные биты представляют число –1
char c = '\uffff'; // Те же биты представляют символ юникода
int i1 = s; // Преобразование типа short в int дает –1
int i2 = c; // Преобразование char в int дает 65535

Сужающее преобразование (narrowing conversion) происходит, если значение преобразуется в значение типа, диапазон которого не шире изначального. Сужающие преобразования не всегда безопасны: например, преобразование целого значения 13 в byte имеет смысл, а преобразование 13000 в byte неразумно, поскольку byte может хранить только числа от −128 до 127. Поскольку во время сужающего преобразования могут быть потеряны данные, Java компилятор возражает против любого такого преобразования, даже если преобразуемое значение укладывается в более узкий диапазон указанного типа:

int i = 13;
byte b = i; // Компилятор не разрешит это выражение

Единственное исключение из правила – присвоение целого литерала (значения типа int) переменной byte или short, если литерал соответствует диапазону переменной.

Сужающее преобразование это всегда явное преобразование типов.

Явное преобразование примитивных типов

Оператором явного преобразования типов или точнее говоря приведения типов являются круглые скобки, внутри которых указан тип, к которому происходит преобразование – (type). Например:

int i = 13;
byte b = (byte) i; // Принудительное преобразование int в byte
i = (int) 13.456; // Принудительное преобразование литерала типа double в int 13

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

При приведении более емкого целого типа к менее емкому старшие биты просто отбрасываются. По существу это равнозначно операции деления по модулю приводимого значения на диапазон целевого типа (например для типа byte это 256).

Слишком большое дробное число при приведении к целому превращается в MAX_VALUE или MIN_VALUE.

Слишком большой double при приведении к float превращается в Float.POSITIVE_INFINITY или Float.NEGATIVE_INFINITY.

Таблица представленная ниже представляет собой сетку, где для каждого примитивного типа указаны типы, в которые их можно преобразовать, и способ преобразования. Буква N в таблице означает невозможность преобразования. Буква Y означает расширяющее преобразование, которое выполняется автоматически. Буква С означает сужающее преобразование, требующее явного приведения. Наконец, Y* означает автоматическое расширяющее преобразование, в процессе которого значение может потерять некоторые из наименее значимых разрядов. Это может произойти при преобразовании int или long во float или double. Типы с плавающей точкой имеют больший диапазон, чем целые типы, поэтому int или long можно представить посредством float или double. Однако типы с плавающей точкой являются приближенными числами и не всегда могут содержать так много значащих разрядов в мантиссе, как целые типы.

JavaBasics_ImplicitTypeCastingPrimitivesDetailedTable

Автоматическое расширение типов в выражениях

Так же стоит еще раз упомянуть об автоматическом повышении (расширении) типов в выражениях. Мы с этим уже сталкивались когда рассматривали целочисленные типы данных и операции над ними, но все же стоит и тут напомнить, чтобы усвоилось еще лучше и к тому же это имеет непосредственное отношение к данной теме. В примере ниже знак @ означает любой допустимый оператор, например +, , *, / и т.п.

С0001

То есть, все целочисленные литералы в выражениях, а так же типы byte, short и char расширяются до int. Если, как описано выше, в выражении не присутствуют другие, более большие типы данных (long, float или double). Поэтому приведенный выше пример вызовет ошибку компиляции, так как переменная c имеет тип byte, а выражение b+1, в результате автоматического повышения имеет тип int.

Неявное приведение типов в выражениях совмещенного присваивания

Хоть данный раздел и относится к неявному преобразованию (приведению) типов, его объяснение мы привели тут, поскольку в данном случае так же работает и автоматическое расширение типов в выражениях, а затем уже неявное приведение типов. Вот такой кордебалет. Пример ниже я думаю все разъяснит. Так же как и в предыдущем объяснении знак @ означает любой допустимый оператор, например +, , *, / и т.п.

С0002

Это стоит пояснить на простом примере:

byte b2 = 50;
b2 = b2 * 2; // не скомпилируется
b2 *= 2; //скомпилируется, хотя и равнозначна b2 = b2 * 2

Вторя строка, приведенная в примере не скомпилируется из-за автоматического расширения типов в выражениях, так как выражение b2*2 имеет тип int, так как происходит автоматическое расширение типа (целочисленные литералы в выражении всегда int). Третья же строка спокойно скомпилируется, так как в ней сработает неявное приведение типов в совмещенном выражении присваивания.

Boxing/unboxing – преобразование примитивных типов в объекты обертки

Boxing и unboxin – это тоже достаточно большая тема, но она достаточно простая.

По существу boxing и unboxing это преобразование примитивных типов в объекты обертки и обратно.

Для объектов оберток примитивных типов применимо все что было сказано выше.

Об классах обертках упоминалось в таблицах, при разборе каждого из примитивных типов. Но тогда это было лишь упоминание в таблице.

Так вот, для каждого примитивного типа есть его старший брат, и он совсем не примитивный, а является настоящим классом, с полями и методами. И для каждой такой парочки возможно автоматическое преобразование.

С0003

Обычно, если в программе есть много математических вычислений, то лучше пользоваться примитивными типами, так как это быстрее и экономнее с точки зрения ресурсов, но иногда бывает необходимость преобразовать примитивный тип в объект.

Приведу простой пример:

int i3;
byte b2=3;
Byte myB;
myB=b2;
myB++;
b2=myB;
i3=myB;

Если пока не понятно зачем это нужно, то это не страшно, просто завяжите узелок на память.

8 апр. 2015 г.

Примитивные типы Java - boolean

Тип boolean представляет значения истинности. Существует только два возможных значения для данного типа, представляющих два булевых состояния: включено или выключено, да или нет, истина или ложь. Java резервирует слова true и false для представления этих булевых значений они же являются и двумя допустимыми литералами для задания значений переменных типа boolean.

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

Помимо задания логического значения непосредственно через оператор присваивания переменной boolean, значения логического типа возникают в результате различных сравнений, вроде 2 > 3, и используются главным образом в условных операторах и операторах циклов.

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

B0001

Операторы сравнения состоят из операторов равенства, которые проверяют равенство или неравенство значений (== , !=), и операторов отношения (<,>,<=,>=), используемых с упорядоченными типами (числами и символами) при проверке соотношения больше/меньше. Операторы обоих типов возвращают значение типа boolean, поэтому их обычно используют с условными операторами if и циклами while и for для выбора ветви или проверки условия выполнения цикла.

В Java предусмотрены следующие операторы равенства:

Равно (==)

Оператор == возвращает true (истина), если оба его операнда равны; если нет, то возвращается false (ложь).

В случае примитивных операндов он проверяет идентичность самих значений операндов, однако в случае операндов ссылочных типов проверяется, ссылаются ли операнды на один и тот же объект или массив. Другими словами, оператор не проверяет равенство двух разных объектов или массивов. По средством этого оператора не удастся проверить равенство двух различных строк.
Если оператор == сравнивает два числовых или символьных операнда различных типов, то до начала сравнения более узкий операнд преобразуется к типу более широкого. Например, при сравнении short и float величина типа short преобразуется во float до начала сравнения. Для чисел с плавающей точкой специальное отрицательное нулевое значение считается равным обычному положительному нулевому значению. Кроме того, специальное значение NaN (нечисловое) не равно ни одному другому числу, включая само себя. Чтобы проверить, является ли значение с плавающей точкой значением NaN, используйте метод Float.isNan()  или Double.isNan() .

Не равно (!=)

Оператор != прямо противоположен оператору ==. Он возвращает true, если два примитивных операнда имеют разные значения либо если два ссылочных операнда относятся к различным объектам или массивам. В противном случае он возвращает false.

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

Меньше (<)
Возвращает true, если первый операнд меньше второго.

Меньше или равно (<=)
Возвращает true, если первый операнд меньше или равен второму.

Больше (>)
Возвращает true, если первый операнд больше второго.

Больше или равно (>=)
Возвращает true, если первый операнд больше или равен второму.

Булевы операторы

B0002

Как вы уже знаете, операторы сравнения сравнивают операнды и возвращают значение типа boolean. Величины такого типа часто используются в операторах ветвления и цикла. Чтобы выбор ветви и проверка цикла по условиям стали полезнее простого сравнения, можно задействовать булевы (или логические) операторы для объединения нескольких выражений сравнения в одно, более сложное выражение. Для логических операторов нужны операнды со значениями типа boolean. Эти операторы также возвращают значения типа boolean. Существуют такие логические операторы:

Условное И (&&)

Данный оператор выполняет логическую операцию И над операндами. Он возвращает true тогда и только тогда, когда оба операнда истинны. Если один или оба операнда ложны, он возвращает false. Например:

if ( x < 10 && y > 3) // Если оба сравнения истинны

Данный оператор (и все другие логические операторы кроме унарного оператора ! ) имеет меньший приоритет, чем операторы сравнения. Таким образом, вышеприведенная запись кода вполне допустима. Однако некоторые программисты прибегают к помощи круглых скобок, чтобы явно обозначить порядок вычислений:

if (( x < 10) && ( y > 3)) ...

Следует выбрать тот стиль, который вы считаете более удобным для чтения.

Этот оператор называется условным И, потому что он не всегда оценивает второй операнд. Если первый операнд равен false, значение выражения также будет false, каким бы ни было значение второго операнда. Поэтому, для большей эффективности, интерпретатор Java пропускает анализ второго операнда. В выражениях с побочными эффектами этот оператор следует применять осторожно, так как нет гарантии, что будет вычислен второй операнд. С другой стороны, этот оператор позволяет писать такие выражения:

if ( data != null && i < data.length && data[i] != -1) ...

Второе и третье сравнения в данном выражении могут привести к ошибкам, если первое или второе сравнение возвращают false. К счастью, это не проблема, так как поведение оператора && условно.

Условное ИЛИ (||)

Данный оператор выполняет логическую операцию ИЛИ на двух операндах типа boolean. Он возвращает true, если один или оба операнда истинны. Если оба операнда ложны, он возвращает false. Подобно оператору &&, оператор ||  не всегда вычисляет второй операнд. Если первый операнд равен true, значение выражения тоже будет true, каким бы ни было значение второго операнда. В этом случае оператор просто пропускает второй операнд.

Логическое НЕ (!)

Этот унарный оператор меняет boolean значение операнда. Он возвращает false, если применяется к true значению, и true, если задано false значение. Данный оператор можно использовать в таких выражениях:

if (!found)// found является булевой переменной, объявленной где-то ранее

while(!c.isEmpty())// Метод isEmpty() возвращает булево значение

Так как оператор ! является унарным, он имеет высокий приоритет, и зачастую его нужно заключать в круглые скобки:

if (!( x > y && y > z))

Логическое И (&)

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

Логическое ИЛИ (|)

Данный оператор выполняет логическую операцию ИЛИ над двумя операндами типа boolean. Он аналогичен оператору ||, но всегда вычисляет оба операнда, даже если первый операнд является true. Оператор |  почти всегда используется как побитовый оператор для целых чисел, а с операндами типа boolean его применяют достаточно редко.

Логическое исключающее ИЛИ (^)

Для операндов типа boolean данный оператор вычисляет исключающее ИЛИ. Он возвращает true, если только один из двух операндов истинен. Другими словами, он возвращает false, если оба операнда ложны либо истинны. В отличие от операторов && и ||  он всегда вычисляет оба операнда. Оператор ^ намного чаще применяется как побитовый оператор для целых чисел. С операндами типа boolean поведение данного оператора аналогично поведению оператора !=.

Логический оператор присваивания И (&=)

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

Например запись x&=y, аналогична записи x=x&y.

Логический оператор присваивания ИЛИ (|=)

Данный оператор аналогичен оператору &=, но только перед присваиванием совершает операцию логического ИЛИ над операндами.

Например запись x|=y, аналогична записи x=x|y.

Логический оператор присваивания исключающее ИЛИ (^=)

Аналогично двум предыдущим, только перед присваиванием совершает над операндами операцию исключающего логического ИЛИ.

Например запись x^=y, аналогична записи x=x^y.

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

B0003

Ну а теперь немного попрактикуемся на простой программке:

B0004

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

B0005

В этой программе все просто и не нужно что-то объяснять дополнительно. Если что-то не понятно, то читаем еще раз матчасть. А вот для следующей программы нужно будет немного разъяснений.

B0006

Методы compare1 и compare2 сравнивают переданное им значение на то меньше оно семи или больше и возвращают соответствующий логический результат – true или false.

В строке 22 мы присваиваем boolean переменной a результат сравнения логическим И значения возвращаемые методами compare1 и compare2, которым были переданы значения 5 и 3 соответственно. В данном случаем compare1 вернет true, поскольку 5 меньше 7, а compare2 вернет false, поскольку 3 меньше 7. И в данном случае выполнятся оба метода – compare1 и compare2, потому что первый оператор условного И вернул true. Если бы он вернул false, то метод compare2 не выполнялся бы, что и происходит в 26 строке.

Условное ИЛИ уж разбирать не буду там все тоже самое с точностью до наоборот Smile 

Ну и теперь посмотрим на вывод этой программы:

B0007

Надеюсь, что все с этой темой понятно. Если нет, то внимательно вкуриваем мануал и медитируем… медитируем… медитируем…. омммммм…