Java классы обертки примитивных типов: Классы обертки в Java | Java master

Содержание

Классы обертки в Java | Java master

С классами обертками мы уже неявно познакомились, когда изучали коллекции. У многих мог возникнуть вопрос: зачем задавать целочисленную переменную не int, а Integer. Все дело в том, что коллекции это набор объектов и для того, чтобы оперировать примитивными типами как объектами и были придуманы классы обертки.

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

Данные классы имеют название от примитивного типа данных, который они представляют: DoubleFloatLongIntegerShortByteCharacter, Boolean. 

Данные классы очень напоминают класс String. Объект обертку для примитивного типа можно создать как явно (используя конструктор), так и не явно (прямым присвоением примитива классу обертке) с помощью оператора»=» либо при передаче примитива в параметры метода (типа «класса-обертки»). Последнее еще называют автоупакова (autoboxing).

  1. package com.javamaster;

  2.  

  3. public class AutoBoxingExample {

  4.    

  5.     public static void main(String[] args){
  6.         Integer a = new Integer(5);//явное создание переменной обертки
  7.        

  8.         int b = a;//неявное создание… 

  9.     }

  10. }

Как и все объекты, переменные созданы с помощью классов оберток будут храниться в куче (heap). НО, есть одно важное замечание, о котором часто любят спрашивать в тестах или собеседованиях. Оно касается целочисленных классов оберток. Неявное создание таких переменных со значением в диапазоне -128 +127 будут храниться в пуле о котором мы говорили в статье о String. Потому такие обертки с одинаковыми значениями будут являться ссылками на один объект.

Автоупаковка переменных примитивных типов требует точного соответствия типа исходного примитива — типу «класса-обертки». Попытка автоупаковать переменную типа byte в Short, без предварительного явного приведения byte->short вызовет ошибку компиляции. О приведении типов мы уже писали.

  1. package com.javamaster;

  2.  

  3. public class AutoBoxingExample {

  4.    

  5.     public static void main(String[] args){
  6.        

  7.         System.out.println(«a = «+a);
  8.        

  9.    

  10.         System.out.println(«d = «+d);
  11.    

  12.     }

  13.  

  14. }

Результат работы программы:

a = 5
d = 456

Классы обертки удобны еще тем, что они предоставляют методы для работы с соответственными типами данных.

Давайте посмотрим на самые популярные:

  1. package com.javamaster;

  2.  

  3. public class AutoBoxingExample {

  4.    

  5.     public static void main(String[] args){
  6.         double b = Double.parseDouble(a);//пожалуй, самый популярный метод перевод из строки в целочисленный или дробный тип
  7.         int c = Integer.parseInt(a);
  8.         System.out.println(b);
  9.         System.out.println(c);
  10.        

  11.        

  12.         System.out.println(Integer.MAX_VALUE);//константа максимального значения
  13.        

  14.         System.out.println(Integer.bitCount(78));// в двоичном виде
  15.        

  16.         System.out.println(Float.valueOf(«80»));//возвращает целочисленный объект, содержащий значение указанного типа
  17.        

  18.         System.out.println(Integer.valueOf(«444»,16)); //возвращает целочисленный объект, содержащий целое значение указанного строкового представления,
  19.         //разобранного со значением системы счисления

  20.     }

  21.  

  22. }

Хотелось бы еще рассмотреть создание Boolean переменной. Этот вопрос встречается достаточно часто в тестах:

  1. package com.javamaster;

  2.  

  3. public class BooleanExample {

  4.    

  5.     public static void main(String[] args){
  6.  

  7.     }

  8.  

  9. }

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

Вот и все, что касается классов оберток для примитивных типов данных. Теперь Вы сможете оперировать примитивными типами, как объектами.

Понравилась статья? Поделиться ссылкой:

Pro Java: Классы-обертки для примитивных типов

Мы уже сталкивались с классами обертками (

wrapper classes) для примитивных типов, но пока не заостряли на них внимание. Сейчас же мы рассмотрим их более подробно, чтобы понять что такое методы классов и что такое поля классов.

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

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

