Типы данных JAVA

Вопросы к собеседованию

Вопросы:

Какие примитивные типы данных есть в Java

Два основных типа: примитивы и ссылочные

Примитивы

Object Number Numeric types
Byte
Short
Long
Integer
Floating-Point Types
Double
Float
Character
Boolean

Инициализация:

Числа 0 или 0.0

boolean = false

Объекты (в том числе Sting) = null

Целочисленные типы

Тип Умолчание Память Допустимо
byte 0 8 бит от -128 до 127
short 0 16 бит от -32_768 до 32_767
char '\u0000' 16 бит от 0 до 65_536
беззнаковое целое число, представляющее собой символ UTF-16 (буквы и цифры)
int 0 32 бита от -2_147_483_648 до  2_147_483_647
long 0L 64 бита от -9_223_372_036_854_775_808L до  9_223_372_036_854_775_807L

Типы с плавающей точкой

Тип Умолчание Память Допустимо
float 0.0f 32 бита от 1.4e-45f до 3.4e+38f
double 0.0d 64 бита от 4.9e-324 до 1.7e+308

Логический тип

Тип Умолчание Память Допустимо
boolean false 8 бит (в массивах)
32 бит (не в массивах - int)
true (истина) или false (ложь)

Что такое классы-обертки?

Значение примитива становится объектом, чтобы выполнять различные операции ( .parseInt()).

Примитивы не имеют методов.

Объекты классов-оберток являются неизменяемыми (Immutable).

Что такое автоупаковка и автораспаковка?

Для присваивания ссылок-примитивов объектам их классов-оберток (и наоборот) не требуется ничего делать, все происходит автоматически

int x = 7;

Integer y = 111;

x = y; // автораспаковка

y = x * 123; // автоупаковка

Автоупаковка и распаковка не работают для массивов.

Автоупаковка - это механизм неявной инициализации объектов классов-оберток (Byte, Short, Integer, Long, Float, Double, Character, Boolean) значениями соответствующих им исходных примитивных типов (byte, short, int...), без явного использования конструктора класса.

Автоупаковка происходит при прямом присваивании примитива классу-обертке (с помощью оператора =), либо при передаче примитива в параметры метода (типа класса-обертки).

Автоупаковке в классы-обертки могут быть подвергнуты как переменные примитивных типов, так и константы времени компиляции (литералы и final-примитивы). При этом литералы должны быть синтаксически корректными для инициализации переменной исходного примитивного типа.

Автоупаковка переменных примитивных типов требует точного соответствия типа исходного примитива типу класса-обертки.

Например, попытка упаковать переменную типа byte в Short, без предварительного явного приведения byte в short вызовет ошибку компиляции.

Автоупаковка констант примитивных типов допускает более широкие границы соответствия.

В этом случае компилятор способен предварительно осуществлять неявное расширение/сужение типа примитивов.

Неявное расширение/сужение исходного типа примитива до типа примитива соответствующего классу-обертке (для преобразования int в Byte, сначала компилятор самостоятельно неявно сужает int к byte) автоупаковку примитива в соответствующий класс-обертку.

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

a) присвоение примитива обертке может производится только оператором = (нельзя передать такой примитив в параметры метода без явного приведения типов)

b) тип левого операнда не должен быть старше чем Character, тип правого не должен старше, чем int.

Допустимо расширение/сужение byte в/из short, byte в/из char, short в/из char и только сужение byte из int, short из int, char из int. Все остальные варианты требуют явного приведения типов.

Дополнительной особенностью целочисленных классов-оберток созданных автоупаковкой констант в диапазоне -128 ... +127 является то, что они кэшируются JVM. Поэтому такие обертки с одинаковыми значениями будут являться ссылками на один объект.

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

Что такое явное и неявное приведение типов?
В каких случаях в JAVA нужно использовать явное приведение?

Каждое выражение и каждая переменная имеет строго определенный тип уже на момент компиляции. Механизм приведения типов (casting) - способ преобразования значения переменной одного типа в значение другого типа:

Неявные – выполняются автоматически (расширяющие преобразования, сужающие с потерей данных: int->float, long->float, long->double).

