Студопедия
Новини освіти і науки:
МАРК РЕГНЕРУС ДОСЛІДЖЕННЯ: Наскільки відрізняються діти, які виросли в одностатевих союзах


РЕЗОЛЮЦІЯ: Громадського обговорення навчальної програми статевого виховання


ЧОМУ ФОНД ОЛЕНИ ПІНЧУК І МОЗ УКРАЇНИ ПРОПАГУЮТЬ "СЕКСУАЛЬНІ УРОКИ"


ЕКЗИСТЕНЦІЙНО-ПСИХОЛОГІЧНІ ОСНОВИ ПОРУШЕННЯ СТАТЕВОЇ ІДЕНТИЧНОСТІ ПІДЛІТКІВ


Батьківський, громадянський рух в Україні закликає МОН зупинити тотальну сексуалізацію дітей і підлітків


Відкрите звернення Міністру освіти й науки України - Гриневич Лілії Михайлівні


Представництво українського жіноцтва в ООН: низький рівень культури спілкування в соціальних мережах


Гендерна антидискримінаційна експертиза може зробити нас моральними рабами


ЛІВИЙ МАРКСИЗМ У НОВИХ ПІДРУЧНИКАХ ДЛЯ ШКОЛЯРІВ


ВІДКРИТА ЗАЯВА на підтримку позиції Ганни Турчинової та права кожної людини на свободу думки, світогляду та вираження поглядів



Види приведення даних

Вступ

План

1 Вступ

2 Змінні

3 Примітивні та вказівні типи даних

4 Види приведення даних

5 Перетворення до рядку

6 Заборонені перетворення

 

Java є суворо типізовані мовою. Це означає, що будь-яка змінна і будь-який вираз мають визначений тип ще на момент компіляції. Таке суворе правило дозволяє виявляти багато помилок вже під час компіляції. Компілятор, знайшовши помилку, вказує точне місце (рядок) і причину її виникнення. Тому чітке розуміння моделі типів даних в Java дуже допомагає в написанні якісних програм.

Всі типи даних поділяються на дві групи. Першу складають 8 простих, або примітивних (від англійського primitive), типів даних. Вони, в свою чергу, поділяються на три підгрупи:

- цілочисельні

- byte

- short

- int

- long

- char (також є цілочисловим типом)

- дробові

- float

- double

- булеві

- boolean

Другові групу складають об'єктні, або вказівні (від англійського reference), типи даних. Це всі класи, інтерфейси та масиви. Ілюструвати логіку роботи з типами даних простіше всього на прикладі змінних.

2 Змінні

Змінні використовуються в програмі для зберігання даних. Будь-яка змінна має три базових характеристики:

- ім'я;

- тип;

- значення.

Крім того, при оголошенні змінної може бути використано ключове слово final. Його вказують перед типом змінної, і тоді її необхідно відразу ініціалізувати й вже більше ніколи не змінювати її значення. Таким чином, final-змінні стають чимось на зразок констант, алі насправді деякі ініціалізатори можуть обчислюватися тільки під час виконання програми, генеруючи різні значення.

3 Примітивні й вказівні типи даних

Тепер на прикладі змінних можна проілюструвати відмінність між примітивними й вказівними типами даних. Розглянемо приклад, коли оголошуються дві змінні одному типу, прирівнюються один одному, а потім значення однієї з їх змінюється. Що станеться з другої змінної?

Візьмемо простий тип int:

 

int a = 5; // оголошуємо першу змінну і інициализуємо її

int b = a; // оголошуємо другові змінну й прирівнюємо її до першої

a = 3; // змінюємо значення першої

print (b); // перевіряємо значення другої

 

Тут і далі ми вважаємо, що функція print (...) дозволяє нам (неважливо, яким саме способом) дізнатися значення її аргументу (як правило, для цього використовують функцію зі стандартної бібліотеки System.out.println (...), яка виводить значення на системну консоль).