Обертки для примитивных типов — это Double, Float, Long, Integer, Short, Byte, Character и Boolean. Эти классы предоставляют широкий диапазон методов, позволяющий в полной мере интегрировать примитивные типы в иерархию объектных типов Java.

И далее уже рассмотрим примеры работы с методами этих классов.

По существу мы много раз использовали поле length массива args, для поучения количества его элементов. Это один из примеров работы с полями объекта класса.

Примером работы с методами класса являются строки 11 и 13.

В 11 строке мы проверяем если первый аргумент командной строки является строкой true, то тогда переменной wBoolean присваивается значение true. В ином случае оно остается равным false, как было задано при инициализации.

Пример вывода этой программы представлен ниже.

Теперь небольшой пример использования класса Character:

В данном примере использован статический метод isDigit класса Character, для определения является ли элемент массива символом или числом.

Метод isDigit возвращает true, если переданный ему как параметр тип char является числом, во всех других случаях возвращается false.

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

Класс Charaster содержит множество полезных методов и полей. Все это можно узнать из оригинальной документации.

Вывод данной программы может быть таким (зависит от аргументов командной строки):

Ну и несколько примеров с классами обертками для числовых типов:

Тут просто показано использование некоторых статических методов классов Byte и Integer, а так же использование статических полей типа MAX_VALUE.

Статический метод parseInt преобразует строку в число типа int.

Статический метод bitCount подсчитывает количество битов равных единице в переданном, как аргумент числовом типе.

Кроме этих методов и полей, у классов-оберток есть еще множество других. Поэтому, если вам понадобятся какие-либо операции над этими типами, то посмотрите сперва в стандартной библиотеке.

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

На этом я думаю с этой темой закончим. Цель была познакомиться с тем, что такое методы и поля классов. Хотя пока, может, это все ясности и не добавило, но на подкорку должно было осесть.

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

Сравнение экземпляров классов-оберток примитивных типов

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

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

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

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

Также нужно обратить внимание, что JVM использует больше памяти, чем занимает куча. Например, для методов Java и стеков потоков выделяется память отдельно от кучи.

Размер кучи зависит от используемой платформы, но, как правило, это где-то между 2 и 128 Кб.

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

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

Сперва рассмотрим простой пример и его вывод:

Здесь стоит обратить внимание на область выделенную желтым цветом. Все вроде как обычно и как должно быть. То есть все так как мы привыкли уже при работе с примитивными типами данных.

Ни что, вроде бы, не предвещает беды 🙂 но надо быть на чеку. Грабли по всюду!!!

Программер, бди! И не бзди! 🙂

Теперь немного изменим программу и посмотрим что будет 🙂

Отани то! Грабельки наши!!! Ну куда ж без них то родимых!

Надеюсь вы заметили в чем разница?


Теперь intA и intB хотя и равны 500, но между собой не равны 🙂 Верней сказать они ссылаются на объекты которые содержат значение поля int равное 500.

 

Так в чем же дело? Давайте разбираться. Во первых надо отметить что intA и intB это ссылки на объекты типа Integer. Но почему же тогда при значении 50 они были равны, а при значении 500 они вдруг стали не равны? Все дело в кэше. Кэш в данном случае это не деньги 🙂 Это массив для кэширования значений, который находится во внутреннем классе класса Integer. Так как мы классы, а тем более внутренние классы, еще не проходили то это все, скорей всего, вам сейчас не будет понятно. Но все же для информации надо завязать себе узелок на память. Теперь посмотрим на этот внутренний класс:

Собственно здесь даже о классах пока ни чего можно и не знать, достаточно просто хоть немного понимать английский язык. И так кэшируемыми значениями для значений в классе Integer являются значения от -128 до 127 по умолчанию. Но этот диапазон можно изменить при помощи опций инициализации JVM или же через аргумент запуска в командной строке. И тогда начнут происходить чудеса 🙂

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

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

