Типы данных java: примитивные, ссылочные
Содержание:
- JSON
- Сужение типов
- Математика
- Присваивание целых и вещественных чисел
- Тип int в Java
- Оператор множественного выбора (switch)
- Тип Optional
- Ссылочные [ править ]
- Абстрактный класс и интерфейс
- Целочисленные типыInteger types
- Конструкторы
- Приведение типов
- Autoboxing и unboxing
- Что такое Generics в Java?
- Деление целых и вещественных чисел в Java
JSON
Сериализация и Десериализация
JSON — невероятно удобный и полезный синтаксис для хранения и обмена данными. Java полностью поддерживает это.
Прим. перев. Для использования JSON из примера необходимо подключить библиотеку JSON Simple.
Вы можете сериализовать данные следующим образом:
Получается следующая строка JSON:
Десериализация в Java выглядит так:
Используемый в примере файл JSON (jsonDemoFile.json):
Прим. перев. В Java проектах очень часто для работы с JSON используют библиотеки Gson от Google или Jackson. Обе библиотеки очень популярны и хорошо поддерживаются. Попробуйте и их.
Сужение типов
А что насчет остальных вариантов? Что делать, если нужно переменной типа присвоить значение переменной типа ?
Представьте, что переменная — это корзина. У нас есть корзины разных размеров: 1, 2, 4 и 8 байт. При перекладывании пирожков из меньшей корзины в большую проблем не будет. А вот при перекладывании из большей в меньшую часть пирожков может потеряться.
Это преобразование — от типа большего размера к меньшему — называют сужением типа. При таком присваивании часть числа может просто не поместиться в новую переменную и «остаться за бортом».
При сужении типа мы должны явно показать компилятору, что мы не ошиблись, и отбрасывание части числа сделано осознанно. Для этого используется оператор приведения типа. Это имя типа в круглых скобочках.
В таких ситуациях Java-компилятор требует от программиста указывать оператор преобразования типа. Выглядит в общем виде он так:
Примеры:
Код | Описание |
---|---|
Каждый раз нужно явно указывать оператор преобразования типа |
В данном случае равно , и это кажется излишним. А что если бы было больше?
Код | Описание |
---|---|
Миллион отлично помещается и в тип , и в тип . А вот при присваивании миллиона переменной типа два первых байта были отброшены, и остались только два последних байта. А при присваивании типу вообще остался один последний байт.
Устройство чисел в памяти:
Тип | Двоичная запись | Десятичная запись |
---|---|---|
0b00000000000011110100001001000000 | ||
0b0100001001000000 | ||
0b01000000 |
Тип
Тип , как и тип , занимает два байта, но для их преобразования в друг друга всегда нужно использовать оператор приведения типа. Все дело в том, что тип знаковый, и может содержать значения от до , а тип беззнаковый, и может содержать значения от до .
В нельзя сохранить отрицательные числа, которые могут храниться в . А в нельзя сохранить числа больше , которые могут храниться в .
Математика
Float или Double?
Программисты часто не могут выбрать необходимую точность для чисел с плавающей запятой. Float требует всего 4 байта, но имеет только 7 значащих цифр, а Double в два раза точнее (15 цифр), но в два раза прожорливее.
Фактически, большинство процессоров могут одинаково эффективно работать как с Float, так и с Double, поэтому воспользуйтесь рекомендацией Бьорна Страуструпа (автор языка С++):
Проверка на нечетность
Можно ли использовать этот код для точного определения нечетного числа?
Надеюсь, вы заметили хитрость. Если мы решим таким образом проверить отрицательное нечетное число (например, -5), остаток от деления не будет равен единице, поэтому воспользуйтесь более точным методом:
Он не только решает проблему отрицательных чисел, но и работает более производительно, чем предыдущий метод. Арифметические и логические операции выполняются намного быстрее, чем умножение и деление.
Присваивание целых и вещественных чисел
Было бы плохо, если бы целые числа можно было присваивать только переменным типа , а вещественные — только переменным типа . Хотелось бы иметь возможность преобразовывать одни числа в другие. И в Java такая возможность есть.
Во-первых, переменным типа можно присваивать как вещественные, так и целые числа. При присваивании целых чисел они просто преобразовываются в вещественные. Хотя иногда при этом возможна небольшая потеря точности.
Команда | Примечание |
---|---|
В переменной хранится значение | |
В переменной хранится значение | |
В переменной хранится значение |
Во-вторых, если в каком-то выражении участвуют целое и вещественное число, целое сначала преобразуется в вещественное и только потом взаимодействует с другим вещественным числом.
Команда | Примечание |
---|---|
В переменной хранится значение | |
На экран будет выведено число | |
На экран будет выведено число |
И наконец, есть возможность присваивать переменным типа вещественные числа. Дробная часть числа при этом отбрасывается — число округляется вниз до целого.
Также компилятор требует, чтобы этот факт программист задокументировал явно (чтобы другие программисты понимали, что тут происходит отбрасывание дробной части). Общий вид этого выражения в коде такой:
Присваивание переменной типа вещественного числа
Примеры:
Команда | Примечание |
---|---|
В переменной хранится значение | |
В переменной хранится значение | |
В переменной хранится значение |
Тип int в Java
Int представляет собой четырехбайтовое число тридцать два бита и составляет в общей сложности 4 294 967 296 номеров. Java обеспечивает нейтральную платформу, всегда делая int 32 бит в каждой JVM, короткие всегда 16 бит, длинные всегда 64 бит и т. Д. Это избавляет Java от проблем, с которыми сталкиваются программисты C, при переносе кода между платформами. Например, int в программе ‘C’ может быть двумя байтами в одной операционной системе и четырьмя байтами в другой операционной системе. Как и все числовые типы данных, int может быть отлит в другие числовые типы (байт, короткий, длинный, поплавковый, двойной). Когда выполняются сбрасывающие потери (например, int by byte), преобразование выполняется по модулю длины меньшего типа.
Python
int i =25000;
1 | inti=25000; |
Оператор множественного выбора (switch)
Он применяется, когда нужно выполнить один из нескольких блоков кода в зависимости от значения аргумента.
Выгода от switch тем очевиднее, чем больше проверок и вариантов действий нам нужно.
Например:
В круглых скобках указывается аргумент для switch, а в каждом блоке case — чему этот аргумент должен равняться, чтобы выполнился код после двоеточия.
В нашем случае выполнится case 5, так как переменная dayOfWeekNum (порядок дня в неделе) равна 5.
В конце каждого блока case мы ставим break. Если этого не сделать, то выполнится также код из следующего блока case и так далее.
Например:
Если для нескольких значений аргумента нужно выполнять один и тот же код, то блоки case можно объединить.
Например, для будних дней (dayOfWeekNum от 1 до 5) будем выводить, какой это по счёту рабочий день, а для уикенда — первый или второй это выходной:
Теперь при значении переменной dayOfWeekNum от 1 до 5 выполнится один и тот же код, и для значений 6 и 7 — тоже одинаковый.
Также можно задать действие, если ни одно из условий не сработало. Делается это с помощью ключевого слова default:
Примечание. Слово break означает выход из switch…case. Поэтому если ваш блок default стоит не последним, то тоже завершайте его словом break, иначе выполнится код из следующего case.
Тип Optional
Последнее обновление: 29.04.2018
Ряд операций сведения, такие как min, max, reduce, возвращают объект Optional<T>. Этот объект фактически обертывает результат операции.
После выполнения операции с помощью метода get() объекта Optional мы можем получить его значение:
import java.util.Optional; import java.util.ArrayList; import java.util.Arrays; public class Program { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<Integer>(); numbers.addAll(Arrays.asList(new Integer[]{1,2,3,4,5,6,7,8,9})); Optional<Integer> min = numbers.stream().min(Integer::compare); System.out.println(min.get()); // 1 } }
Но что, если поток не содержит вообще никаких данных:
// список numbers пустой ArrayList<Integer> numbers = new ArrayList<Integer>(); Optional<Integer> min = numbers.stream().min(Integer::compare); System.out.println(min.get()); // java.util.NoSuchElementException
В этом случае программа выдаст исключение java.util.NoSuchElementException. Что мы можем сделать, чтобы избежать выброса исключения?
Для этого класс Optional предоставляет ряд методов.
Самой простой способ избежать подобной ситуации — это предварительная проверка наличия значения в Optional с помощью метода isPresent(). Он
возврашает true, если значение присутствует в Optional, и false, если значение отсутствует:
ArrayList<Integer> numbers = new ArrayList<Integer>(); Optional<Integer> min = numbers.stream().min(Integer::compare); if(min.isPresent()){ System.out.println(min.get()); }
orElse
Метод orElse() позволяет определить альтернативное значение, которое будет возвращаться, если Optional не получит из потока какого-нибудь значения:
// пустой список ArrayList<Integer> numbers = new ArrayList<Integer>(); Optional<Integer> min = numbers.stream().min(Integer::compare); System.out.println(min.orElse(-1)); // -1 // непустой список numbers.addAll(Arrays.asList(new Integer[]{4,5,6,7,8,9})); min = numbers.stream().min(Integer::compare); System.out.println(min.orElse(-1)); // 4
orElseGet
Метод orElseGet() позволяет задать функцию, которая будет возвращать значение по умолчанию:
import java.util.Optional; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; public class Program { public static void main(String[] args) { ArrayList<Integer> numbers = new ArrayList<Integer>(); Optional<Integer> min = numbers.stream().min(Integer::compare); Random rnd = new Random(); System.out.println(min.orElseGet(()->rnd.nextInt(100))); } }
В данном случае возвращаемое значение генерируется с помощью метода nextInt класса Random, который возвращает случайное число.
orElseThrow
Еще один метод — orElseThrow позволяет сгенерировать исключение, если Optional не содержит значения:
ArrayList<Integer> numbers = new ArrayList<Integer>(); Optional<Integer> min = numbers.stream().min(Integer::compare); // генеррация исключения IllegalStateException System.out.println(min.orElseThrow(IllegalStateException::new));
Обработка полученного значения
Метод ifPresent() определяет действия со значением в Optional, если значение имеется:
ArrayList<Integer> numbers = new ArrayList<Integer>(); numbers.addAll(Arrays.asList(new Integer[]{4,5,6,7,8,9})); Optional<Integer> min = numbers.stream().min(Integer::compare); min.ifPresent(v->System.out.println(v)); // 4
В метод ifPresent передается функция, которая принимает один параметр — значение из Optional. В данном случае полученное минимальное число выводится на консоль.
Но если бы массив numbers был бы пустым, и соответственно Optional не сдержало бы никакого значения, то никакой ошибки бы не было.
Метод ifPresentOrElse() позволяет определить альтернативную логику на случай, если значение в Optional отсутствует:
ArrayList<Integer> numbers = new ArrayList<Integer>(); Optional<Integer> min = numbers.stream().min(Integer::compare); min.ifPresentOrElse( v -> System.out.println(v), () -> System.out.println("Value not found") );
В метод ifPresentOrElse передается две функции. Первая обрабатывает значение в Optional, если оно присутствует. Вторая функция представляет действия, которые выполняются,
если значение в Optional отсутствует.
НазадВперед
Ссылочные [ править ]
Ссылочные типы — это все остальные типы: классы, перечисления и интерфейсы, например, объявленные в стандартной библиотеке Java, а также массивы.
Строки
Строки это объекты класса String, они очень распространены, поэтому в некоторых случаях обрабатываются отлично от всех остальных объектов. Строковые литералы записываются в двойных кавычках.
Эта программа выведет: Hello World foo == bar ? true foo равен bar ? true foo == baz ? false foo равен baz ? true
Обертки
Если требуется создать ссылку на один из примитивных типов данных, необходимо использовать соответствующий класс-обертку. Также в таких классах есть некоторые полезные методы и константы, например минимальное значение типа int можно узнать использовав константу Integer.MIN_VALUE. Оборачивание примитива в объект называется упаковкой (boxing), а обратный процесс распаковкой (unboxing).
Тип | Класс-обертка |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
Рекомендуется использовать valueOf, он может быть быстрее и использовать меньше памяти потому что применяет кэширование, а конструктор всегда создает новый объект.
Получить примитив из объекта-обертки можно методом Value.
Абстрактный класс и интерфейс
- В интерфейсе отсутствует код реализации, а все методы являются абстрактными. То есть, все методы объявляются, но ни один не определяется.
- В абстрактном классе есть исполняемые и абстрактные методы.
- Класс реализует сколько угодно интерфейсов, но расширяет только один абстрактный класс.
- Методы абстрактного класса могут быть или не быть абстрактными.
- Абстрактный класс не может превратиться в экземпляр, но может стать подклассом.
- Все абстрактные методы должны определяться в подклассе, то есть, подкласс является абстрактным.
- Создавать экземпляры из интерфейса нельзя. Их можно реализовывать в других классах или расширять другими интерфейсами.
- Переменные интерфейсов конечные и статические. По умолчанию, все методы интерфейса публичные и абстрактные.
- Интерфейс не может содержать реализацию и не может превращаться в подкласс. Все переменные должны быть постоянными.
Целочисленные типыInteger types
Тип является базовым целочисленным типом по умолчанию.The type is the default basic integer type. Он может представлять все целые числа в диапазоне, зависящем от реализации.It can represent all of the whole numbers over an implementation-specific range.
Представление целого числа со знаком — это одно из значений, которое может содержать положительные и отрицательные значения.A signed integer representation is one that can hold both positive and negative values. Он используется по умолчанию или при наличии ключевого слова модификатор.It’s used by default, or when the modifier keyword is present. Ключевое слово модификатор задает Неподписанное представление, которое может содержать только неотрицательные значения.The modifier keyword specifies an unsigned representation that can only hold non-negative values.
Модификатор размера задает ширину в битах используемого представления целых чисел.A size modifier specifies the width in bits of the integer representation used. Язык поддерживает модификаторы, и .The language supports , , and modifiers. Тип должен быть не менее 16 бит в ширину.A type must be at least 16 bits wide. Тип должен быть не менее 32 бит в ширину.A type must be at least 32 bits wide. Тип должен быть не менее 64 бит в ширину.A type must be at least 64 bits wide. Стандартный задает отношение размера между целыми типами:The standard specifies a size relationship between the integral types:
Реализация должна поддерживать как минимальные требования к размеру, так и отношение размера для каждого типа.An implementation must maintain both the minimum size requirements and the size relationship for each type. Однако фактические размеры могут и зависеть от реализации.However, the actual sizes can and do vary between implementations. См. раздел для деталей реализации, связанных с Майкрософт.See for Microsoft-specific implementation details.
Ключевое слово можно опустить, если заданы модификаторы, или.The keyword may be omitted when , , or size modifiers are specified. Модификаторы и тип, если они есть, могут использоваться в любом порядке.The modifiers and type, if present, may appear in any order. Например, и следует ссылаться на один и тот же тип.For example, and refer to the same type.
Синонимы целочисленного типаInteger type synonyms
Компилятор считает синонимами следующие группы типов:The following groups of types are considered synonyms by the compiler:
-
, , , , , ,
-
, ,
-
, , , ,
-
, ,
-
, , , , , ,
-
, ,
-
, , , , , ,
-
, ,
Целочисленные типы, определяемые корпорацией Майкрософт , включают в себя конкретные типы,, и .Microsoft-specific integer types include the specific-width , , , and types. Эти типы могут использовать модификаторы и.These types may use the and modifiers. Тип данных аналогичен типу , — типу , — типу , а — типу .The data type is synonymous with type , is synonymous with type , is synonymous with type , and is synonymous with type .
Конструкторы
- Их единственная цель — создавать экземпляры класса. Они вызываются в процессе создания объекта класса.
- Если конструктор с аргументами определен в классе, то нельзя будет работать со стандартным конструктором без аргументов (no-argument constructor) — придется их прописать.
- Java не поддерживает конструктор копирования.
- Имя конструктора и класса совпадает.
- Если конструктор вызывается из другого конструктора с синтаксисом this, то речь идет именно об этом объекте.
- В Java есть стандартный конструктор.
Приватный конструктор:
- Защищает класс от явного превращения в экземпляр.
- Построение объекта возможно только внутри конструктора.
- Используется в шаблоне «Одиночка» (Singleton).
Вопрос: Можно ли синхронизировать конструкторы в Java?
Нет. В Java запрещен многопоточный доступ к конструкторам объекта, поэтому необходимость в синхронизации отсутствует.
Вопрос: Наследуются ли конструкторы? Может ли подкласс вызывать конструктор родительского класса?
Конструкторы не наследуются. При переопределении конструктора суперклассов нарушается инкапсуляция языка. Конструктор родительского класса вызывается ключевым словом super.
Приведение типов
Когда мы производим какие-то действия с переменными, то нужно следить за типами. Нельзя умножать котов на футбольные мячи, это противоречит здравому смыслу. Также и с переменными. Если вы присваиваете переменной одного типа значение другого типа, то вспоминайте теорию. Например, вы без проблем можете присвоить значение типа int переменной типа long, так как все числа из диапазона типа int гарантировано помещаются в диапазон чисел long. В этом случае Java выполнит преобразование автоматически, вы даже ничего не заметите.
Представим обратную картину — мы хотим присвоить переменной типа byte значение типа double. Java не сможет автоматически выполнить ваше желание. Не все числа типа double могут стать числом типа byte. Но часть чисел может, например, число 9. В таком случае используется так называемое приведение типов, чтобы подсказать Java о допустимости операции.
Итак, автоматическое преобразование типов осуществляется, если оба типа совместимы и длина целевого типа больше длины исходного типа. В этом случае происходит преобразование с расширением. Вы всегда можете преобразовать любое число типа byte в число типа int. Такая операция произойдёт без вашего участия автоматически.
Таблица выглядит следующим образом.
Сплошные линии обозначают преобразования, выполняемые без потери данных. Штриховые линии говорят о том, что при преобразовании может произойти потеря точности.
Типы целых чисел и чисел с плавающей точкой совместимы частично. Например, число 5 вполне может быть числом с плавающей точкой (5.0).
Совсем не совместимы, например, char и boolean.
С автоматическим приведением мы разобрались. Рассмотрим вариант, когда нужно преобразовать число типа int в число типа byte. Преобразование автоматически невозможно, поскольку byte меньше int. Но, например, число 99 вполне можно использовать и как int и как byte. В этом случае используется явное приведение типов, то есть преобразование из одного типа в другой (преобразование с сужением).
Выглядит это следующим образом:
Как видите, вы в скобках указываете тип, к которому нужно явно привести переменную.
Существует ещё вариант приведения с усечением. Это когда число с плавающей точкой приводится к целочисленному типу. В этом случае отбрасывается дробная часть (хвост). Например, число 3.14 будет усечено до числа 3:
Если размер целочисленной части слишком велик для целочисленного типа, то значение будет уменьшено до результата деления по модулю на диапазон целевого типа.
Например, попробуйте преобразовать число 454.874 в тип byte:
У меня вывелся удивительный результат: b равно -58.
Рассмотрим такой пример. Допустим у нас есть выражение, где промежуточное значение может выходить за пределы допустимого диапазона:
При умножении переменных a * b промежуточный результат вышел за пределы диапазона допустимых значений для типов byte. Java во время вычисления промежуточных результатов автоматически повышает тип каждого операнда до int и ошибки не происходит.
Это удобно, но может поставить в тупик в следующем примере:
С виду всё правильно. Если не слишком больше число типа byte, а итоговый результат тоже не выходит за диапазон допустимых значений. Но Java не позволит вам написать подобный код. Происходит следующее. Во время вычисления выражения тип операндов был автоматически повышен до int, как об этом говорилось выше. При этом тип результата тоже был повышен до int. Получается, что результат вычисления равен типу int, а мы пытаемся его присвоить переменной b, которая у нас объявлена как byte. И это несмотря на то, что итоговый результат может быть типом byte. Как же выйти из этого положения? Следует использовать явное приведение типов:
Мы рассмотрели единичные примеры. Пора обобщить и запомнить несколько правил.
Типы всех значений byte, short, char повышаются до типа int, как это было рассмотрено выше.
Если один операнд имеет тип long, то тип всего выражения повышается до long.
Если один операнд имеет тип float, то тип всего выражения повышается до float.
Если один операнд имеет тип double, то тип всего выражения повышается до double.
В первом промежуточном выражении (f * b) тип переменной b повышается до float и промежуточный результат также становится float. В следующем выражении (i / c) тип у переменной c повышается до int и промежуточный результат также становится типом int. В выражении (d * s) тип переменной s повышается до double и промежуточное выражение также становится double. В результате у нас появились три промежуточные значения типов: float, int, double. При сложении float и int мы получаем float, затем при вычитании с использованием float и double тип повышается до double, который и становится окончательным типом результата выражения.
Autoboxing и unboxing
Однако даже простые операции с типом писать непросто.
Как мы уже говорили, тип — неизменяемый (immutable). Чтобы создать объект типа с новым значением типа , нужно явно создать новый объект . Зато получить значение типа , которое хранится внутри объекта , просто: нужно вызвать метод .
Пример:
Код | Описание |
---|---|
Оборачиваем в класс Получаем значение из объекта Создаем новое значение |
Код довольно громоздкий, не находите?
Создатели Java тоже так считают, поэтому они научили компилятор делать эти операции автоматически. Автоматическое преобразование в называется autoboxing (box — коробка, класть в коробку), а обратная операция — к — unboxing.
Ваш код | Что видит компилятор |
---|---|
Благодаря autoboxing и unboxing, вы можете спокойно присваивать тип типу и наоборот. Можно писать выражения любой сложности и не делать различий между типами и .
Примеры:
Код | Что сгенерирует компилятор |
---|---|
Что такое Generics в Java?
Дженерики в Java – это термин, обозначающий набор языковых возможностей, связанных с определением и использованием общих типов и методов. Общие методы Java отличаются от обычных типов данных и методов. До Generics мы использовали коллекцию для хранения любых типов объектов, т.е. неуниверсальных. Теперь Generics заставляет программиста Java хранить объекты определенного типа.
Если вы посмотрите на классы платформы Java-коллекции, то увидите, что большинство классов принимают параметр / аргумент типа Object. По сути, в этой форме они могут принимать любой тип Java в качестве аргумента и возвращать один и тот же объект или аргумент. Они в основном неоднородны, т.е. не похожего типа.
Иногда в приложении Java тип данных ввода не является фиксированным. Входными данными могут быть целое число, число с плавающей запятой или строка. Чтобы назначить ввод переменной правильного типа данных, необходимо было провести предварительные проверки.
В традиционном подходе после получения ввода проверяется тип данных ввода, а затем назначается переменная правого типа данных. При использовании этой логики длина кода и время выполнения были увеличены. Чтобы избежать этого, были введены дженерики.
Когда вы используете Generics, параметры в коде автоматически проверяются во время компиляции, и он устанавливает тип данных по умолчанию. Так что это то место, где вам нужна концепция обобщений в Java.
Существует 4 различных способа применения:
- Типовой класс
- Интерфейс
- Метод
- Конструктор
Деление целых и вещественных чисел в Java
При делении целого числа на целое остаток всегда отбрасывается. Как же тогда, скажем, поделить на , чтобы получить ?
Поначалу кажется, что правильный вариант такой:
Однако не все так просто. Дело в том, что Java-машина сначала вычислит значение выражения и только потом присвоит результат в переменную . А деление выполнится нацело. Т.е. будет содержать или, если быть более точным,
Правильный вариант такой: хотя бы одно из чисел, участвующих в делении, нужно записать как вещественное (т.е. с точкой):
В любом из этих выражений будет содержать значение
А как же быть с переменными? Что если у нас есть такой код:
Тут есть хитрое (и очевидное) решение — заставить Java-машину преобразовать переменные в вещественные, умножив их на вещественную единицу —
Обратите внимание, что у операций умножения и деления равный приоритет, и они выполняются слева направо, поэтому имеет значение, где именно мы умножаем на вещественную единицу. Примеры:
Примеры:
Команда | Порядок выполнения | Результат |
---|---|---|