Явные – надо указывать тип (сужающие преобразования от типа с большей разрядностью к типу с меньшей разрядностью). Потеря данных (старшие биты будут потеряны).

Схема приведения типов
↓ИЗ | В→ boolean byte short char int long float double
boolean - N N N N N N N
byte N - Y C Y Y Y Y
short N C - C Y Y Y Y
char N C C - Y Y Y Y
int N C C C - Y Y* Y
long N C C C C - Y* Y*
float N C C C C C - Y
double N C C C C C C -

N - невозможно

Y - автоматически

C - сужающее преобразование, требующее явного приведения

Y* - автоматическое расширяющее преобразование, в процессе которого значение может потерять некоторые из наименее значимых разрядов

Разновидности приведения:

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

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

Parent parent = new Child();

if (parent instanceof Child) {

Child child = (Child) parent;

}

Что такое пул интов?

Для более эффективного использования памяти, в JAVA используются так называемые пулы.

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

Особенность Integer-пула — он хранит только числа, которые помещаются в тип данных byte: от -128 до 127. Для остальных чисел пул не работает.

Integer i1 = 127;

Integer i2 = 127;

Integer i3 = 128;

Integer i4 = 128;

System.out.println(i1==i2); //true

System.out.println(i3==i4); //false

Какие нюансы у строк в Java?

Это неизменяемый (immutable) (не меняется после создания) и финализированный (без потомков) тип данных; потокобезопасный может использоваться в многопоточке

Каждый объект можно привести к строке .toString

Что такое пул строк?

Пул строк – это набор строк хранящийся в Heap .

Пул строк возможен благодаря неизменяемости строк в Java и реализации идеи интернирования строк;

Интернирование строк — это механизм, при котором одинаковые литералы представляют собой один объект в памяти.

Пул строк помогает экономить память, но по этой же причине создание строки занимает больше времени;

Когда для создания строки используются ", то сначала ищется строка в пуле с таким же значением, если находится, то просто возвращается ссылка, иначе создается новая строка в пуле, а затем возвращается ссылка на неё;

String habr = "habrahabr"; - “строковый литерал”

При использовании оператора new создается новый объект String. Затем при помощи метода intern() эту строку можно поместить в пул или же получить из пула ссылку на другой объект String с таким же значением;

String q = "I'm";

String w = "I'm";

String r = new String("I'm");

String t = r.intern();

System.out.println("q==w: " + (q==w)); //true

System.out.println("q==r: " + (q==r)); //false

System.out.println("q==t: " + (q==t)); //true

Пул строк является примером паттерна «Приспособленец» (Flyweight).

Почему String неизменяемый и финализированный класс?

Какая основная разница между String, StringBuffer, StringBuilder?

Класс StringString является неизменяемым (immutable) - модифицировать объект такого класса нельзя, можно лишь заменить его созданием нового экземпляра.

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

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

Класс StringJoiner используется, чтобы создать последовательность строк, разделенных разделителем с возможностью присоединить к полученной строке префикс и суффикс:

StringJoiner joiner = new StringJoiner(".", "prefix-", "-suffix");

for (String s : "Hello the brave world".split(" ")) {

joiner.add(s);

}

System.out.println(joiner); //prefix-Hello.the.brave.world-suffix

Что такое сигнатура метода?

Объявление метода - это весь код, который описывает метод.

public String methodName(double a, int b) throws IOException

модификатор_доступа + тип_возвращаемого_значения + имя_метода(список_параметров) + исключения

Сигнатура - название метода и типы параметров в определенном порядке. methodName(double, int)

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

String methodName(double a, int b) throws IOException

Насколько я понял модификатор доступа ни куда не входит.

Каким образом переменные передаются в методы, по значению или по ссылке?

Java передает параметры по значению. Всегда.

int х = 3;

int у = х;

Скопировать значение внутри х и записать эту копию в у.

Cat A = new Cat();

Cat B = A;

Ссылка А копируется в ссылку B. К объекту это не относится — у нас по-прежнему всего один объект. Но теперь есть две различных ссылки, контролирующие один и тот же объект Cat.