В результаті ми побачимо, що значення змінної b не змінилося, воно залишилося рівним 5. Це означає, що змінні простого типу зберігають безпосередньо свої значення й при прирівнювання двох змінних відбувається копіювання даного значення.

Тепер розглянемо вказівний тип даних. Змінні таких типів завжди зберігають посилання на деякі об'єкти. Розглянемо для прикладу клас, що описує крапку на координатній площині з цілочисельними координатами:

 

class Point {

int x, y;

}

 

Тепер складемо приклад, аналогічний наведеному вище для int-змінних, вважаючи, що вираз new Point (3,5) створює новий об’єкт крапки з координатами (3,5).

 

Point p1 = new Point (3,5);

Point p2 = p1;

p1.x = 7;

print (p2.x);

 

У третьому рядку ми змінили горизонтальну координату крапки, на якупосилалась змінна p1, і тепер нас цікавить, як це позначилося на точці, на яку посилається змінна p2. Зробивши такий експеримент, можна переконатись, що цього разу ми побачимо оновлене значення. Тобто об'єктні змінні після прирівнювання залишаються «пов'язаними»одна з одною, зміни однієї позначаються на іншій.

Таким чином, примітивні змінні є дійсними сховищами даних. Кожна змінна має значення, яке не залежить від інших. Посилальні ж змінні зберігають лише посилання на об'єкти, причому різні змінні можуть посилатися на один і тої самий об'єкт, як це було в нашому прикладі.

Пам'ять в Java з точки зору програміста представляється не нулями та одиницями або набором байтів, а як певний віртуальний простір, в якому існують об'єкти. І доступ до пам'яті здійснюється не за фізичною адресою або вказівником, а лише через посилання на об'єкти. Посилання повертається при створенні об'єкта й далі може бути збережене в змінну, передане в якості аргументу і т.д. Як вже говорилося, допускається наявність декількох посилань на один об'єкт. Можлива й протилежна ситуація - коли на якийсь об'єкт не існує жодного посилання. Такий об'єкт вже недоступний програмі і є «сміттям», тобто займає апаратні ресурси. Для їхнього звільнення не потрібно ніяких зусиль. До складу будь-віртуальної машини обов'язково входить автоматичний прибиральник сміття «garbage collector» - фоновий процес, який якраз і займається знищенням непотрібних об'єктів.

Дуже важливо пам'ятати, що об'єктна змінна, на відміну від примітивної, може мати значення іншого типу, яка не збігається з типом змінної. Наприклад, якщо тип змінної - якийсь клас, то змінна може посилатися на об'єкт, породжений від спадкоємця цього класу.

В Java передбачено сім видів приведення:

- тотожне (identity);

- розширення примітивного типу (widening primitive);

- звуження примітивного типу (narrowing primitive);

- розширення об'єктного типу (widening reference);

- звуження об'єктного типу (narrowing reference);

- перетворення до рядка (String);

- заборонені перетворення (forbidden).

Розглянемо їх окремо.

Перетворення примітивних типів (розширення та звуження)

Для простих типів розширення означає, що здійснюється перехід від менш місткого типу до більш місткого. Наприклад, від типу byte (довжина 1 байт) до типу int (довжина 4 байти). Такі перетворення безпечні в тому сенсі, що новий тип завжди гарантовано вміщає в собі всі дані, які зберігалися в старому типі, і таким чином не відбувається втрати даних. Саме тому компілятор здійснює його сам, непомітно для розробника:

 

byte b = 3;

int a = b;

 

Наступні 19 перетворень є такими, що розширяються

від byte до short, int, long, float, double

від short до int, long, float, double

від char до int, long, float, double

від int до long, float, double

від long до float, double

від float до double

Тим не менш, слід пам'ятати, що навіть при розширені, дані все-таки можуть бути в особливих випадках спотворені.

 