Вообще объекты надо сравнивать через метод .equals(), который есть у каждого класса, так как он есть у класса Object, от которого наследуются все классы в Java. Но это тема для большого и отдельного разговора о наследовании и классе Object. И пока об этом говорить еще рано. В принципе, сейчас, даже эта статья еще сложна для понимания если вы только начали изучать Java, но все же ее стоит дочитать до конца, а потом когда уже у вас будет больше знаний и опыта, вернуться к ней и прочитать еще разок 🙂 Я привел эту статью сейчас чтобы предупредить о возможных граблях.


Но это еще не все грабли 🙂 Вас еще ждет много неожиданных сюрпризов!

К нашему классу я дописал еще несколько строк. Теперь рассмотрим их и их вывод:

Опять же тут может быть пока не понятно, что такое valueOf() и оператор new, так как это мы еще не изучали. Вкратце скажу что valueOf() это метод класса Integer, а оператор new используется для создания новых объектов.

Пока на все внимание обращаем на вывод программы. Как видим при сравнении объектов Integer содержащих одинаковые значения, созданных с использованием оператора new, даже при значениях попадающие в диапазон кэширования, мы получаем в результате false. Это происходит потому, что это ссылки intA и intB указывают на разные объекты, а при использовании valueOf(), в данном примере, ссылки intA и intB указывают на один и тот же объект. От сюда вывод, что при использовании для сравнения оператора == для ссылочных типов, сравниваются ссылки, а не объекты. Чтобы сравнить объекты надо использовать метод equals():

println

(«Сравнение через equals() intA и intB дает » + intA.equals(intB));

Данный код нам выведет на экран: Сравнение через equals() intA и intB дает true

Теперь немного изменим значения и посмотрим на результат:

Когда мы задали значение 500, то получили вполне ожидаемый результат при сравнении ссылок. Но все же хорошо бы посмотреть на код метода valueOf().

Как видно из кода (хотя возможно вам пока это пока и не видно) при выходе за пределы значений по умолчанию метод возвращает новый объект типа Integer. Если же значение находится в пределах кэша, то оно берется от туда. Кэш из себя представляет просто массив значений типа Integer в заданном диапазоне, по умолчанию от -128 до 127.

Из всего вышесказанного следует:

  • Кэшируемыми значениями для Integer по умолчанию являются значения от -128 до 127
  • Диапазон значений по умолчанию для Integer можно изменить при помощи опций запуска JVM
  • Кэширование работает только в случае autoboxing
  • При создании объекта через оператор new кэширования не происходит
  • При сравнении объектов при помощи оператора == можно получить по морде гарблями ошибку
  • Объекты надо сравнивать при помощи метода equals()

Кэширование так же есть и для других объектов классов оберток примитивных типов:

  • Byte, Short, Long (диапазон от -128 до 127 включительно)
  • Charaster (диапазон от 0 до 127 включительно)
  • В отличие от Integer эти значения заданы жестко и изменить их параметрами запуска нельзя

Классы Float и Double не имеют механизма кэширования поскольку не могут представлять точные значения. Ну а Boolean кэшировать вообще бессмысленно.

Ну и на последок еще один примерчик и его вывод:

Хотя, опять же, многое может быть пока не понятно, но просто намотайте на ус 🙂 ну или если нет усов завяжите узелок на память. Суть в том, что мой статический метод iHash возвращает хэш объекта, который однозначно его идентифицирует. Из вывода программы видно, что при присвоении ссылкам intA и intB значения 5, данные ссылки указывают на один и тот же объект, в данном случае находящийся в кэше. Просьба не путать хэш и кэш. А при присвоении этим ссылкам значения 500 происходит скрытый вызов оператора new и объекты создаются каждый раз новые, что видно по их хэшу, в то время как при присвоении этим ссылкам снова значения 5 они продолжают указывать, но тот же самый объект, поскольку он всегда хранится в кэше.

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

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

Примитивные типы данных в Java

Продолжаем серию уроков по программированию на языке Java. И сегодня пора нам написать чуть более сложное приложение, чем «Hello world». Мы будем говорить о типах данных. 

