Не очень понятно про оболочечные Данные,ячейки памяти могут наверно размер менять,какое это значение те же операции только ячейки больше,по скорости тоже самое |
Примитивные типы данных и операторы для работы с ними
3.5. Правила явного и автоматического преобразования типа при работе с числовыми величинами
Компьютер может проводить на аппаратном уровне целочисленные математические вычисления только с величинами типа int или long. При этом операнды в основных математических операциях ( + , - , * , / , % ) должны быть одного типа. Поэтому в тех случаях, когда операнды имеют разные типы, либо же типы byte, short или char, действуют правила автоматического преобразования типов: для величин типа byte, short или char сначала происходит преобразование в тип int, после чего производится их подстановка в качестве операндов. Если же один из операндов имеет тип long, действия производятся с числами типа long, поскольку второй операнд автоматически преобразуется к этому типу.
Аналогично, при работе с вещественными величинами в Java возможна работа на аппаратном уровне только с операндами типов float и double. При этом операнды в основных математических операциях должны быть одного типа. Если один из операндов имеет тип double, а другой float, действия производятся с числами типа double, поскольку операнд типа float автоматически преобразуется к типу double.
Если один из операндов целочисленный, а другой вещественный, сначала идет преобразование целочисленного операнда к такому же вещественному типу, а потом выполняется оператор.
Рассмотрим теперь правила совместимости типов по присваиванию. Они просты: диапазон значений типа левой части не должен быть уже, чем диапазон типа правой. Поэтому в присваиваниях, где тип в правой части не умещается в диапазон левой части, требуется указывать явное преобразование типа. Иначе компилятор выдаст сообщение об ошибке с не очень адекватной диагностикой "possible loss of precision" ("возможная потеря точности").
Для явного преобразования типа в круглых скобках перед преобразуемой величиной ставят имя того типа, к которому надо преобразовать. Например, пусть имеется
double d=1.5;
Величину d можно преобразовать к типу float таким образом:
(float)d
Аналогично, если имеется величина f типа float, ее можно преобразовать в величину типа double таким образом:
(double)f
Во многих случаях к явному преобразованию типов прибегать не нужно, так как действует автоматическое преобразование, если переменной, имеющий тип с более широким диапазоном изменения, присваивается выражение, имеющее тип с более узким диапазоном изменения.
Например, для вещественных типов разрешено присваивание только в том случае, когда слева стоит вещественная переменная более широкого диапазона, чем целочисленная или вещественная – справа. Например, переменной типа double можно присвоить значение типа float, но не наоборот. Но можно использовать преобразование типов для того, чтобы выполнить такое присваивание. Например:
double d=1.5; float f=(float)d;
Другие примеры допустимых присваиваний:
byte | byte0=1; //-128..127 |
short | short0=1;//- 32768.. 32767 |
char | char0=1;//0.. 65535 |
int | int0=1; //- 2.147483648E9.. 2.147483647E9 |
long | long0=1;//-9.223E18.. 9.223E18 |
float | float0=1;// ±(1.4E-45..3.402E38) |
double | double0=1;// ±(4.9E-324..1.797E308 ) |
short0=byte0; |
byte0=( byte )short0; |
char0=( char )short0; |
int0=short0; |
int0=char0; |
char0=( char )int0; |
short0=( short )int0; |
long0=byte0; |
byte0=( byte )long0; |
long0=char0; |
long0=int0; |
int0=( int )long0; |
float0=byte0; |
float0=int0; |
float0=( float )double0; |
double0=float0; |
3.6. Оболочечные классы. Упаковка (boxing) и распаковка (unboxing)
В ряде случаев вместо значения примитивного типа требуется объект. Например, для работы со списками объектов. Это связано с тем, что работа с объектами в Java может быть унифицирована, поскольку все классы Java являются наследниками класса Object, а для примитивных типов этого сделать нельзя.
Для таких целей в Java каждому примитивному типу сопоставляется объектный тип, то есть класс. Такие классы называются оболочечными ( class wrappers ). В общем случае они имеют те же имена, что и примитивные типы, но начинающиеся не со строчной, а с заглавной буквы. Исключение почему-то составляют типы int и char, для которых имена оболочечных классов Integer и Character.
Примитивный тип | Оболочечный класс |
---|---|
byte | Byte |
short | Short |
char | Character |
int | Integer |
long | Long |
float | Float |
double | Double |
Внимание! Класс Character несколько отличается от остальных оболочечных числовых классов потому, что тип char "не вполне числовой".
Основное назначение оболочечных классов – создание объектов, являющихся оболочками над значениями примитивных типов. Процесс создания такого объекта ("коробки" - box) из значения примитивного типа называется упаковкой ( boxing ), а обратное преобразование из объекта в величину примитивного типа – распаковкой ( unboxing ). Оболочечные объекты ("обертки" для значения примитивного типа) хранят это значение в поле соответствующего примитивного типа, доступном по чтению с помощью функции имяТипаValue(). Например, метода byteValue() для объекта типа Byte. Но во многих случаях можно вообще не обращать внимания на отличие переменных с типом оболочечных классов от переменных примитивных типов, так как упаковка и распаковка при подстановке такого объекта в выражение происходит автоматически, и объект оболочечного типа в этих случаях внешне ведет себя как число. Таким образом, если нам необходимо хранить в объекте числовое значение, следует создать объект соответствующего оболочечного типа.
Например, возможны такие фрагменты кода:
Integer obj1=10; int i1= obj1*2; Byte b=1; obj1=i1/10; b=2;
Но не следует забывать, что при операциях упаковки-распаковки происходят внешне невидимые дополнительные операции копирования значений в промежуточные буферные ячейки, поэтому соответствующие вычисления несколько медленнее операций с примитивными типами и требуют несколько больше памяти. Поэтому в критических к быстродействию и занимаемым ресурсам местам программы их использование нежелательно. С другой стороны, автоматическая упаковка-распаковка (она появилась в пятой версии JDK) во многих случаях заметно упрощает программирование и делает текст программы более читаемым. Так что для участков программы, некритичных к быстродействию, ею вполне можно пользоваться.
Помимо создания объектов оболочечные классы имеют ряд других полезных применений. Например, в числовых оболочечных классах хранятся константы, с помощью которых можно получить максимальные и минимальные значения:
- Byte.MIN_VALUE, Byte.MAX_VALUE, Float.MIN_VALUE, Float.MAX_VALUE, Double.MIN_VALUE, Double.MAX_VALUE и т.п.
В оболочечных классах также имеются методы классов, то есть такие, которые могут работать в отсутствии объекта соответствующего типа – для их вызова можно пользоваться именем типа. Например, как мы уже знаем, имеются методы
- Byte.parseByte ( строка )
- Short.parseShort ( строка )
- Integer.parseInt ( строка )
- Long.parseLong ( строка )
- Float.parseFloat ( строка )
- Double.parseDouble ( строка )
Они преобразуют строку в число соответствующего типа. Вызовы
- Byte.valueOf ( строка )
- Short.valueOf ( строка )
- Integer.valueOf ( строка )
- Long.valueOf ( строка )
- Float.valueOf ( строка )
- Double.valueOf ( строка )
аналогичны им, но возвращают не числовые значения, а объекты соответствующих оболочечных типов.
Примеры использования оболочечных классов:
- int n1=Integer.MAX_VALUE;
- double d1= Double.MIN_VALUE;
- double d2= Double.parseDouble(jTextField1.getText());
будет работать совершенно так же, как
- double d2= Double.valueOf(jTextField1.getText());
несмотря на то, что во втором случае методом valueOf создается объект оболочечного типа Double. Поскольку в левой части присваивания стоит переменная типа double, происходит автоматическая распаковка, и переменной d2 присваивается распакованное значение. Сам объект при этом становится мусором – программная связь с ним теряется, и он через некоторое время удаляется из памяти системой сборки мусора. В данном случае ни быстродействие, ни объем памяти некритичны, поскольку операции взаимодействия с пользователем по компьютерным меркам очень медленные, а один объект оболочечного типа занимает пренебрежимо мало места в памяти (около сотни байт). Так что с потерями ресурсов в этом случае можно не считаться, обращая внимание только на читаемость текста программы. Поэтому автор предпочитает второй вариант присваивания: хотя он и "неоптимальный" по затрате ресурсов, но более читаем.
3.7. Приоритет операторов
При вычислении выражений важен приоритет операторов. Для операторов сложения, вычитания, умножения и деления он "естественный": умножение и деление обладают одинаковым наиболее высоким приоритетом, а сложение и вычитание – одинаковым приоритетом, который ниже. Таким образом, например,
- a*b/c+d
это то же, что
- ( (a*b)/c )+d
Круглые скобки позволяют группировать элементы выражений, при этом выражение в скобках вычисляется до того, как участвует в вычислении остальной части выражения. То есть скобки обеспечивают больший приоритет, чем все остальные операторы. Поэтому (a+b)*c будет вычисляться так: сначала вычислится сумма a+b, после чего полученный результат будет умножен на значение c.
Кроме перечисленных в Java имеется большое количество других правил, определяющих приоритеты различных операторов. Автор считает их изучение не только нецелесообразным, но даже вредным: программу следует писать так, чтобы все последовательности действий были очевидны и не могли вызвать сложностей в понимании текста программы и привести к логической ошибке. Поэтому следует расставлять скобки даже в тех случаях, когда они теоретически не нужны, но делают очевидной последовательность действий. Отметим, такие действия часто помогают заодно решить гораздо более сложные проблемы, связанные с арифметическим переполнением.
Далее в справочных целях приведена таблица приоритета операторов. Ей имеет смысл пользоваться в случае анализа плохо написанных программ, когда из текста программы неясна последовательность операторов.