Зворотне перетворення (звуження) - означає, що перехід здійснюється від більш місткого типу до менш місткого. При такому перетворенні є ризик втратити дані. Наприклад, якщо число типу int було більше 127, то при приведенні його до byte значення бітів старше восьмого будуть втрачені. В Java таке перетворення повинно відбуватися явним чином, тобто програміст в коді винний явно вказати, що він має намір здійснити таке перетворення і готовий при необхідності втратити дані.

Наступні 23 перетворення є звужуючими:

від byte до char

від short до byte, char

від char до byte, short

від int до byte, short, char

від long до byte, short, char, int

від float до byte, short, char, int, long

від double до byte, short, char, int, long, float

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

Перетворення вказівних типів (розширення та звуження)

Переходимо до вказівнихтипів. Перетворення об'єктних типів найкраще ілюструється за допомогою дерева наслідування. Розглянемо невеликий приклад наслідування:

 

// Оголошуємо клас Parent

class Parent {

int x;

}

// Оголошуємо клас Child і успадковуємо

// Його від класу Parent

class Child extends Parent {

int y;

}

// Оголошуємо іншого спадкоємця

// Класу Parent - клас Child2

class Child2 extends Parent {

int z;

}

 

В класі оголошено поле з унікальним ім'ям. Будемо розглядати це поле як приклад набору унікальних властивостей, притаманних деякого об'єктному типу.

Три оголошених класи можуть породжувати три види об'єктів. Об'єкти класу Parent володіють тільки одним полем x, а значити, тільки вказівники типу Parent можуть посилатися на такі об'єкти. Об'єкти класу Child володіють полем y і полем x, отриманим у спадок від класу Parent. Стало бути, на такі об'єкти можуть вказувати вказівник типу Child або Parent. Другий випадок вже ілюструвався таким прикладом:

 

Parent p = new Child ();

 

Зверніть увагу, що за допомогою такого вказівника p можна звертатися лише до поля x створеного об'єкту. Поле y недоступні, тому що компілятор, перевіряючи коректність висловлювання p.y, не можна передбачити, що посилання p буде вказувати на об'єкт типу Child під година виконання програми. Він аналізує лише тип самої змінної, а вона оголошена як Parent, алі в цьому класі немає поля y, що й викличе помилку компіляції.

Аналогічно, об'єкти класу Child2 володіють полем z і полем x, отриманим у спадок від класу Parent. Значити, на такі об'єкти можуть вказувати посилання типу Child2 або Parent.

Таким чином, посилання типу Parent можуть вказувати на об'єкт будь-якого з трьох розглянутих типів, а посилання типу Child і Child2 - тільки на об'єкти точно такого ж типу. Тепер можна перейти до перетворення вказівних типів на основі такого дерева наслідування.

Розширення означає перехід від більш конкретного типу до менш конкретного, тобто перехід від дітей до батьків. У нашому прикладі перетворення від будь-якого спадкоємця (Child, Child2) до батьків (Parent) є розширення, перехід до більш загального типу. Подібно до випадку з примітивними типами, цей перехід здійснюється самою JVM при необхідності й непомітно для розробника, тобто не вимагає ніяких додаткових зусиль, оскільки він завжди проходити успішно: завжди можна звертатися до об'єкта, породженому від спадкоємця, за типом його батька.

 

Parent p1 = new Child ();

Parent p2 = new Child2 ();

 

В обох рядках змінним типу Parent присвоюється значення іншого типу, а значити, відбувається перетворення. Оскільки це розширення, воно виконується автоматично й завжди успішно.

Зверніть увагу, що при подібному перетворенні з самим об'єктом нічого не відбувається. Незважаючи на ті, що, наприклад, поле y класу Child тепер недоступні, це не означає, що воно зникло. Така істотна зміна структури об'єкта неможлива. Вона була породжена від класу Child і зберігає всі його властивості. Змінився лише тип посилання, через яку йде звернення до об'єкта. Цю ситуацію можна умовно порівняти з розгляданням якогось предмета через підзорну трубу. Якщо перейти від сурми з великим збільшенням до більш слабкої, те видимих деталей стані менше, алі сам предмет, звичайно, ніяк від цього не зміниться.

