Преобразование char в int java. Явное преобразование типа

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

Что все это означает? Начнем по порядку. Для простых типов расширение означает, что осуществляется переход от менее емкого типа к более емкому. Например, от типа byte (длина 1 байт) к типу int (длина 4 байта). Такие преобразования безопасны в том смысле, что новый тип всегда гарантированно вмещает в себя все данные, которые хранились в старом типе, и таким образом не происходит потери данных. Именно поэтому компилятор осуществляет его сам, незаметно для разработчика:

byte b=3; int a=b;

В последней строке значение переменной b типа byte будет преобразовано к типу переменной a (то есть, int ) автоматически, никаких специальных действий для этого предпринимать не нужно.

Следующие 19 преобразований являются расширяющими:

  • от byte к short , int , long , float , double
  • от short к int , long , float , double
  • от char к int , long , float , double
  • от int к long , float , double
  • от long к float , double
  • от float к double

Обратите внимание, что нельзя провести преобразование к типу char от типов меньшей или равной длины (byte , short ), или, наоборот, к short от char без потери данных. Это связано с тем, что char , в отличие от остальных целочисленных типов, является беззнаковым.

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

Повторим этот пример:

long a=111111111111L; float f = a; a = (long) f; print(a);

Результатом будет:

Обратное преобразование - сужение - означает, что переход осуществляется от более емкого типа к менее емкому. При таком преобразовании есть риск потерять данные. Например, если число типа int было больше 127, то при приведении его к byte значения битов старше восьмого будут потеряны. В Java такое преобразование должно совершаться явным образом, т.е. программист в коде должен явно указать, что он намеревается осуществить такое преобразование и готов потерять данные.

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

  • от byte к char
  • от short к byte , char
  • от char к byte , short
  • от int к byte , short , char
  • от long к byte , short , char , int
  • от float к byte , short , char , int , long
  • от double к byte , short , char , int , long , float

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

print((byte)383); print((byte)384); print((byte)-384);

Результатом будет:

Видно, что знаковый бит при сужении не оказал никакого влияния, так как был просто отброшен - результат приведения противоположных чисел (384 и -384) оказался одинаковым. Следовательно, может быть потеряно не только точное абсолютное значение, но и знак величины.

Это верно и для типа char :

char c=40000; print((short)c);

Результатом будет:

Сужение дробного типа до целочисленного является более сложной процедурой. Она проводится в два этапа.

На первом шаге дробное значение преобразуется в long , если целевым типом является long , или в int - в противном случае (целевой тип byte , short , char или int ). Для этого исходное дробное число сначала математически округляется в сторону нуля, то есть дробная часть просто отбрасывается.

Например, число 3,84 будет округлено до 3 , а -3,84 превратится в -3 . При этом могут возникнуть особые случаи:

  • если исходное дробное значение является NaN , то результатом первого шага будет 0 выбранного типа (т.е. int или long );
  • если исходное дробное значение является положительной или отрицательной бесконечностью, то результатом первого шага будет, соответственно, максимально или минимально возможное значение для выбранного типа (т.е. для int или long );
  • наконец, если дробное значение было конечной величиной, но в результате округления получилось слишком большое по модулю число для выбранного типа (т.е. для int или long ), то, как и в предыдущем пункте, результатом первого шага будет, соответственно, максимально или минимально возможное значение этого типа. Если же результат округления укладывается в диапазон значений выбранного типа, то он и будет результатом первого шага.
  • и int вполне очевидны - дробные бесконечности преобразовались в, соответственно, минимально и максимально возможные значения этих типов. Результат для следующих трех типов (short , char , byte ) есть, по сути, дальнейшее сужение значений, полученных для int , согласно второму шагу процедуры преобразования. А делается это, как было описано, просто за счет отбрасывания старших битов. Вспомним, что минимально возможное значение в битовом виде представляется как 1000..000 (всего 32 бита для int , то есть единица и 31 ноль). Максимально возможное - 1111..111 (31 единица). Отбрасывая старшие биты, получаем для отрицательной бесконечности результат 0 , одинаковый для всех трех типов. Для положительной же бесконечности получаем результат, все биты которого равняются 1

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

