Автор: Ткаченко О.М.
1. Вказівники на об'єкти. Збір "сміття"
Як уже зазначено вище, при створенні об'єкта виділяється ділянка пам'яті в динамічній області. Нехай маємо деякий клас:
class Myclass {
int a;
Myclass(int avalue) {a=avalue; }
public int getA() {return a;}
}
Нагадаємо, що саме оголошення об'єктів, наприклад:
Myclass m1, m2;
лише ствоює змінні-вказівник на майбутній об'єкт. Сам же об'єкт створюється лише командою new. Створимо 2 об'єкти даного класу:
m1 = new Myclass(2);
m2 = new Myclass(5);
В результаті під кожен об’єкт буде виділено окрему ділянку динамічної пам'яті, а змінні m1 і m2 – лише вказівники на ці ділянки:
Значення змінних m1 і m2 – вказівники, тобто адреси ділянок пам'яті, де розмішуються об’єкти, на який ці вказівники показують. Тому присвоєння m1=m2 означає присвоєння змінній m1 значення вказівника m2, тобто адреси другого об'єкта. В результаті цього матимемо:
Отже, після зазначеного присвоєння два вказівники показують на одну і ту ж саму ділянку пам'яті. Тобто значення атрибуту m1.a і m2.a – однакові. Крім того, ділянка пам'яті, у якій розміщено перший об'єкт, виявилася загубленою, тобто не є жодний вказівник. Якщо таких загублених об’єктів буде досить
багато, може виникнути проблема заповнення пам'яті комп'ютера непотрібними об’єктами, які ще називають "сміттям". Неконтрольоване заповнення пам'яті потенційноможе призвести до збою в роботі програми і системи вцілому. Розробники Java-платформи передбачили таку ситуацію:
контроль над створенням і використанням динамічних об’єктів бере на себе віртуальна машина Java.
Збір "сміття" – автоматичне звільнення пам'яті комп’ютера від об’єктів, на які встрачено вказівники.
Лістинг
class Myclass {
int a;
Myclass(int avalue) {a=avalue; }
public int getA() {return a;}
}
class pr3_5 {
public static void main(String [ ] args){
Myclass m1, m2;
m1 = new Myclass(2);
m2 = new Myclass(5);
System.out.println("Значення атрибуту а перед присвоєнням:");
System.out.println("Для першого об’єкту: "+m1.getA());
System.out.println("Для другого об’єкту: "+m2.getA());
m1=m2;
System.out.println("Значення атрибуту а після присвоєння:");
System.out.println("Для першого об’єкту: "+m1.getA());
System.out.println("Для другого об’єкту: "+m2.getA());
}
}
Результати роботи програми:
Значення атрибуту а перед присвоєнням:
Для першого об’єкту: 2
Для другого об’єкту: 5
Значення атрибуту а після присвоєння:
Для першого об’єкту: 5
Для другого об’єкту: 5
2. Статичні атрибути і методи
Статичним називається метод, для виклику якого не потрібно створювати/вказувати об’єкт.
Із статичними методами ви зустрілись, коли використовували методи класу Math. Замість форми ім’я_об’єкта.метод(...); використовують форму ім’я_класу.метод(. . .);
Приклади:
y = Math.exp(2*x); // Math – ім’я класу
s = String.valueOf(5.12); // String – ім’я класу
При створенні статичного методу в описі класу вказується специфікатор static.
Приклад . Статичні методи класу
Нехай нам треба обчислити значення виразу використовуючи підпрограму (метод) користувача.
Розв’язання. Подамо вираз у лінійній формі, відповідно до синтаксису Java:
y = Math.sin(x)/(1+Math.sqrt(Math.abs(x)));
Лістинг
class Function {
static double myFunc(double x) {
return Math.sin(x)/(1+Math.sqrt(Math.abs(x)));
}
}
class pr3_6 {
public static void main(String [ ] args){
double y;
// ім’я_класу.ім’я_методу(параметри)
y = Function.myFunc(2.5);
System.out.println("y="+y);
}
}
Бачимо, що обчислювальна команда y = Function.myFunc(2.5); викликає метод myFunc() з префіксом Function (іменем класу, а не об'єкту).
Статичним атрибутом є атрибут класу, для доступу до якого не треба створювати/вказувати об’єкт. Значення статичного атрибуту єдине для всіх екземплярів класу.
Як і для статичних методів, для виклику статичної змінної вказується не ім’я об’єкту, а ім’я класу.
Приклад . Статичні атрибути
Нехай клас Balance описує деяку суму грошей у гривнях, розміщену на рахунку. Нехай іншим атрибутом є еквівалент накопиченої суми в євро. Одним з методів класу є перерахунок суми у євро. Зрозуміло, для коректного перетворення треба мати актуальний курс співвідношення EUR/UAH. Цей курс зручно зберігати у статичній змінній, оскільки він єдиний для всіх персональних рахунків.
Лістинг
class Balance {
double sum_uah; // сума в гривнях
static double curex=10.52; // біжуче значення курсу
Balance (double h) { // конструктор
sum_uah=h;
}
double getUah() { // отримання балансу
return sum_uah;
}
double getEur() { // баланс в євро
return sum_uah/curex;
}
}
class pr3_7 {
public static void main(String [ ]args){
// створення двох обєктів (двох рахунків)
Balance b1 = new Balance(1000);
Balance b2 = new Balance(2000);
// виведення балансу в грн і євро
System.out.println("Актуальний курс EUR: "+Balance.curex);
System.out.println(b1.getUah()+" грн.="+b1.getEur());
System.out.println(b2.getUah()+" грн.="+b2.getEur());
// зміна значення статичної змінної curex,
// спрацьовує одразу для обох об'єктів - b1 i b2
Balance.curex=10.55;
// баланс в грн і євро після зміни курсу
System.out.println("Змінений курс EUR: "+Balance.curex);
System.out.println(b1.getUah()+" грн.="+b1.getEur());
System.out.println(b2.getUah()+" грн.="+b2.getEur());
}
}