Вітання! Під час проходження JavaRush ти неодноразово стикався з примітивними типами. Ось короткий список того, що ми про них знаємо:
Але, крім значень, типи відрізняються ще й розміром у пам'яті.
Для типу
- Вони не є об'єктами і є значенням, що зберігається в пам'яті
- Примітивні типи бувають кількох видів:
- Цілі числа -
byte
,short
,int
,long
- Числа з плаваючою точкою (дрібні) -
float
іdouble
- Логічний -
boolean
- Символьний (для позначення букв та цифр)
char
- Цілі числа -
- Кожен має свій діапазон значень:
Примітивний тип | Розмір у пам'яті | Діапазон значень |
---|---|---|
byte | 8 біт | від -128 до 127 |
short | 16 біт | до -32768 до 32767 |
char | 16 біт | від 0 до 65536 |
int | 32 біта | від -2147483648 до 2147483647 |
long | 64 біта | від -9223372036854775808 до 9223372036854775807 |
float | 32 біта | від (2 у ступені -149) до ((2-2 у ступені -23)*2 у ступені 127) |
double | 64 біта | від (-2 у ступені 63) до ((2 у ступені 63) - 1) |
boolean | 8 (при використанні в масивах), 32 (при використанні не в масивах) | true або false |
int
займає більше, ніж byte
. А long
- більше, ніж short
. Об'єм займаної примітивами пам'яті можна порівняти з матрьошками: Усередині матрьошки є вільне місце. Чим більше матрьошка – тим більше місця. Всередину великої матрьошки long
ми легко можемо покласти меншу за розміром int
. Вона легко вміститься, і нічого робити додатково не потрібно. У Java під час роботи з примітивами це називається автоматичним перетворенням. Інакше його називають розширенням. Ось простий приклад розширення:
public class Main {
public static void main(String[] args) {
int bigNumber = 10000000;
byte littleNumber = 16;
bigNumber = littleNumber;
System.out.println(bigNumber);
}
}
Тут ми привласнюємо значення byte
змінну int
. Привласнення пройшло успішно і без будь-яких проблем: значення, що зберігається в byte
, займає менший обсяг у пам'яті, ніж "влазить" в int
. "Маленька матрьошка" (значення byte
) легко влазить у "велику матрьошку" (змінну int
). Інша річ, коли ти намагаєшся зробити навпаки — покласти значення великого розміру в змінну, яка не розрахована на такі розміри. Зі справжніми матрьошками такий номер у принципі не пройде, а в Java — минеться, але з нюансами. Давай спробуємо покласти значення int
в змінну short
:
public static void main(String[] args) {
int bigNumber = 10000000;
short littleNumber = 1000;
littleNumber = bigNumber;//помилка!
System.out.println(bigNumber);
}
Помилка! Компілятор розуміє, що ти намагаєшся зробити щось нестандартне і засунути велику матрьошку ( int
) всередину маленької ( short
). Помилка компіляції у разі — попередження від компілятора: “ Гей, ти точно впевнений, що це хочеш? ” Якщо ти певен, говориш про це компілятору: “ Все ок, я знаю, що роблю! ” Цей процес називається явним перетворенням типів, або звуженням . Щоб зробити звуження, тобі необхідно вказати тип, до якого ти хочеш привести своє значення. Іншими словами, відповісти компілятору на його запитання: " Ну і в яку з цих маленьких матрьошок ти хочеш засунути цю велику матрьошку?" ” У нашому випадку це буде виглядати так:
public static void main(String[] args) {
int bigNumber = 10000000;
short littleNumber = 1000;
littleNumber = (short) bigNumber;
System.out.println(littleNumber);
}
Ми явно вказали, що хочемо помістити значення int
у змінну short
та беремо відповідальність на себе. Компілятор, бачачи явну вказівку більш вузький тип, проводить перетворення. Яким буде результат? Висновок: -27008 Трохи несподівано. Чому саме такий? Насправді, все просто. У нас було початкове значення - 10000000 Воно зберігалося в змінній int
, яка займала 32 біти, і в двійковій формі воно виглядало так: Ми записуємо це значення в змінну short
, але вона може зберігати лише 16 біт! Відповідно, лише перші 16 біт нашого числа будуть туди переміщені, решта — відкинуться. У результаті змінну short
потрапить значення Саме тому компілятор “просив підтвердження” у формі явного приведення до конкретного типу. По-перше, воно показує, що ти береш відповідальність за результат на себе, а по-друге, вказує компілятор скільки місця виділити при наведенні типів. Адже якби ми в останньому прикладі приводабо int
до типу byte
, а не до short
, у розпорядженні було б тільки 8 біт, а не 16, і результат був би іншим. Для дробових типів ( float
і double
) звуження відбувається по-своєму. Якщо спробувати привести таке число до цілого типу, у нього буде відкинута дробова частина.
public static void main(String[] args) {
double d = 2.7;
long x = (int) d;
System.out.println(x);
}
Виведення в консоль: 2
Тип даних char
Ти вже знаєш, що тип char використовується для відображення окремих символів.public static void main(String[] args) {
char c = '!';
char z = 'z';
char i = '8';
}
Але він має ряд особливостей, які важливо розуміти. Давай ще раз подивимося в таблицю з діапазонами значень:
Примітивний тип | Розмір у пам'яті | Діапазон значень |
---|---|---|
byte | 8 біт | від -128 до 127 |
short | 16 біт | від -32768 до 32767 |
char | 16 біт | від 0 до 65536 |
int | 32 біта | від -2147483648 до 2147483647 |
long | 64 біта | від -9223372036854775808 до 9223372036854775807 |
float | 32 біта | від (2 у ступені -149) до ((2-2 у ступені -23)*2 у ступені 127) |
double | 64 біта | від (-2 у ступені 63) до ((2 у ступені 63)-1) |
boolean | 8 (при використанні в масивах), 32 (при використанні не в масивах) | true або false |
char
вказаний числовий діапазон від 0 до 65536. Але що це означає? Адже char
— це не лише цифри, а й букви, розділові знаки… Справа в тому, що значення char
зберігаються в Java у форматі Юнікоду. Ми вже стикалися з Юнікодом в одній із минулих лекцій. Ти, напевно, пам'ятаєш, що Unicode — це стандарт кодування символів, що включає знаки майже всіх письмових мов світу. Інакше кажучи, це список спеціальних кодів, у якому знайдеться код майже будь-якого символу з мови. Загальна таблиця Юнікод дуже велика, і, звичайно, її не потрібно вчити напам'ять. Ось, наприклад, її шматочок: Головне — розуміти принцип зберігання значень char
і пам'ятати, що знаючи код конкретного символузавжди можна отримати його у програмі. Давай спробуємо це зробити з якимось випадковим числом:
public static void main(String[] args) {
int x = 32816;
char c = (char) x ;
System.out.println(c);
}
Висновок в консоль: 耰 Саме в такому форматі Java зберігаються символи char
. Кожному символу відповідає число - числовий код розміром 16 біт, або два байти. Юнікоду 32816 відповідає ієрогліф 耰. Зверни увагу на якийсь момент. У цьому прикладі ми використали змінну int
. Вона займає в пам'яті 32 біти , тоді як char
- 16 . Тут ми вибрали int
, тому що потрібне число 32816 знаходиться за межами діапазону short
. Хоча розмір char
, як і short, дорівнює 16 біт, але в діапазоні char
немає негативних чисел, тому "позитивний" діапазон char
вдвічі більше (65536 замість 32767 у short
). Ми можемо використовуватиint
, Поки наш код вкладається в діапазон до 65536. Але якщо створити число int >65536
, воно займатиме більше 16 бітів. І при звуженні типів:
char c = (char) x;
зайві біти будуть відкинуті, і результат буде несподіваним.
Особливості складання char і цілих чисел
Давай розглянемо такий незвичайний приклад:public class Main {
public static void main(String[] args) {
char c = '1';
int i = 1;
System.out.println(i+c);
}
}
Висновок у консоль: 50 O_О Де логіка? 1+1, звідки взялося 50?! Ти вже знаєш, що значення char
зберігаються в пам'яті як числа в діапазоні від 0 до 65536, що позначають юнікод нашого символу. Так ось. Коли ми виробляємо додавання char
і якогось цілого типу, char
перетворюється на число, яке відповідає йому в Юнікоді. Коли в нашому коді ми складали 1 і '1' — символ '1' перетворився на свій код, який дорівнює 49 (можеш перевірити в таблиці вище). Тому результат і дорівнював 50. Давай ще раз візьмемо для прикладу нашого старого друга —耰, і спробуємо скласти його з якимось числом.
public static void main(String[] args) {
char c = '耰';
int x = 200;
System.out.println(c + x);
}
Висновок в консоль: 33016 Ми вже з'ясували, що відповідає коду 32816. А при складанні цього числа і 200 ми отримуємо якраз наш результат — 33016 :) Механізм роботи, як бачиш, досить простий.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