Java является строго типизированным языком программирования. Это означает, что переменные имеют конкретный известный тип еще на этапе компиляции. Например, если мы возьмем язык Javascript, то там можно объявить переменную как «var a = 45» или «var b = «Hello». То есть тип переменной здесь не указывается. В Java же, напротив. Если мы хотим объявить некую переменную, нам нужно указать ее тип, чтобы компилятор знал с каким типом он работает. Закрепим прочитанное примером. Откройте наш первый проект, который мы создали в статье о первом приложении и создайте новый класс. Назовем его TypesOfData. Не забудьте при создании поставить галочку, чтобы создался метод main. Теперь можно создать пару переменных. Хм.. Но мы не сможем ничего создать, так как ничего еще не выучили. Нужно еще немного теории.

В Java есть 8 примитивных типов, которые делят на 3 группы, вот они:

  1. Целые числа — byte, short, char, int, long
  2. Числа с плавающей точкой (иначе дробные) — float, double
  3. Логический — boolean

Целочисленные типы данных

Типы с плавающей точкою. То есть дробные

Логический тип данных

Предлагаю пока поработать с целочисленными типами. Самым «популярным» среди целочисленных типов данных является тип int. Он позволяет хранить достаточно большой диапазон чисел и занимает не всего 32 бита.

Подкрепив наши знания теорией давайте воплотим их на практике. Для этого создайте несколько глобальных переменных в своем классе. Забыл упомянуть, что в Java переменные делятся на глобальные и локальные переменные. Глобальные — это те которые объявленные в классе. Локальные — те которые объявленные в методе. Об их разнице мы поговорим позже.

Вот пример объявленных глобальных приложений. Как Вы могли заметить, я называю переменные и классы в так называемом CamelCase или верблюжий  регистр. Это не просто так. Дело в том, что в Java принято объявлять переменные, классы и методы именно в таком стиле. Я советую Вам начать программирование с правильного стиля, который принятый в сообществе программистов для того, чтобы Ваш код могли читать и редактировать другие люди. Также старайтесь называть переменные, методы и классы в читаемом стиле: для класса — это сущность, которую он изображает, для метода — действие, для переменной — читаемое имя, которое ей соответствует.

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

Давайте перенесем наши переменные в main метод и попробуем поиграть с их размерностью. Для начала поэкспериментируем с byte:

public class TypesOfData {

    public static void main(String[] args){
         byte byteVariable = 3;    
       
         byte newByteVariable = (byte) (byteVariable + 125);//здесь нам нужно кастить к типу байт, так
         //операция распознается как инт
        System.out.println(«First output » + newByteVariable);//так как тип байт имеет размерность -128 127, то добавление к 125 3 будет переполнять размерность
        //и мы получим отрицательный результат
       
        System.out.print(«Second output»);
        System.out.println(byteVariable+=125);//сокращенную запись нельзя записать с конкатенацией в выводе на консоль как предыдущую
        //но для сокращенной записи нам не нужно кастить выражение к типу byte
       
        byte newByteVariable1 = (byte) (byteVariable — 20);
        System.out.println(«Third output » + newByteVariable1);
        //при отнимании ситуация противоположная. Выход за диапазон размерности даст нам положительный результат
    }
}

Вот результат запуска и выполнения данного кода: 

  1. First output -128

  2. Second output-128

  3. Third output 108

Теперь пример работы с int типом. 

int intValue = 3;
        int nextIntValue = 5;
        int resultOfDivision = intValue/nextIntValue;//int переменная автоматически откидает дробную часть
        System.out.println(«Result of division » + resultOfDivision);
       
        int resultOfDivision1 = nextIntValue/intValue;//от деление остается только целое число
        System.out.println(«Result of division1 » + resultOfDivision1);

Результат работы программы:

Result of division 0
Result of division1 1

Как видно из примера, для работы с дробными числами нужно использовать double или float. Давайте попробуем поработать с типом double.

  1. //для работы с дробными числами лучше использовать тип double, float

  2.         double doubleValue1 = 4;

  3.         double doubleValue2 = 5;

  4.        

  5.         double resultOfDivision = doubleValue1/doubleValue2;

  6.         System.out.println(resultOfDivision);
  7.        

  8.         double doubleValue3 = 1.234;

  9.         double doubleValue4 = 4.0854;

  10.        

  11.         double resultOfMultiplication = doubleValue3*doubleValue4;

  12.         System.out.println(resultOfMultiplication);

