1. Властивості: геттер і сетер
Коли великий проєкт розробляють десятки програмістів одночасно, часто виникають проблеми з тим, що вони по-різному ставляться до даних, які зберігаються в полях класу.
Ніхто детально не вивчає документацію по класах, або в ній можуть бути не описані всі випадки, тому часто можуть виникати ситуації, коли дані всередині об'єкта «псуються», і об'єкт стає невалідним.
Щоб уникнути таких ситуацій, у Java прийнято всі поля класу робити private. Тільки методи класу можуть змінювати змінні класу, і жодні методи з інших класів не мають доступу до змінних класу напряму. Ось так.
Якщо ви хочете, щоб інші класи могли отримувати або змінювати дані всередині об'єктів вашого класу, ви повинні додати в код вашого класу два методи — get-метод і set-метод. Приклад:
| Код | Примітка |
|---|---|
|
private-поле nameІніціалізація поля через конструктор getName()— метод повертає значення поля namesetName()— метод змінює значення поля name |
Жоден інший клас не зможе змінити значення поля name напряму. Якщо комусь потрібно отримати значення поля name, йому доведеться викликати метод getName() у об'єкта типу Person. Якщо якийсь код хоче змінити значення поля name, йому потрібно буде викликати метод setName() у об'єкта типу Person.
Метод getName() ще називають «геттер поля name», а метод setName() — «сетер поля name».
Це дуже поширений підхід. У 80-90% всього Java коду ви ніколи не побачите публічні змінні класу. Замість цього вони будуть оголошені private (або protected), і у кожної змінної будуть публічні геттери та сеттери.
Цей підхід робить код довшим, але надійнішим.
Звертання до змінної класу напряму — це як поворот через подвійну суцільну: простіше і швидше, але якщо так будуть робити всі, то всім же буде від цього і гірше.
Припустимо, ви хочете створити клас, що описує точку на площині x і y. Ось як це зробив би програміст-початківець:
class Point
{
public int x;
public int y;
}
А ось як це зробив би досвідчений Java-програміст:
| Код |
|---|
|
Код став довшим? Безсумнівно.
Зате в сетери та геттери можна додати валідацію параметрів. Наприклад, можна слідкувати за тим, щоб x і y завжди були більше нуля (або не менше нуля). Приклад:
| Код | Примітка |
|---|---|
|
2. Час життя об'єкта
Ви вже знаєте, що об'єкти створюються за допомогою оператора new, а ось як об'єкти видаляються? Не існують же вони вічно — для цього жодної пам'яті не вистачить.
У багатьох мовах програмування, таких як С++, для видалення об'єкта є спеціальний оператор delete. А як ситуація з цим в Java?
У Java все влаштовано трохи інакше, і оператора delete в Java немає. Чи означає це, що об'єкти в Java не видаляються? Ні, видаляються звісно ж. Інакше в Java-додатках швидко закінчилася б пам'ять, і ні про які місяці безперервної роботи не йшло б і мови.
У Java процес видалення об'єктів повністю автоматизований – видаленням об'єктів займається сама Java-машина. Такий процес називається збиранням сміття (garbage collecting), а механізм, що збирає сміття — збирачем сміття — Garbage Collector, або скорочено GC.
Як Java-машина дізнається, що якийсь об'єкт потрібно видалити і коли?
Усі об'єкти збирач сміття ділить на досяжні та недосяжні. Якщо на об'єкт є хоча б одне посилання, він вважається досяжним. Якщо немає жодної змінної, яка посилається на об'єкт, такий об'єкт вважається недосяжним і оголошується сміттям: значить, його можна видаляти.
У Java не можна взяти й створити посилання на існуючий об'єкт: її можна лише призначити. Якщо ми стерли всі посилання на об'єкт, він втрачений назавжди.
Циклічні посилання
Попередня логіка звучить відмінно, поки ми не придумуємо простий контрприклад: у нас є два об'єкти, які посилаються один на одного (зберігають посилання один на одного). Більше ніхто жодних посилань на ці об'єкти не зберігає.
До цих об'єктів не можна звернутися з решти коду, проте посилання на них усе ж є.
Саме тому збирач сміття ділить об'єкти не на «об'єкти з посиланнями» і «об'єкти без посилань», а на досяжні та недосяжні.
Досяжні об'єкти
Спочатку у список досяжних додаються ті об'єкти, які 100% живі. Наприклад, поточний потік (Thread.current()) або Консоль (System.in).
Потім список досяжних об'єктів поповнюють ті, на які посилаються перші досяжні об'єкти. Потім ті, на кого посилаються другі, і т.д.
Таким чином, якщо є якась група об'єктів, які посилаються тільки один на одного, але від досяжних об'єктів до них ніяк дістатися не можна, такі об'єкти будуть вважатися сміттям і будуть видалені.
3. Збирання сміття
Фрагментація пам'яті
Ще один важливий момент, пов'язаний із видаленням об'єктів — фрагментація пам'яті. Якщо постійно створювати та видаляти об'єкти, скоро вся пам'ять буде впереміш: області зайнятої пам'яті будуть постійно перемежовуватись пустими областями.
І легко може трапитися ситуація, коли ми не можемо створити великий об'єкт (наприклад, масив на мільйон елементів), тому що немає великого куска вільної пам'яті. Тобто вільна пам'ять начебто і є, і багато, але от великого цільного куска вільної пам'яті може і не бути.
Оптимізація (дефрагментація) пам'яті
Java-машина вирішує цю проблему специфічним чином. Виглядає це приблизно так:
Пам'ять ділиться на дві частини. Усі об'єкти створюються (і видаляються) лише в одній її половині. Коли настає час прибрати дірки в пам'яті, всі об'єкти з першої половини копіюються в другу половину. Але копіюються вже впритул одна до одної, щоб дір не було.
Виглядає цей процес приблизно так:
Етап 1: Після створення об'єктів

Етап 2: Поява «дір»

Етап 3: Усунення «дір»

Таким чином, навіть не потрібно видаляти об'єкти. Java-машина просто копіює всі досяжні об'єкти в нове місце, а всю область пам'яті зі старими об'єктами оголошує вільною.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