Наступні перетворення є розширюючі:

- від класу A до класу B, якщо A успадковується від B (важливим окремим випадком є перетворення від будь-якого посилального типу до Object);

- від null-типу до будь-якого об'єктному типу.

Другий випадок ілюструється таким прикладом:

 

Parent p = null;

 

Порожнє посилання null не має будь-якого конкретного вказівного типу, тому іноді говорять про спеціальний null-тип. Однак на практиці важливо, що таке значення можна прозоро перетворити до будь-якого об'єктному типу.

З вивченням інших вказівних типів (інтерфейсів і масивів) цей список буде розширюватися.

Зворотний перехід, тобто рух по дереву успадкування долілиць, до спадкоємців, є звуженням. Наприклад, для розглянутого випадку, перехід від вказівного типу Parent, який може посилатися на об'єкти трьох класів, до посилання типу Child, яку може посилатися на об'єкти лише одного з трьох класів, мабуть, є звуженням. Такий перехід може виявитися неможливим. Якщо посилання типу Parent посилається на об'єкт типу Parent або Child2, то перехід до Child неможливий, адже в обох випадках об'єкт не має поле y, яку оголошено в класі Child. Тому при звуженні розробнику необхідно явним чином вказувати на ті, що необхідно спробувати провести таке перетворення. JVM під година виконання перевірить коректність переходу. Якщо він можливий, перетворення буде проведено. Якщо ж ні - виникне помилка.

 

Parent p = new Child ();

Child c = (Child) p; // Перетворення буде успішним.

Parent p2 = new Child2 ();

Child c2 = (Child) p2; // Під година виконання виникне помилка!

 

Щоб перевірити, чи можливий бажаний перехід, можна скористатися оператором instanceof:

 

Parent p = new Child ();

if (p instanceof Child) {

Child c = (Child) p;

}

Parent p2 = new Child2 ();

if (p2 instanceof Child) {

Child c = (Child) p2;

}

Parent p3 = new Parent ();

if (p3 instanceof Child) {

Child c = (Child) p3;

}

 

У даному прикладі помилок не виникне. Перше перетворення можливе, і воно буде здійснено. В іншому й третьому випадках умови операторів if не спрацюють і спроб некоректного переходу не буде.

На даний момент можна назвати лише одне звужуюче перетворення: від класу A до класу B, якщо B успадковується від A (важливим окремим випадком є звуження типу Object до будь-якого іншого посилального типу). З вивченням інших документів, які типів (інтерфейсів і масивів) цей список буде розширюватися.


Читайте також:

  1. Аналіз паралельного інтерейсу з DSP-процесорами: запис даних в ЦАП, що під’єднаний до адресного простору пам’яті
  2. Аналіз паралельного інтерфейсу з DSP-процесорами: читання даних з АЦП, що під’єднаний до адресного простору пам’яті
  3. Аналіз статистичних даних про склад та плинність кадрів, які обіймали керівні
  4. Аналіз та інтерпретація одержаних даних
  5. Архіватори даних.
  6. Архітектура баз даних
  7. Аудит розрахунків за відшкодуванням завданих збитків
  8. Бази даних АС ДЗК
  9. Бази даних як засіб зберігання й обробки інформації
  10. Банки даних
  11. Безпека даних
  12. Введення даних




Переглядів: 1679

<== попередня сторінка | наступна сторінка ==>
Мультиплексування | Масиви як тип даних в Java

Не знайшли потрібну інформацію? Скористайтесь пошуком google:

  

© studopedia.com.ua При використанні або копіюванні матеріалів пряме посилання на сайт обов'язкове.


Генерація сторінки за: 0.027 сек.