Иногда возникают ситуации, когда у вас есть величина какого-то определенного типа, а вам нужно ее присвоить переменной другого типа. Для некоторых типов это можно проделать и без приведения типа, в таких случаях говорят об автоматическом преобразовании типов. В Java автоматическое преобразование возможно только в том случае, когда точности представления чисел переменной-приемника достаточно для хранения исходного значения. Такое преобразование происходит, например, при занесении литеральной константы или значения переменной типа byte или short в переменную типа int. Это называется расширением (widening ) или повышением (promotion ), поскольку тип меньшей разрядности расширяется (повышается) до большего совместимого типа. Размера типа int всегда достаточно для хранения чисел из диапазона, допустимого для типа byte, поэтому в подобных ситуациях оператора явного приведения типа не требуется. Обратное в большинстве случаев неверно, поэтому для занесения значения типа int в переменную типа byte необходимо использовать оператор приведения типа. Эту процедуру иногда называют сужением (narrowing ), поскольку вы явно сообщаете транслятору, что величину необходимо преобразовать, чтобы она уместилась в переменную нужного вам типа. Для приведения величины к определенному типу перед ней нужно указать этот тип, заключенный в круглые скобки. В приведенном ниже фрагменте кода демонстрируется приведение типа источника (переменной типа int) к типу приемника (переменной типа byte). Если бы при такой операции целое значение выходило за границы допустимого для типа byte диапазона, оно было бы уменьшено путем деления по модулю на допустимый для byte диапазон (результат деления по модулю на число - это остаток от деления на это число),

int а = 100;
byte b = (byte) а;

2.2.1. Автоматическое преобразование типов в выражениях

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

byte а = 40;
byte b = 50;
byte с = 100;
int d = a* b / с ;

Результат промежуточного выражения (а*b) вполне может выйти за диапазон допустимых для типа byte значений. Именно поэтому Java автоматически повышает тип каждой части выражения до типа int, так что для промежуточного результата (а* b) хватает места.

Автоматическое преобразование типа иногда может оказаться причиной неожиданных сообщений транслятора об ошибках. Например, показанный ниже код, хотя и выглядит вполне корректным, приводит к сообщению об ошибке на фазе трансляции. В нем мы пытаемся записать значение 50*2, которое должно прекрасно уместиться в тип byte, в байтовую переменную. Но из-за автоматического преобразования типа результата в int мы получаем сообщение об ошибке от транслятора - ведь при занесении int в byte может произойти потеря точности.

byte b = 50;
b = b* 2:
^ Incompatible type for =. Explicit cast needed to convert int to byte.
(Несовместимый тип для =. Необходимо явное преобразование int в byte)

Исправленный текст:
byte b = 50;
b = (byte) (b* 2);

что приводит к занесению в b правильного значения 100.

Если в выражении используются переменные типов byte, short и int, то во избежание переполнения тип всего выражения автоматически повышается до int. Если же в выражении тип хотя бы одной переменной - long, то и тип всего выражения тоже повышается до long. Не забывайте, что все целые литералы, в конце которых не стоит символ L (или 1), имеют тип int.

Если выражение содержит операнды типа float, то и тип всего выражения автоматически повышается до float. Если же хотя бы один из операндов имеет тип double, то тип всего выражения повышается до double. По умолчанию Java рассматривает все литералы с плавающей точкой как имеющие тип double. Приведенная ниже про1рамма показывает, как повышается тип каждой величины в выражении для достижения соответствия со вторым операндом каждого бинарного оператора.

class Promote {
public static void main (String args ) {
byte b= 42;
char с = "a’;
shorts = 1024;
int i = 50000;
float f = 5.67f;
doubled =.1234;
double result = (f*b) + (i/ c) - (d* s);
System, out. println ((f* b)+ "+ "+ (i / c)+ " -" + (d* s));
System, out. println ("result = "+ result); }
}

Подвыражение f*b - это число типа float, умноженное на число типа byte, поэтому его тип автоматически повышается до float. Тип следующего подвыражения i / с (int, деленный на char) повышается до int. Аналогично этому тип подвыражения d*s (double, умноженный на short) повышается до double. На следующем шаге вычислений мы имеем дело с тремя промежуточными результатами типов float, int и double. Сначала при сложении первых двух тип int повышается до float и получается результат типа float. При вычитании из него значения типа double тип результата повышается до double. Окончательный результат всего выражения - значение типа double.

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

