Свежие записи из блогов Widowmaker1984

Widowmaker1984, блог «Java для собеседований»

Вопрос 15 (побитовые операции)

"Какая арифметическая операция выполняется при побитовом сдвиге вправо/влево?"

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

2. Список побитовых операций (без вариаций с присвоением):
~ (побитовое унарное отрицание (NOT))
&(побитовое И (AND))
| (побитовое ИЛИ (OR))
^ (побитовое исключающее ИЛИ (XOR))
>> (сдвиг вправо)
<< (сдвиг влево)
>>> (сдвиг вправо с заполнением нулями)

скрытый текст2.1. Примеры выполнения побитовых операций:
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a = 1100 0011

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

3.1.При применении оператора сдвига влево к операнду типа int биты теряются, как только они сдвигаются за пределы 31 позиции. Если операнд имеет тип long, биты теряются после сдвига за пределы 63 позиции.

3.2. Автоматическое повышение типа, используемое в Java, может привести к странным результатам при выполнении сдвига в значениях типа byte, short. При вычислении выражений тип значений byte и short повышается до типа int. Это означает, что результатом выполнения сдвига влево значения типа byte или short будет значение int, и сдвинутые влево позиции не будут отброшены до тех пор, пока они не будут сдвинуты за пределы 31 позиции. Более того, при повышении до типа int отрицательное значение типа byte или short получит дополнительный знаковый разряд. Следовательно, старшие биты будут заполнены единицами. Поэтому при выполнении сдвига влево в значении типа byte сначала будет повышение до типа int и лишь затем сдвиг. Поэтому для получения требуемого сдвинутого значения типа byte необходимо отбросить три старших байта результата. Простейший способ достижения этого - обратное приведение результата к типу byte.

byte x = 64;
byte y;
int i;
i = x << 2; // сдвиг влево
y = (byte) (x << 2); // сдвиг влево с приведением

Результат:
i равно: 256
y равно: 0

Поскольку для выполнения вычислений тип переменной повышается до int, сдвиг влево на две позиции значение 64 (0100 0000) приводит к значению 256 (1 0000 0000). Однако, переменная y содержит значение не 256, а 0, поскольку после сдвига крайний единичный бит оказывается сдвинутым за пределы допустимого диапазона.

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

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

4.1. Примеры выполнения операций сдвига:
A << 2 равно 0011 1100 << 2 равно 1111 0000 равно 240
A >> 2 равно 0011 1100 >> 2 равно 0000 1111 равно 15

4.2. Иногда требуется, чтобы при сдвиге вправо расширение знакового разряда не происходило, а освобождающиеся левые разряды просто заполнялись бы нулями. Для этого используется оператор >>>:

int x = -17, z1, z2;
// x: 11111111 11111111 11111111 11101111
z1 = x >> 2;
// z1 = -5: 11111111 11111111 11111111 11111011
z2 = x >>> 2;
// z2 = 1073741819 : 00111111 11111111 11111111 11111011

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

4.3.1. Обратите внимание, что результат сдвига вправо значения -1 всегда равен -1, поскольку дополнительные знаковые разряды добавляют новые единицы к старшим битам.

5. Как и бинарные арифметические операторы побитовые операции имеют составные формы, которые объединяет побитовый оператор с оператором присваивания.

&=(побитовое И (AND) с присваиванием)
|= (побитовое ИЛИ (OR) с присваиванием)
^= (побитовое исключающее ИЛИ (XOR) с присваиванием)
>>= (сдвиг вправо с присваиванием)
<<= (сдвиг влево с присваиванием)
>>>= (сдвиг вправо с заполнением нулями с присваиванием)

Widowmaker1984, блог «Дом на краю кладбища»

Второй блог про Java: оглавление и планы

Привожу свой второй блога про Java в более-менее цивилизованный вид. Добавил оглавление с разбиением по частям, текстом вопросов и ссылками на ответы.

 

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

 

По первой части надо написать до 25 вопроса; дальше пойдут уже более интересные темы вроде ООП, обработки исключений, и тому подобного.

Widowmaker1984, блог «Java для собеседований»

Вопрос 14 (оператор присвоения, отношения, арифметические бинарные операции)

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