Результат работы программы:

0.8
5.0413836

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

Мы чуть не забыли рассмотреть такой тип данных, как boolean. Его используют очень часто. Этот тип данных хранит значение истина или ложь. Это все значение которые может принимать данный тип данных. 

  1. boolean booleanValueTrue = true;

  2.         boolean booleanValueFalse = false;

  3.         System.out.println(booleanValueTrue);
  4.         System.out.println(booleanValueFalse);
  5.         booleanValueTrue = false;

  6.         System.out.println(booleanValueTrue);

Результат работы программы:

true
false
false

Вы спросите: «А как же добавление int к double или double к char?». Отвечаю: можно кастить и приводить типы данных, НО только в пределах правил, которые мы изучим в следующей статье.

Понравилась статья? Поделиться ссылкой:

Типы данных Java – Примитивные типы

Теперь более подробно рассмотрим примитивные типы данных Java. Как уже упоминалось их восемь. В Java размеры всех примитивных типов жестко фиксированы. Они не меняются с переходом на иную машинную архитектуру, как это происходит во многих других языках. Незыблемость размера — одна из причин улучшенной переносимости Java-npoгpaмм.

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

  Тип   Содержит   По умолчанию   Размер   Диапазон   Обертки
  boolean  true или false    flase  1 bit  не применимо  Boolean
  char  целое без знаковое    \u0000  16 bits  от \u0000  до \uFFFF  Charaster
  byte  целое знаковое   0  8 bits  от -128 до 127  Byte
  short  целое знаковое   0  16 bits  от -32768 до 32767  Short
  int  целое знаковое   0  32 bits  от -2147483648 до 2147483647  Integer
  long  целое знаковое   0  64 bits  от -9223372036854775808 до 9223372036854775807  Long
  float  вещественное знаковое   0.0  32 bits  от 1.4E−45 до 3.4028235E+38  Float
  double  вещественное знаковое   0.0  64 bits  от 4.9E−324 до 1.7976931348623157E+308  Double

О типе данных char надо отметить, что он содержит код симола Unicode.

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

Сперва приведу пример с ошибкой попытки присвоения значения по умолчанию локальной переменной:

Здесь в 16 строке мы попытались вывести на консоль значение локальной переменной localInt, которую предварительно не инициализировали (не присвоили) ни каким допустимым значением. На что нам была выдана ошибка компиляции. В то время как для переменной defInt, такой ошибки выдано не было.

На копипасту внимания не обращаем, лень было переделывать скриншот

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

И посмотрим что эта программа выводит в терминал:

Как говорится: “Что и требовалось доказать”.

Для типа char выведено пустое значение так как код \u0000 относится к непечатным символам.

Так же стоит упомянуть и взять на заметку, что для любого объекта и строки (String) значением по умолчанию является null. Ну это так на заметку.

Далее уже будем рассматривать подробно каждый примитивный тип и операции которые можно над ним совершать.

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

Преобразование типов в Java это достаточно большая тема, но мы постараемся рассмотреть ее как можно более полно и вместе с тем компактно. Частично мы уже касались этой темы когда рассматривали примитивные типы 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;

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

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

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

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

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

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

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

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

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

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

454 % 256 = 198

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

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

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

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

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

Преимущества использования классов-оболочек над примитивами в Java

Переполнение стека
  1. Около
  2. Продукты
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
  3. Вакансии Программирование и связанные с ним технические возможности карьерного роста
  4. Талант Нанимайте технических специалистов и создавайте свой бренд работодателя
  5. Реклама Обратитесь к разработчикам и технологам со всего мира
  6. О компании

Загрузка…

  1. Авторизоваться зарегистрироваться
  2. текущее сообщество

    • Переполнение стека Помогите болтать
.

примитивных типов данных (Руководства по Java ™> Изучение языка Java> Основы языка)

Язык программирования Java является статически типизированным, что означает, что все переменные должны быть сначала объявлены, прежде чем их можно будет использовать. Это включает в себя указание типа и имени переменной, как вы уже видели:

Это сообщит вашей программе, что поле с именем «шестерня» существует, содержит числовые данные и имеет начальное значение «1». Тип данных переменной определяет значения, которые она может содержать, а также операции, которые могут выполняться с ней.В дополнение к int , язык программирования Java поддерживает еще семь других примитивных типов данных . Примитивный тип предопределен языком и назван зарезервированным ключевым словом. Примитивные значения не разделяют состояние с другими примитивными значениями. Язык программирования Java поддерживает следующие восемь примитивных типов данных:

  • байт : Тип данных байт является 8-битовым целым числом с дополнением до двух со знаком. Он имеет минимальное значение -128 и максимальное значение 127 (включительно).Тип данных байт может быть полезен для экономии памяти в больших массивы, где действительно важна экономия памяти. Их также можно использовать вместо int , где их пределы помогают прояснить ваш код; тот факт, что диапазон переменной ограничен, может служить формой документации.

  • short : Тип данных short представляет собой 16-битовое целое число со знаком в дополнительном коде. Он имеет минимальное значение -32 768 и максимальное значение 32 767 (включительно).Как и для байта , применяются те же правила: вы можете использовать короткий для экономии памяти в больших массивах в ситуациях, когда экономия памяти действительно имеет значение.

  • int : по умолчанию тип данных int представляет собой 32-битное знаковое целое число с дополнением до двух, которое имеет минимальное значение -2 31 и максимальное значение 2 31 -1. В Java SE 8 и более поздних версиях вы можете использовать тип данных int для представления 32-разрядного целого числа без знака, минимальное значение которого равно 0, а максимальное — 2 32 -1.Используйте класс Integer, чтобы использовать тип данных int как целое число без знака. См. Раздел «Числовые классы» для получения дополнительной информации. Статические методы, такие как compareUnsigned , diverUnsigned и т. Д., Были добавлены в Integer Класс для поддержки арифметических операций с целыми числами без знака.

  • long : Тип данных long — это 64-битное целое число с дополнением до двух. Длинный знак со знаком имеет минимальное значение -2 63 и максимальное значение 2 63 -1.В Java SE 8 и более поздних версиях вы можете использовать тип данных long для представления беззнакового 64-битного типа long, который имеет минимальное значение 0 и максимальное значение 2 64 -1. Используйте этот тип данных, когда вам нужен более широкий диапазон значений, чем тот, который предоставляется int . В Long Класс также содержит такие методы, как compareUnsigned , diverUnsigned и т. Д. Для поддержки арифметических операций для значений unsigned long.

  • float : Тип данных float представляет собой 32-битное число с плавающей запятой одинарной точности IEEE 754.Диапазон его значений выходит за рамки данного обсуждения, но указан в Раздел «Типы, форматы и значения с плавающей запятой» Спецификации языка Java. Как и в случае с рекомендациями для байта и короткого , используйте с плавающей запятой (вместо double ), если вам нужно сохранить память в больших массивах чисел с плавающей запятой. Этот тип данных никогда не следует использовать для точных значений, таких как валюта. Для этого вам нужно будет использовать java.math.BigDecimal вместо этого.Numbers and Strings охватывает BigDecimal и другие полезные классы, предоставляемые платформой Java.

  • double : Тип данных double представляет собой 64-битное число с плавающей запятой IEEE 754 двойной точности. Диапазон его значений выходит за рамки данного обсуждения, но указан в Раздел «Типы, форматы и значения с плавающей запятой» Спецификации языка Java. Для десятичных значений этот тип данных обычно выбирается по умолчанию. Как упоминалось выше, этот тип данных никогда не следует использовать для точных значений, таких как валюта.

  • boolean : Тип данных boolean имеет только два возможных значения: true и false . Используйте этот тип данных для простых флагов, которые отслеживают истинные / ложные условия. Этот тип данных представляет один бит информации, но его «размер» не определен точно.

  • char : Тип данных char — это один 16-битный символ Unicode. Он имеет минимальное значение '\ u0000' (или 0) и максимальное значение '\ uffff' (или 65 535 включительно).