class SimpleTypes {
public static void main(String args ) {
byte b = 0x55;
short s = 0x55ff;
int i = 1000000;
long l = 0xffffffffL;
char с = ’a’;
float f= .25f;
double d = .00001234;
boolean bool = true;
System.out.println("byte b = " + b);
System.out.println("short s = " +s);
System.out.println("int i =” + i);
System.out.println("long 1 = " + l);
System.out.println("char с =” + с );
System.out.println("float f = " + f);
System.out.println("double d = " + d);
System.out.println("boolean bool =” + bool); }
}

Запустив эту программу, вы должны получить результат, показанный ниже:

byte b = 85
shorts = 22015
int i = 1000000
long 1 = 4294967295
char с = a
float f = 0.25
double d=1.234e-005
boolean bool = true

Обратите внимание на то, что целые числа печатаются в десятичном представлении, хотя мы задавали значения некоторых из них в шестнадцатиричном формате.

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

Приведение типов в арифметических выражениях выполняется автоматически.

byte->short->int->long->float->double

Если операнды a и b комбинируются бинарным оператором (ниже мы это обсудим), перед его исполнением оба операнда преобразуются в данные одного типа следующим образом:

  • Если один из операторов имеет тип double, второй также преобразуется в double;
  • Если один из операторов имеет тип float, второй также преобразуется в float;
  • Если один из операторов имеет тип long, второй также преобразуется в long;
  • Если один из операторов имеет тип int, второй также преобразуется в int;

Разрешенные преобразования типов

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

Для сужения каст необходимо сделать явным. Например: byte b = (byte)128; прикастили инт к байт типу.

Предлагаю сделать несколько примеров.

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

  • Имена переменных не могут начинаться с цифры, в именах не могут использоваться как символы арифметических и логических операторов, а также символ ‘#’.
  • Применение символов ‘$’ или ‘_’ приемлемо, включая первую позицию и имя.
  • Переменная примитивного типа, объявленная как член класса (глобальная переменная), по умолчанию задается нулем.
  • Если переменная объявлена как локальная переменная в методе, перед использованием она должна обязательно быть проинициализирована. Так как локальные переменные не изициализируются по умолчанию. Это значит, что Вы не можете объявить локальную переменную и оставить ее без инициализации. То есть вот так: int i;. Если Вы так сделаете в методе, компилятор попросит Вас задать значение по умолчанию в то время, как создав такую переменную как член класса (глобальную) компилятор сам задаст ей значение 0.
  • Область действия и время жизни переменной ограничено блоком {}, в котором она объявлена. Если Вы создали переменную внутри метода (как мы это делали в примерах), то Вы не сможете использовать ее вне метода, так как метод ограничен скобками {}. Глобальную переменную видно во всех блоках.
  • Также запрещено использовать зарезервированные слова java. Весь перечень ключевых слов можно увидеть на картинке ниже.

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

Java имеет несколько типов операторов: простое присваивание, арифметическое, унарное, равноправное и реляционное, условное, сравнение типов, побитовое и битовое смещение.

Много умных слов, но очень просто все объяснит вот эта картинка:

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

    public class OperatorsInJava {

    int a = 5 ;

    int b = 6 ;

    int sum = a + b;

    int difference = a - b;

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

Каждый базовый тип данных занимает определенное количество байт памяти. Это накладывает ограничение на операции, в которые вовлечены различные типы данных. Рассмотрим следующий пример:

Int a = 4; byte b = a; // ! Ошибка

В данном коде мы столкнемся с ошибкой. Хотя и тип byte, и тип int представляют целые числа. Более того значение переменной a, которое присваивается переменной типа byte, вполне укладывается в диапазон значений для типа byte (от -128 до 127). Тем не менее мы сталкиваемся с ошибкой на этапе компиляции. Поскольку в данном случае мы пытаемся присвоить некоторые данные, которые занимают 4 байта, переменной, которая занимет всего один байт.

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

Int a = 4; byte b = (byte)a; // преобразование типов: от типа int к типу byte System.out.println(b); // 4

Операция преобразования типов предполагает указание в скобках того типа, к которому надо преобразовать значение. Например, в случае операции (byte)a , идет преобразование данных типа int в тип byte. В итоге мы получим значение типа byte.

Явные и неявные преобразования

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

Автоматические преобразования

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

Автоматически без каких-либо проблем производятся расширяющие преобразования (widening) - они расширяют представление объекта в памяти. Например:

Byte b = 7; int d = b; // преобразование от byte к int

В данном случае значение типа byte, которое занимает в памяти 1 байт, расширяется до типа int, которое занимает 4 байта.

Расширяющие автоматические преобразования представлены следующими цепочками:

byte -> short -> int -> long

int -> double

short -> float -> double

char -> int

Автоматические преобразования с потерей точности

Некоторые преобразования могут производиться автоматически между типами данных одинаковой разрядности или даже от типа данных с большей разрядностью к типа с меньшей разрядностью. Это следующие цепочки преобразований: int -> float , long -> float и long -> double произволятся без ошибок, но при преобразовании мы можем столкнуться с потерей информации.

Например:

Int a = 2147483647; float b = a; // от типа int к типу float System.out.println(b); // 2.14748365E9

Явные преобразования

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

Long a = 4; int b = (int) a;

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

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

Int a = 5; byte b = (byte) a; System.out.println(b); // 5

Число 5 вполне укладывается в диапазон значений типа byte, поэтому после преобразования переменная b будет равна 5. Но что будет в следующем случае:

Int a = 258; byte b = (byte) a; System.out.println(b); // 2

Результатом будет число 2. В данном случае число 258 вне диапазона для типа byte (от -128 до 127), поэтому произойдет усечение значения. Почему результатом будет именно число 2?

Число a, которое равно 258, в двоичном системе будет равно 00000000 00000000 00000001 00000010 . Значения типа byte занимают в памяти только 8 бит. Поэтому двоичное представление числа int усекается до 8 правых разрядов, то есть 00000010 , что в десятичной системе дает число 2.

Усечение рациональных чисел до целых

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

Double a = 56.9898; int b = (int)a;

Здесь значение числа b будет равно 56, несмотря на то, что число 57 было бы ближе к 56.9898. Чтобы избежать подобных казусов, надо применять функцию округления, которая есть в математической библиотеке Java:

Double a = 56.9898; int b = (int)Math.round(a);

Преобразования при операциях

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

    если один из операндов операции относится к типу double , то и второй операнд преобразуется к типу double

    если предыдущее условие не соблюдено, а один из операндов операции относится к типу float , то и второй операнд преобразуется к типу float

    если предыдущие условия не соблюдены, один из операндов операции относится к типу long , то и второй операнд преобразуется к типу long

    иначе все операнды операции преобразуются к типу int

Примеры преобразований:

Int a = 3; double b = 4.6; double c = a+b;

Так как в операции участвует значение типа double, то и другое значение приводится к типу double и сумма двух значений a+b будет представлять тип double.

Другой пример:

Byte a = 3; short b = 4; byte c = (byte)(a+b);

Две переменных типа byte и short (не double, float или long), поэтому при сложении они преобразуются к типу int , и их сумма a+b представляет значение типа int. Поэтому если затем мы присваиваем эту сумму переменной типа byte, то нам опять надо сделать преобразование типов к byte.

Если в операциях участвуют данные типа char, то они преобразуются в int:

Int d = "a" + 5; System.out.println(d); // 102

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

Каким образом работает явное приведение типов

В вашем примере показано восходящее преобразование (Upcasting ):

List coll = new ArrayList();

На русский язык переводится так: создай ворону, типа птицы. Создай динамический массив, типа лист. В большинстве ситуаций восходящее преобразование совершенно не нужно .
Однако, приведение типов работает на собеседованиях, когда вам дают вопросы на наследование. К примеру, сайт quizful.net вообще содержит в себе множество вопросов на приведение типов. Поэтому разъясню особенности, которые знаю.

Итак, в вышеприведенном примере мы создали объект типа ArrayList , а ссылка типа List . Запомните аксиомы для этого способа:

1. Ссылку можно указать на любого родителя. Даже очень давнего. То есть, можно привести ссылку coll даже к типу Object . Компилятор пропустит любую ссылку на класс родителя, или родителя-родителя, или родителя-родителя...родителя

2. Обращение к полю - всегда идёт возврат поля ссылки, не поля объекта. Если такого поля нет в классе-ссылке будет ошибка компиляции.

Class A{ int x = 2; //Поле родителя } Class B extends A { int x = 3; //Поле которое должно перекрыть родительское int y = 5; //Поле, которого нет в родительском классе. } Class Test{ public static void main(String args) { A ab = new B(); //Восходящее преобразование System.out.println("Int x = " + ab.x); } }

Вернет Int x = 2 . Если вы попробуете обратиться к полю объекта:

System.out.println("Int y = " + ab.y); //Ошибка компилляции

Ваш компилятор скажет, что вы не правы, так как он по ссылке (A ab) не видит такого поля. Всё вышесказанное сохраняет силу, даже если ваши поля пометить модификаторами static.

3. Обращение к нестатическому методу: в этом случае вернёт метод объекта. Но при обращении к статическому методу - возвращает метод ссылки.

Class D{ public void doSome(){ //Нестатический метод System.out.println("Nonstatic doSome from D"); } public static void Action(){ //Статический метод System.out.println("static Action from D"); } } public class Okey extends D{ public void doSome(){ System.out.println("doSome from Okey"); } public static void Action(){ System.out.println("static Action from Okey"); } public static void main(String args) { D o=new Okey(); o.doSome(); //Из класса Okey o.Action(); //Из класса D } }

Nonstatic doSome from Okey

static Action from D

Разгадка проста, нестатический метод - это метод объекта, статический - метод класса. Когда мы вызываем не статический метод - компилятор понимает так: летай как ворона. Когда мы вызываем статический - буквально, летай как птица.

4. Если идёт вызов метода, который описан в классе объекта, но не описан в классе ссылки - пойдёт ошибка компилляции. Потому что, вызов метода происходит по ссылке:

Class A {} Class B extends A { void someMethod(){}; public static void main(String args) { A ab = new B(); ab.someMethod(); //Ошибка компилляции. } }

5. Конструктор объекта (при создании командой new) работает также, как если давать ссылку на свой класс.