Вітання! Ти вже непогано знайомий із примітивними типами, і чимало з ними попрацював.
У примітивів у програмуванні, і Java зокрема, є безліч переваг: вони займають мало пам'яті, за рахунок чого підвищується ефективність роботи програми, і чітко розділені по діапазонах значень. Однак у процесі вивчення Java ми неодноразово, немов мантру, повторювали — “ в Java все є об'єктом ”. Адже примітиви — пряме спростування цих слів. Об'єктами вони є. Виходить, принцип "все є об'єктом" є хибним? Насправді ні. У Java у кожного примітивного типу є свій брат-близнюк - клас-обгортка (
Об'єкти класів обгорток створюються так само, як і будь-які інші:
Давай розберемося з цим поняттям. Як ми з тобою вже дізналися раніше, Java - об'єктно-орієнтована мова. Це означає, що всі програми, написані Java, складаються з об'єктів. Примітиви є об'єктами. Але при цьому змінної класу-обгортки можна надавати значення примітивного типу. Цей процес називається автоупаковкою ( autoboxing ). Так само змінної примітивного типу можна надавати об'єкт класу-обгортки. Цей процес називається авторапакуванням (autounboxing) . Наприклад:
У примітивів у програмуванні, і Java зокрема, є безліч переваг: вони займають мало пам'яті, за рахунок чого підвищується ефективність роботи програми, і чітко розділені по діапазонах значень. Однак у процесі вивчення Java ми неодноразово, немов мантру, повторювали — “ в Java все є об'єктом ”. Адже примітиви — пряме спростування цих слів. Об'єктами вони є. Виходить, принцип "все є об'єктом" є хибним? Насправді ні. У Java у кожного примітивного типу є свій брат-близнюк - клас-обгортка ( Wrapper). Що таке обгортка? Обгортка - це спеціальний клас, який зберігає в собі значення примітиву. Але оскільки це саме клас, він може створювати свої екземпляри. Вони зберігатимуть усередині потрібні значення примітивів, у своїй будуть справжніми об'єктами. Назви класів-оберток дуже схожі на назви відповідних примітивів, або повністю з ними збігаються. Тож запам'ятати їх буде дуже легко.
| Wrapper Classes for Primitive Data Types | |
|---|---|
| Primitive Data Types | Wrapper Classes |
| int | Integer |
| short | Short |
| long | Long |
| byte | Byte |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
public static void main(String[] args) {
Integer i = new Integer(682);
Double d = new Double(2.33);
Boolean b = new Boolean(false);
} Класи-обертки дозволяють нівелювати недоліки, які є у примітивних типів. Найочевидніший із них — примітиви не мають методів . Наприклад, у них немає методу toString(), тому ти не зможеш, наприклад, перетворити число intна рядок. А ось із класом-обгорткою Integer— запитто.
public static void main(String[] args) {
Integer i = new Integer(432);
String s = i.toString();
} Виникнуть складнощі і зі зворотним перетворенням. Припустимо, у нас є рядок, про який ми точно знаємо, що він містить число. Тим не менш, у випадку з примітивним типом intми ніяк не зможемо це число з рядка дістати і перетворити, власне, на число. Але завдяки класам-обгорткам така можливість у нас з'явилася.
public static void main(String[] args) {
String s = "1166628";
Integer i = Integer.parseInt(s);
System.out.println(i);
}Висновок: 1166628 Ми успішно отримали число з рядка і привласнабо його в змінну-посилання Integer i. До речі, щодо посилань. Ти вже знаєш, що параметри передаються в методи по-різному: примітиви за значенням, а об'єкти за посиланням. Ти можеш використовувати це знання при створенні своїх методів: якщо твій метод працює, наприклад, з дробовими числами, але тобі потрібна логіка саме передачі за посиланням, ти можеш передати метод параметри Double/Floatзамість double/float. Крім того, крім методів у класах-обгортках є дуже зручні для використання статичні поля. Наприклад, уяви, що перед тобою зараз стоїть завдання: вивести в консоль максимально можливе число int, а потім мінімально можливе. Завдання начебто елементарне, а все одно — без гугла навряд чи впораєшся. А класи-обгортки легко дозволяють вирішувати такі “побутові завдання”:
public class Main {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
}
} Такі поля дозволяють не відволікатися від більш серйозних завдань. Не кажучи вже про те, що в процесі друку числа 2147483647 (це якраз MAX_VALUE) не дивно і опечататися:) Крім того, в одній з минулих лекцій ми вже звертали увагу на те, що об'єкти класів- обгорток є незмінними (Immutable) .
public static void main(String[] args) {
Integer a = new Integer(0);
Integer b = new Integer(0);
b = a;
a = 1;
System.out.println(b);
}Висновок: 0 Об'єкт, на який спочатку вказувало посилання а, не змінив свій стан, інакше значення bтеж змінилося б. Як і у випадку зіStringзамість зміни стану об'єкта-обгортки в пам'яті створюється абсолютно новий об'єкт. Чому ж творці Java, зрештою, вирішабо залишити у мові примітивні типи? Якщо все має бути об'єктом, і ми вже маємо класи-обгортки, якими можна висловити все, що виражають примітиви, чому взагалі не залишити в мові тільки їх, а примітиви видалити? Відповідь проста - продуктивність. Примітивні типи тому і називають примітивними, тому що вони позбавлені багатьох "важкоатлетних" особливостей об'єктів. Так, об'єкт має багато зручних методів, але ж вони не завжди тобі потрібні. Іноді тобі потрібно просто число 33, чи 2,62, чи значення true/ false. У ситуаціях, коли всі переваги об'єктів не мають значення і не потрібні для роботи програми, примітиви впораються із завданням набагато краще.
Автоупаковка/автораспаковка
Однією з особливостей примітивів та їх класів-обгорток у Java є автоупаковка/автораспаковка (Autoboxing/Autounboxing)
Давай розберемося з цим поняттям. Як ми з тобою вже дізналися раніше, Java - об'єктно-орієнтована мова. Це означає, що всі програми, написані Java, складаються з об'єктів. Примітиви є об'єктами. Але при цьому змінної класу-обгортки можна надавати значення примітивного типу. Цей процес називається автоупаковкою ( autoboxing ). Так само змінної примітивного типу можна надавати об'єкт класу-обгортки. Цей процес називається авторапакуванням (autounboxing) . Наприклад:
public class Main {
public static void main(String[] args) {
int x = 7;
Integer y = 111;
x = y; // авторопакування
y = x * 123; // Автоупаковка
}
} У рядку 5 ми присвоюємо примітиву x значення y, який є об'єктом класу-обгортки Integer. Як бачиш, ніяких додаткових дій для цього не потрібно: компілятор знає що intі Integer, по суті, те саме . Це і є авторопакування. Також відбувається і автоупаковка в рядку 6: об'єкту y легко присвоюється значення примітивів (х*123). Це приклад автоупаковки. Саме тому додається слово "авто": для присвоєння посилань-примітивів об'єктам їх класів-оберток (і навпаки) не потрібно нічого робити, все відбувається автоматично . Зручно, правда? :) Ще одна дуже велика зручність автоупаковки/автораспаковки проявляється в роботі методів. Справа в тому, що параметри методів теж підлягають автоупаковці та автоупаковці. І, наприклад, якщо один із них приймає на вхід два об'єкти Integer— ми легко можемо передати туди звичайні примітиви int!
public class Main {
public static void main(String[] args) {
printNumber(7);//Звичайний int, навіть без змінної
}
public static void printNumber(Integer i) {
System.out.println("Ви ввели число" + i);
}
}Висновок: Ви ввели число 7 Так само працює і навпаки:
public class Main {
public static void main(String[] args) {
printNumber(new Integer(632));
}
public static void printNumber(int i) {
System.out.println("Ви ввели число" + i);
}
} Важливий момент, про який потрібно пам'ятати: автоупаковка та розпакування не працюють для масивів !
public class Main {
public static void main(String[] args) {
int[] i = {1,2,3,4,5};
printArray(i);//помилка, не компілюється!
}
public static void printArray(Integer[] arr) {
System.out.println(Arrays.toString(arr));
}
} Спроба передати масив примітивів метод, який приймає на вхід масив об'єктів, викличе помилку компіляції. Насамкінець, ще раз коротко порівняємо примітиви та обгортки .
- мають перевагу у продуктивності
- Дозволяють не порушувати принцип “все є об'єктом”, завдяки чому цифри, символи та булеві значення true/false не випадають із цієї концепції
- Розширюють можливості роботи з цими значеннями, надаючи зручні методи та поля
- Необхідно, коли якийсь метод може працювати виключно з об'єктами
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