1.1 В Java допустимо многократное использование операции присваивания в одном выражении. Например, x1 = x2 = x3 = 0;
Эта операция выполняется справа налево, т.е. сначала переменной x3 присваивается значение 0, затем переменной x2 присваивается значение переменной x3 (0), и, наконец, переменной x1 присваивается значение переменной x2 (0).

скрытый текст2. Арифметические бинарные операции. Используются для вычислений так же как в алгебре. Допустимые операнды должны иметь числовые типы. Например, исполь­зовать эти операторы для работы с логическими типами нельзя, но можно для работы с типом char.

В Java определены следующие арифметические бинарные операции:
- сложение "+";
- вычитание "-";
- умножение "*";
- деление "/";
- вычисление остатка от деления целых чисел "%".

2.1. Вычисление остатка возвращает остаток от деления первого числа на второе, причем результат будет иметь тот же знак, что и делимое), например, 5%3 будет равно 2, а (-7)%(-4) будет равен -3. Может использоваться и для вещественных переменных (типа float или double.

2.2. Для каждого из арифметических операторов есть форма, в которой одновременно с заданной операцией выполняется присваивание. Это +=; -= , *= ; += ; %=.

3. Для того, чтобы можно было сравнивать два значения, в Java имеется набор операторов, описывающих отношение и равенство. Значения любых типов, включая целые и вещественные числа, символы, логические значения и ссылки, можно сравнивать, используя оператор проверки на равенство == и неравенство !=. Один знак (=) — это оператор присваи­вания.

3.1. Список операторов отношения:
- равно (==)
- не равно (!=)
- больше (>)
- меньше (<)
- больше или равно (>=)
- меньше или равно (<=)

Widowmaker1984, блог «Java для собеседований»

Вопрос 14 (унарные операции)

"Какие унарные операции вы знаете?"

1. Унарные операции — это операции, которые выполняются над одним операндом (бинарные над двумя, тринарные — над тримя).

2. В Java имеются следующие семь унарных операций:
+ (унарный плюс)
- (унарный минус)
++ (префиксный либо постфиксный инкремент; только для целых)
-- (префиксный либо постфиксный декремент; только для целых)
~ (побитовое инвертирование; только для целых)
! (логическое отрицание; только для boolean)
( ) (приведение типа)

2.1. Унарные операторы + и - отличаются от бинарных операторов + и -, которые трактуются, как сложение и вычитание. Унарный + не имеет никакого эффекта, кроме подчёркивания положительной природы численного литерала. Унарный - меняет знак выражения с плюса на минус и наоборот.

скрытый текст2.2. Инкремент увеличивает значение переменной на 1; декремент уменьшает. По сути, являются сокращенным вариантом записи для сложения или вычитания из операнда единицы. В зависимости от того, используется операция в префиксной или постфиксной форме (стоит до или после переменной) меняется порядок выполнения операций: инкремент/декремент выполняются до или после прочих.

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

2.3. Побитовое инвертирование. Каждый примитивный тип Java представляется в виртуальной машине так, что представление не зависит от платформы. Это означает, что битовый шаблон, используемый для представления отдельного числа, будет всегда тем же самым. Таким образом и манипулирование битами — процесс более эффективный, в силу независимости от платформы. Побитовое инвертирование означает, что в двоичном представлении числа 0 заменяется на 1, а 1 — на 0. Например, применение этого оператора к байту с содержимым 00001111 даст 11110000.

2.4. Оператор ! инвертирует логическое значение выражения. Например, !true = false. Этот оператор часто используется в тестовой части if () конструкции.

2.5. Приведение типа используется для явной конвертации выражения в заданный тип. Операция возможна только для допустимых типов. И во время компиляции, и во время выполнения программы, приведение типа проверяется на корректность.

2.5.1.Приведение типа применяется для изменения типа значений примитивного типа. Например, можем форсировать конвертацию double к int как, например, в следующем фрагменте:

int circum = (int)(Math.PI * diameter);

Здесь приведение типа выражается фрагментом (int). Если бы этот фрагмент отсутствовал, компилятор бы выдал ошибку, поскольку значение типа double, возвращаемое арифметическим выражением не может быть точно представлено значением типа int, к которому присваивается. Присвоение типа — это способ, которым программист говорит компилятору: "Я знаю, что такое присвоение может быть рискованным, но верь мне, ведь я — специалист". Конечно, если результат при этом теряет точность так, что программа не функционирует должным образом, - это ответственность программиста.

2.5.2. Присвоение типа может быть применено и к ссылочным типам. Это типично, например, для случаев, когда используются контейнеры типа Vector. Если вы помещаете объект типа String в Vector, то когда вы его извлекаете, тип, возвращаемый методом elementAt(), будет Object. Для использования извлечённого объекта как String нужно применить приведение типа, как, например, в следующем фрагменте кода:

Vector v = new Vector();
v.add ("Hello");
String s = (String)v.get(0);

Widowmaker1984, блог «Java для собеседований»

Вопросы 10, 11, 12, 13 (логические операции)

"Какие логические операции и операторы вы знаете?"
"В чем разница краткой и полной схемы записи логических операторов?"
"Что такое таблица истинности?"
"Что такое тернарный оператор выбора?"

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

1.1. Список логических операторов Java:
& Логическое AND (И)
&& Сокращённое AND
| Логическое OR (ИЛИ)
|| Сокращённое OR
^ Логическое XOR (исключающее OR (ИЛИ))
! Логическое унарное NOT (НЕ)
&= AND с присваиванием
|= OR с присваиванием
^= XOR с присваиванием
== Равно
!= Не равно
?: Тернарный (троичный) условный оператор

скрытый текст2. Результаты выполнения логических операторов (AND, OR, XOR, NOT).
AND возвращает true, если оба операнда равны true. OR, если хотя бы один. XOR если только один. NOT возвращает true, если операнд false; и наоборот.

2.1. Таблица истинности — это таблица, описывающая логическую функцию (значения операндов и возвращаемые значения).
http://developer.alexanderklimov.ru/android/java/logic_operators.php

3. Кроме стандартных операторов AND (&) и OR (|) существуют сокращённые операторы && и ||. Если взглянуть на таблицу истинности, видно, что результат выполнения оператора OR всегда равен true, когда значение левого операнда равно true. Аналогично, результат выполнения оператора AND всегда равен false, когда значение первого операнда равно false. Поэтому при использовании сокращенных операторов (в отличие от "стандартных") значение второго операнда не вычисляется, если результат можно определить уже по первому.

3.1. Это становится удобным в случаях, когда значение правого операнда зависит от значения левого. Например, if (mouse != 0 && weight / mouse < 1000) в отличие от версии со стандартным AND не приведет к ошибке, если значение mouse равняется 0.

3.2. Сокращённые варианты операторов AND и OR принято использовать в ситуациях, когда требуются именно операторы булевой логики. А их односимвольные родственники обычно используются для побитовых операций.

4. В языке Java есть также специальный тернарный условный оператор, которым можно заменить определённые типы операторов if-then-else - это оператор ?:

4.1. Тернарный оператор использует три операнда. Выражение записывается в следующей форме:
[логическоеУсловие] ? [выражение1] : [ выражение2]

Если логическоеУсловие равно true, то вычисляется выражение1, и его результат становится результатом выполнения всего оператора. Если же логическоеУсловие равно false, вычисляется выражение2, и его значение становится результатом работы оператора. Оба операнда выражение1 и выражение2 должны возвращать значение одинакового (или совместимого) типа.

4.2. Пример использования оператора выбора для вычисления значения по модулю:
absval = val < 0 ? -val : val;
Аналогичный код с использованием if:
if(val < 0) absval = -val;
else absval = val;

Widowmaker1984, блог «Java для собеседований»

Вопрос 9 (функция main)

"Что вы знаете о функции main, какие обязательные условия ее определения?"

1. Всякое приложение должно содержать метод с именем main . Он может быть один на все приложение или содержаться в нескольких его классах. Метод main() записывается как обычный метод, может содержать любые описания и действия, но он обязательно должен быть открытым (public), статическим (static), не иметь возвращаемого значения (void). Его аргументом обязательно должен быть массив строк (String[]). По традиции этот массив называют args, хотя имя может быть любым. Туда передаются параметры из командной строки, с помощью которых можно предоставить приложению нужную информацию или настроить его для запуска нужным образом.

public static void main (String args []) {}

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

1.1.1 Когда вы запускается java.exe (или javaw.exe в Windows), на самом деле происходит несколько вызовов Java Native Interface (JNI). JNI - это инструмент, который мы используем, когда нам приходится соединяться между миром виртуальной машины (JVM) и миром C, С++ и т.д. java.exe - это простое приложение C, которое анализирует командную строку, создает новый массив String в JVM для хранения этих аргументов, анализирует имя класса, указанное при вызове как содержащее main(), использует вызовы JNI для поиска самого метода main(), затем вызывает метод main(), передавая вновь созданный строковый массив в качестве параметра.

1.2. Иногда метод main() заносят в каждый класс с целью отладки. В этом случае в метод main() включают тесты для проверки работы всех методов класса.

2. При вызове интерпретатора java можно передать в метод main() несколько параметров, которые интерпретатор заносит в массив строк. Эти параметры перечисляются в строке вызова java через пробел сразу после имени класса. Если параметр содержит пробелы, надо заключить его в кавычки. Кавычки не будут включены в параметр, это только ограничители.

2.1. Примеры вызова main из класса Echo и соответствующих им значений параметра args:

java Echo Hello XXI century: массив из трех элементов Hello, XXI, century
java Echo "Hello XXI century": массив из одного элемента Hello XXI century
java Echo Hello "XXI century": массив из двух элементов Hello, XXI century

Widowmaker1984, блог «Дом на краю кладбища»

Пять (и еще несколько) фактов про группу "Немного нервно"

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

 

2. Про жанр. Однажды возмущенная фанатка тяжелой музыки из Запорожья сказала НН: "Да вы же играете какой-то средневековый шансон!". После этого группа играла средневековый шансон, пока не перешла на более цивилизованный жанр dream folk, самое удивительное в котором то, что он на самом деле существует.

 

3. Жизнь солистки группы, Екатерины Гопенко, навсегда изменил встреченный в юности бомж. Зайдя в проезжавший по рабочему району трамвай, он запел: "Видишь, там на горе возвышается крест". "Надо же," — подумала Екатерина. — "Какую удивительную песню сочинил бомж". И полюбила с тех пор перчатки без пальцев и песни о странном.

 

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

 

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

 

Что касается меня, то мое знакомство с НН началось с того, что как-то раз я решил проверить, смогу ли получить удовольствие от концерта совсем незнакомой группы. И именно они оказались первым, что мне принес Youtube. В общем, это определенно была судьба.

 

Widowmaker1984, блог «Java для собеседований»

Вопрос 8 (передача значения переменной)

"Как передается значение переменной (по ссылке/значению)?"

1. Термины семантики "pass-by-value" и семантики "pass-by-reference" относятся к параметрам функции.

1.1. Pass-by-value (передача параметра по значению). Действительный параметр (или выражение в аргументе) полностью вычисляется, и значение результата копируется в отдельную ячейку памяти, предназначенную для хранения значения этого параметра во время выполнения функции. То есть функция имеет дело с копией переменной, которую передали в функцию в качестве параметра. Место в памяти под параметр - обычно кусок runtime-стека в приложении (обрабатываемый Java), но другие языки могут выбрать хранение параметра в другом месте.

1.2. Pass-by-reference (передача параметра по ссылке). Формальный параметр действует просто как псевдоним (alias) реального параметра. Функция или метод, которая использует формальный параметр (для чтения или записи), в действительности использует актуальный параметр, существующий где-то вне функции.

скрытый текст2. Спецификация Java утверждает, что всегда передача параметров происходит по принципу pass-by-value. В Java нет такого понятия, как "pass-by-reference". Это очевидно для примитивов; в Java вообще нет такого понятия, как указатель/ссылка на примитив. Но объекты также не передаются по ссылке. Вместо этого передаются значения указателей на них. Т.е. скажем при вызове foo(d) в функцию foo передается значение указателя d, а не объект, на который он указывает. Нет никакого способа передать в качестве параметра сам объект.

2.1. То есть данные объявления идентичны:
//Java: пример объявления указателя.
Dog d;
//C++: пример объявления указателя.
Dog *d;

Используются указатели при этом тоже одинаково:
//Java: вызов метода по указателю.
d.setName("Fifi");
//C++: вызов метода по указателю.
d->setName("Fifi");

2.2. Ключевым понятием для понимания является следующее: на Java переменная myDog не является собакой, это указатель на собаку. То есть, если здесь:

Dog myDog = new Dog("Rover");
foo(myDog);

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

2.3. Предположим, что объект Dog находится в памяти по адресу 42. Это означает, что методу foo будет передано 42. Пусть метод foo определен так:

someDog.setName("Max"); // AAA
someDog = new Dog("Fifi"); // BBB
someDog.setName("Rowlf"); // CCC

Параметр someDog установлен в значение 42.

На строке "AAA": someDog следует за объектом Dog по указателю (который находится по адресу 42), так что для этого Dog (находящегося по адресу 42) будет запрошено изменение имени на Max.

На строке "BBB": будет создан новый объект класса Dog. Новый объект получит новый адрес в памяти. Предположим, что этот адрес 74, так что указателю someDog будет присвоено значение 74.

На строке "CCC": someDog следует за объектом Dog по указателю (который находится по адресу 74), так что для этого Dog (находящегося по адресу 74) будет запрошено изменение имени на Rowlf. После этого выполнение метода foo заканчивается.

Если иметь в виду, что myDog является указателем, а не просто объектом Dog, то вне метода foo он все еще имеет значение 42, как и до вызова функции; он все еще указывает на оригинальный объект Dog. Совершенно допустимо следовать по адресу myDog для изменения содержимого объекта, адрес myDog при этом остается однако неизменным.

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

3. Итого, Java всегда передает параметры по значению.

Widowmaker1984, блог «Java для собеседований»

Вопрос 7 (значения по умолчанию)

"Какими значениями инициализируются переменные по умолчанию?"

1. Существует три типа переменных:
- локальные переменные
- переменные экземпляра
- статические переменные (они же переменные класса)

Локальные переменные объявляются в методах, конструкторах или блоках (далее МКБ). Для них не существует значения по умолчанию. Переменные экземпляра и класса объявляются в классе, но за пределами МКБ. Они имеют значения по умолчанию, поэтому явная инициализация не является для них обязательной. С другой стороны обращение к локальной переменной без предварительной инициализации приведет к ошибке при компиляции.

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

boolean: false
char: u0000 (пустой символ)
int,short,byte / long: 0 / 0L
float /double: 0.0f / 0.0d
ссылочные типы: null

Widowmaker1984, блог «Java для собеседований»

Вопрос 6 (преобразования примитивных типов)

"Что вы знаете о преобразовании примитивных типов данных, есть ли потеря данных, можно ли преобразовать логический тип?"

1. В Java предусмотрено семь видов приведений:

- тождественное (identity);
- расширение примитивного типа (widening primitive);
- сужение примитивного типа (narrowing primitive);
- расширение объектного типа (widening reference);
- сужение объектного типа (narrowing reference);
- преобразование к строке (String);
- запрещенные преобразования (forbidden).

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

2. Иногда возникают ситуации, когда переменной одного типа нужно присвоить значение другого типа. Для некоторых типов это можно проделать и без приведения, в таких случаях говорят об автоматическом преобразовании. В Java оно возможно лишь в случае, когда точности представления чисел переменной-приемника достаточно для хранения исходного значения. Например, при занесении значения переменной типа byte или short в переменную типа int.

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

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


2.1. Следующие 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

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

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

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


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

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

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


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

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

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

3.3. Автоматическое преобразование типа иногда может оказаться причиной неожиданных сообщений транслятора об ошибках. Например, показанный ниже код, хотя и выглядит вполне корректным, приводит к сообщению об ошибке на фазе трансляции. В нем значение 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);


4. Если попытаться поместить в переменную-контейнер то, что туда не помещается, то в ней останется лишь то, что туда «влезло». К примеру, у чисел с плавающей точкой будет «отсекаться» дробная часть.

double a=11.2345;
int b=(int)a;
System.out.println(b); // в консоли получится число 11


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

4.1. При этом могут возникнуть особые случаи:

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

4.2. Надо учитывать, что дробная часть не округляется, а именно отбрасывается. Т.е. 2.7 -> 2. А если в byte (диапазон от -128 до 127) положить число 128, то результатом будет не 1, а -128 (значения старших битов будут потеряны). Значение переменной при таком преобразовании можно рассчитать, но цель программиста – не допускать ситуации, когда значение выходит за допустимые границы, поскольку это может привести к неправильной работе программы. Если число больше своего контейнера, результат будет непредсказуемым.

4.2. Следующие 23 преобразования являются сужающими:
- от 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

Лучшее   Правила сайта   Вход   Регистрация   Восстановление пароля

Материалы сайта предназначены для лиц старше 16 лет (16+)