В дополнение к восьми примитивным типам данных, перечисленным выше, язык программирования Java также обеспечивает специальную поддержку символьных строк через java.lang.String класс. Заключение строки символов в двойные кавычки автоматически создаст новый объект String ; например, String s = "это строка"; . String Объекты неизменяемые , что означает, что после создания их значения не могут быть изменены. Класс String технически не является примитивным типом данных, но, учитывая особую поддержку, предоставляемую ему языком, вы, вероятно, будете думать о нем как о таковом.Вы узнаете больше о классе String в Простые объекты данных

Значения по умолчанию

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

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

Тип данных Значение по умолчанию (для полей)
байт 0
короткий 0
внутренний 0
длинный 0L
поплавок 0,0f
двойной 0,0d
символ ‘\ u0000’
Строка (или любой объект) null
логический ложный

Локальные переменные немного отличаются; компилятор никогда не присваивает значение по умолчанию неинициализированной локальной переменной.Если вы не можете инициализировать свою локальную переменную там, где она объявлена, обязательно присвойте ей значение, прежде чем пытаться использовать ее. Доступ к неинициализированной локальной переменной приведет к ошибке времени компиляции.

Литералы

Вы могли заметить, что ключевое слово new не используется при инициализации переменной примитивного типа. Примитивные типы — это специальные типы данных, встроенные в язык; они не объекты, созданные из класса. Литерал — это представление исходного кода фиксированного значения; литералы представлены непосредственно в вашем коде, не требуя вычислений.Как показано ниже, можно присвоить литерал переменной примитивного типа:

логический результат = истина;
char capitalC = 'C';
байт b = 100;
короткий s = 10000;
int i = 100000;
 
Целочисленные литералы

Целочисленный литерал имеет тип long , если он заканчивается буквой L или l ; в противном случае это тип int . Рекомендуется использовать прописную букву L , потому что строчную букву l трудно отличить от цифры 1 .

Значения целочисленных типов byte , short , int и long могут быть созданы из int литералов. Значения типа long , превышающие диапазон int , могут быть созданы из литералов long . Целочисленные литералы можно выразить с помощью этих систем счисления:

  • Десятичный: основание 10, цифры которого состоят из цифр от 0 до 9; это система счисления, которую вы используете каждый день
  • Шестнадцатеричный: База 16, цифры которой состоят из цифр от 0 до 9 и букв от A до F
  • Двоичный: база 2, цифры которой состоят из чисел 0 и 1 (вы можете создавать двоичные литералы в Java SE 7 и более поздних версиях)

Для программирования общего назначения десятичная система, вероятно, будет единственной системой счисления, которую вы когда-либо будете использовать.Однако, если вам нужно использовать другую систему счисления, следующий пример показывает правильный синтаксис. Префикс 0x указывает на шестнадцатеричный, а 0b указывает на двоичный:

// Число 26 в десятичном формате
int decVal = 26;
// Число 26 в шестнадцатеричном формате
int hexVal = 0x1a;
// Число 26 в двоичном формате
int binVal = 0b11010;
 
Литералы с плавающей запятой

Литерал с плавающей запятой имеет тип float , если он заканчивается буквой F или f ; в противном случае его тип — double и может дополнительно заканчиваться буквой D или d .

Типы с плавающей запятой ( float и double ) также могут быть выражены с помощью E или e (для научного представления), F или f (32-битный литерал с плавающей запятой) и D или d (64-битный двойной литерал; это является значением по умолчанию и по соглашению опускается).

двойной d1 = 123,4;
// то же значение, что и d1, но в экспоненциальном представлении
двойной d2 = 1.234e2;
float f1 = 123.4f;
 
Символьные и строковые литералы

Литералы типов char и Строка может содержать любые символы Unicode (UTF-16).Если ваш редактор и файловая система позволяют это, вы можете использовать такие символы прямо в своем коде. Если нет, вы можете использовать «escape-последовательность Unicode», например '\ u0108' (заглавная C с циркумфлексом) или «S = Se \ u00F1or» (Sí Señor на испанском языке). Всегда используйте «одинарные кавычки» для символьных литералов и «двойные кавычки» для строковых литералов. Управляющие последовательности Unicode могут использоваться в любом месте программы (например, в именах полей), а не только в литералах char или String .

