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

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

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

    • Преобразование типа int в тип long требует во время выполнения программы расширения знака 32-битового целого значения до 64-битового целого. Потери информации при этом не происходит.
    • Преобразование типа float в тип long требует во время выполнения программы нетривиального преобразования 32-битового плавающего значения в 64-битовое целое. В зависимости от исходного значения может произойти или не произойти потеря информации.
    • Преобразование типа Thread в тип Object не требует никаких действий: поскольку класс Thread является потомком класса Object , любая ссылка на объект типа Thread автоматически является ссылкой на объект типа Object .
    • Преобразование типа Object в тип Thread требует проверки в период исполнения. Если преобразуемая ссылка действительно является ссылкой на объект типа Thread , то она возвращается как результат преобразования, в противном случае генерируется исключение.

    5.4.1.1. Расширяющие преобразования чисел

    Расширяющие преобразования чисел — это преобразования числового типа в "больший" числовой тип, которые считаются безопасными, т. к. не приводят к потере величины преобразуемого значения. Такими преобразованиями в Java являются:

    • преобразования 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 .

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

    Class Test { public static void main(String args) { int bigNumber = 1234567890; float approximate = bigNumber; System.out.println(approximate); } }

    выведет на экран строку 1234567936 . Это связано с тем, что при преобразовании int в float результирующее значение равно 1.2345679E9 из-за того, что мантисса чисел типа float вмещает только 8 десятичных цифр (здесь для правильной работы следует использовать преобразование к типу double ). Тем не менее, исполняющая система никогда не генерирует ошибок при выполнении перечисленных преобразований.

    5.4.1.2. Сужающие преобразования чисел

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

    • преобразования byte в char ;
    • преобразования short в byte и char ;
    • преобразования int в byte , short и char ;
    • преобразования long в byte , short , int и char ;
    • преобразования float в byte , short , int , long и char ;
    • преобразования double в byte , short , int , long , float и char ;

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

    5.4.1.3. Расширяющие преобразования ссылок

    Расширяющие преобразования ссылок — это преобразования производных ссылочных типов в типы их предков, которые не требуют никаких действий на этапе исполнения и никогда не генерируют ошибок. Такими преобразованиями в Java являются:

    • преобразование любого класса или интерфейса в его предка (в частности, в тип Object);
    • преобразование класса в интерфейс, который он реализует;
    • преобразование любого массива в тип Object или тип Cloneable ;
    • преобразование массива типа S в массив типа T, если S и T — ссылочные типы, и преобразование S в T является расширяющим;
    • преобразование нулевого типа в любой ссылочной тип.

    5.4.1.4. Сужающие преобразования ссылок

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

    • преобразование любого класса в его потомка (в частности, преобразование типа Object в любой другой класс);
    • преобразование класса в интерфейс, когда класс не является финальным и не реализует данный интерфейс (в частности, преобразование типа Object в любой интерфейс);
    • преобразование типа Object в любой массив;
    • преобразование любого интерфейса в класс, который не является финальным;
    • преобразование любого интерфейса в класс, который является финальным и реализует данный интерфейс;
    • преобразование интерфейса J в интерфейс K, когда J не является потомком K, и не существует метода, декларированного и в J, и в K с одинаковой сигнатурой, но разными типами результата;
    • преобразование массива типа S в массив типа T, если S и T — ссылочные типы, и преобразование S в T является сужающим.

    5.4.1.5. Преобразования в строки

    Любое выражение в Java, включая null , может быть преобразовано в тип String .

    5.4.1.6. Недопустимые преобразования

    Следующие преобразования типов в Java запрещены:

    • преобразование любого ссылочного типа в любой примитивный тип;
    • преобразование любого примитивного типа в любой ссылочной тип, кроме типа String ;
    • преобразование нулевого типа в любой примитивный тип;
    • преобразования в нулевой тип или тип boolean ;
    • преобразования типа boolean в любой другой тип, кроме типа String ;
    • преобразование одного класса в другой, если ни один из них не является предком другого (кроме преобразования в тип String);
    • преобразование класса в интерфейс, если класс является финальным и не реализует данный интерфейс;
    • преобразование класса в массив, если класс отличен от Object ;
    • преобразование интерфейс в класс, который является финальным и не реализует данный интерфейс (кроме преобразования в тип String);
    • преобразование интерфейса J в интерфейс K, если существует метод, декларированный и в J, и в K с одинаковой сигнатурой, но разными типами результата;
    • преобразование массива в класс, отличный от Object и String ;
    • преобразование массива в интерфейс, отличный от Cloneable ;
    • преобразование массива типа S в массив типа T, если преобразование S в T является запрещенным

    5.4.2. Контексты преобразований

    5.4.2.1. Преобразование при присваивании

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

    • переменная имеет тип byte , short или char ;
    • значением выражения является константа типа int , которая попадает в диапазон возможных значений переменной.

    Например, оператор byte x = 123; допустим, поскольку константа 123 (имеющая тип int ) лежит в диапазоне допустимых значений типа byte .

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

    Short s = 123; char c = s; // генерирует ошибку компиляции

    приведет к генерации ошибки, поскольку типы char и short несовместимы по присваиванию согласно данных выше определениям (первый реализован 16-битовыми словами без знака, а второй — со знаком).

    5.4.2.2. Преобразование аргументов метода

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

    Class Test { static int m(byte a, int b) { return a + b; } static int m(short a, short b) { return a - b; } public static void main(String args) { System.out.println(m(1, 2) ); // генерирует ошибку компиляции } }

    Здесь класс Test содержит два одноименных метода, которые различаются только типами параметров. Если бы сужающие преобразования аргументов были в Java разрешены, то исполняющей системе пришлось бы определять, к какому из этих методов относится вызов m(1, 2) . Чтобы избежать подобных двусмысленностей, разработчики языка решили проблему радикально: они запретили подобные вызовы методов. В данной ситуации для вызова, к примеру, первого метода мы должны явно указать тип первого операнда (второй по умолчанию уже имеет тип int ), а именно m((byte)1, 2) .

    5.4.2.3. Преобразование в строку

    Преобразование в строку происходит только в одном случае: когда бинарная операция + применяется к двум операндам, один из которых имеет тип String . В этой ситуации второй операнд также преобразуется к типу String , и результатом операции является конкатенация полученных строк. Подробнее этот процесс описан в гл. 5.14 .

    5.4.2.4. Явное преобразование типа

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

    5.4.3. Преобразования типов числовых операндов

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

    Перед выполнением унарной операции:

    • если операнд имеет тип byte , short или char , он преобразуется к типу int ;
    • в остальных случаях его тип не изменяется.

    Перед выполнением бинарной операции:

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

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

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

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

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

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

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

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

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

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

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

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

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

    Стоит немного пояснить почему, к примеру тип 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. Однако типы с плавающей точкой являются приближенными числами и не всегда могут содержать так много значащих разрядов в мантиссе, как целые типы.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Последнее обновление: 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

    Иногда возникают ситуации, когда необходимо переменной одного типа присвоить значение переменной другого типа. Например:

    Пример 1. Присвоение значения переменной одного типа другому

    int i = 11; byte b = 22; i = b;

    В Java существует 2 типа преобразований - автоматическое преобразование (неявное) и приведение типов (явное преобразование) .

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

    Рассмотрим сначала автоматическое преобразование. Если оба типа совместимы, их преобразование будет выполнено в Java автоматически. Например, значение типа byte всегда можно присвоить переменной типа int , как это показано в примере 1.

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

    • оба типа должны быть совместимы
    • длина целевого типа должна быть больше длины исходного типа

    В этом случае происходит преобразование с расширением .

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

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

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

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

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

    2. Приведение типов

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

    Чтобы выполнить преобразование двух несовместимых типов данных, нужно воспользоваться приведением типов. Приведение - это всего лишь явное преобразование типов. Общая форма приведения типов имеет следующий вид:

    (целевой_тип) значение

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

    Пример 2. Приведение типов

    Например, в следующем фрагменте кода тип int приводится к типу byte :

    Int i = 11; byte b = 22; b = (byte) i;

    Пример 3. Преобразования значений с плавающей точкой в целые числа

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

    Double d = 3.89; int a = (int) d; //Результат будет 3

    Пример 4. Преобразования более емкого целого типа к менее емкому целому числу

    При приведении более емкого целого типа к менее емкому старшие биты просто отбрасываются:

    Int i = 323; byte b = (byte) i; //Результат будет 67

    Пример 5. Преобразования более емкого значения с плавающей точкой к менее емкому целому числу

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

    Double d = 389889877779.89; short s = (short) d; //Результат будет -1

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

    Помимо операций присваивания, определенное преобразование типов может выполняться и в выражениях.

    В языке Java действуют следующие правила:

    1. Если один операнд имеет тип double double .
    2. float , другой тоже преобразуется к типу float .
    3. Иначе, если один операнд имеет тип long , другой тоже преобразуется к типу long .
    4. Иначе оба операнда преобразуются к типу int .
    5. В выражениях совмещенного присваивания (+=,-=,*=,/=) нет необходимости делать приведение.

    Приведем пример:

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

    При умножении переменной b1 (byte ) на 2 (int ) результат будет типа int . Поэтому при попытке присвоить результат в переменную b2 (byte ) возникнет ошибка компиляции. Но при использовании совмещенной операции присваивания (*=), такой проблемы не возникнет.

    Byte b1 = 1; byte b2 = 2 * b1; //Ошибка компиляции int i1 = 2 * b1; b2 *= 2;

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

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

    Public class IntegerDemo1 { public static void main(String args) { byte b1 = 50, b2 = -99; short k = b1 + b2; //ошибка компиляции System.out.println("k=" + k); } }

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

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

    Public class IntegerDemo2 { public static void main(String args) { byte b1 = 50, b2 = -99; b1 += b2; System.out.println("b1=" + b1); } }

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

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

    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;