Язык программирования Java также поддерживает несколько специальных управляющих последовательностей для литералов char и String : \ b (backspace), \ t (tab), \ n (перевод строки), \ f (подача страницы), \ r (возврат каретки), \ " (двойная кавычка), \ ' (одинарная кавычка) и \\ (обратная косая черта).

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

Наконец, существует также особый вид литерала, называемый литералом класса , образованный путем взятия имени типа и добавления « .class» ; например, String.class .Это относится к объекту (типа , класс ), который представляет сам тип.

Использование символов подчеркивания в числовых литералах

В Java SE 7 и более поздних версиях любое количество символов подчеркивания ( _ ) может появляться где угодно между цифрами в числовом литерале. Эта функция позволяет вам, например. для разделения групп цифр в числовых литералах, что может улучшить читаемость вашего кода.

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

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

long creditCardNumber = 1234_5678_9012_3456L;
long socialSecurityNumber = 999_99_9999L;
float pi = 3.14_15F;
длинный hexBytes = 0xFF_EC_DE_5E;
длинные шестнадцатеричные слова = 0xCAFE_BABE;
длинный maxLong = 0x7fff_ffff_ffff_ffffL;
byte nybbles = 0b0010_0101;
длинные байты = 0b11010010_01101001_10010100_10010010;
 

Знаки подчеркивания можно ставить только между цифрами; нельзя ставить подчеркивание в следующих местах:

  • В начале или конце числа
  • Рядом с десятичной запятой в литерале с плавающей запятой
  • До F или L суффикс
  • В позициях, где ожидается строка цифр

Следующие примеры демонстрируют допустимые и недопустимые места подчеркивания (выделенные) в числовых литералах:

//  Недействительно: нельзя ставить подчеркивания 
//  рядом с десятичной точкой 
поплавок pi1 = 3_.1415F;
//  Недействительно: нельзя ставить подчеркивания 
//  рядом с десятичной точкой 
float pi2 = 3._1415F;
//  Недействительно: нельзя ставить подчеркивания 
//  перед суффиксом L 
long socialSecurityNumber1 = 999_99_9999_L;

// ОК (десятичный литерал)
интервал x1 = 5_2;
//  Недействительно: нельзя ставить подчеркивания 
//  В конце литерала 
int x2 = 52_;
// ОК (десятичный литерал)
int x3 = 5_______2;

//  Недействительно: нельзя ставить подчеркивания 
//  в префиксе системы счисления 0x 
int x4 = 0_x52;
//  Недействительно: нельзя ставить подчеркивания 
//  в начале числа 
int x5 = 0x_52;
// ОК (шестнадцатеричный литерал)
int x6 = 0x5_2;
//  Недействительно: нельзя ставить подчеркивания 
//  в конце числа 
int x7 = 0x52_;
 
.

java — примитивы против инициализации класса оболочки

Переполнение стека
  1. Около
  2. Продукты
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
  3. Вакансии Программирование и связанные с ним технические возможности карьерного роста
  4. Талант Нанимайте технических специалистов и создавайте свой бренд работодателя
  5. Реклама Обратитесь к разработчикам и технологам со всего мира
  6. О компании
.Преобразование типа

— преобразование из примитива Java в классы-оболочки

Переполнение стека
  1. Около
  2. Продукты
  3. Для команд
  1. Переполнение стека Общественные вопросы и ответы
  2. Переполнение стека для команд Где разработчики и технологи делятся частными знаниями с коллегами
  3. Вакансии Программирование и связанные с ним технические возможности карьерного роста
  4. Талант Нанимайте технических специалистов и создавайте свой бренд работодателя
  5. Реклама Обратитесь к разработчикам и технологам со всего мира
  6. О компании

Загрузка…

  1. Авторизоваться зарегистрироваться
  2. текущее сообщество

    • Переполнение стека Помогите болтать
.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *