Об’єкти. Створення та збереження об’єктів класів. Оператор new. Області зберігання даних в пам’яті. Використання масивів посилань на об’єкти

Об’єкти. Створення та збереження об’єктів класів. Оператор new. Області зберігання даних в пам’яті. Використання масивів посилань на об’єкти


Зміст


1. Яким чином створюється об’єкт класу з допомогою оператора new? Загальна форма

В Java для роботи з об’єктами використовується єдиний синтаксис. Об’єкт класу оголошується з допомогою посилання (reference).
Загальна форма оголошення об’єкту класу без виділення пам’яті для нього має наступний вигляд:

ClassName objName;

де

  • ClassName – ім’я класу для якого створюється об’єкт з іменем objName;
  • objName – ім’я посилання на об’єкт класу ClassName.

Вищенаведене оголошення говорить про те, що ідентифікатор objName є посиланням на об’єкт класу ClassName. Для посилання потрібно виділити пам’ять (ініціалізувати посилання) з допомогою оператора new як показано нижче:

objName = new ClassName();

Якщо не виділити пам’ять для посилання і звернутись до нього як до об’єкту класу, то виникне помилка.

Існує й інша загальна форма оголошення об’єкту класу. У цьому випадку пам’ять виділяється при його оголошенні:

ClassName objName = new ClassName();

Виділення пам’яті для посилання на об’єкт класу ще називається “приєднання” об’єкту до посилання.

2. Приклади створення об’єктів різних класів

Приклад 1. Створення об’єкту класу CLine, що реалізує лінію на координатній площині. Програмний код оголошення класу наступний:

// клас, що реалізує лінію на координатній площині
public class CLine
{
    // внутрішні змінні класу
    private double x1, y1, x2, y2;

    // конструктори класу
    // конструктор без параметрів
    CLine()
    {
        x1 = y1 = 0;
        x2 = y2 = 1;
    }

    // конструктор з 4 параметрами
    CLine(double x1, double y1, double x2, double y2)
    {
        this.x1 = x1; this.y1 = y1;
        this.x2 = x2; this.y2 = y2;
    }

    // методи доступу
    // читання даних
    public double GetX1() { return x1; }
    public double GetY1() { return y1; }
    public double GetX2() { return x2; }
    public double GetY2() { return y2; }

    // запис даних
    void SetXY(double nx1, double ny1, double nx2, double ny2)
    {
        x1 = nx1; y1 = ny1;
        x2 = nx2; y2 = ny2;
    }

    // метод, що обчислює довжину лінії
    double Length()
    {
        double len;
        len = Math.sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
        return len;
    }
}

Нижченаведений програмний код демонструє створення та використання об’єктів класу CLine.

public class CTestLine
{
    public static void main(String[] args)
    {
        double x, y; // допоміжні змінні

        // створення об'єкту класу CLine з допомогою конструктора без параметрів
        CLine line1 = new CLine();

        // створення об'єкту класу CLine з допомогою конструктора з 4 параметрами
        CLine line2 = new CLine(2.0, 3.0, 4.0, 5.0);

        // використання об'єктів класу line1
        x = line1.GetX1(); // x = 0.0
        y = line1.GetY2(); // y = 1.0

        // використання об'єкту класу line2
        x = line2.GetX1(); // x = 2.0
        x = line2.GetX2(); // x = 4.0
        y = line2.GetY2(); // y = 5.0

        // перевизначення об'єкту line1, попередній об'єкт буде знищено при "зборі" сміття
        line1 = new CLine(-4.0, -2.0, 33.4, -20.5);

        x = line1.GetX1(); // x = -4.0
        y = line1.GetY2(); // y = -20.5
    }
}

Як видно з коду, у функції main() оголошуються два посилання з іменами line1, line2 на об’єкти класу CLine. Для цих посилань виділяється пам’ять з допомогою оператора new. Потім, для об’єкту line2 пам’ять заново перевизначається. Стара пам’ять буде звільнена при черговому “зборі” сміття.

Приклад 2. Створення об’єкту класу CName, що реалізує ім’я. Функція main() демонструє створення об’єкту класу CName різними способами з допомогою різних конструкторів.

// клас, що реалізує ім'я
public class CName
{
    private String name;
    private String surname;
    private String patronymic;

    // конструктори класу
    // конструктор без параметрів
    CName()
    {
        name = surname = patronymic = "";
    }

    // конструктор з трьома параметрами
    CName(String _name, String _surname, String _patronymic)
    {
        name = _name;
        surname = _surname;
        patronymic = _patronymic;
    }

    // методи доступу
    String GetName() { return name; }
    String GetSurname() { return surname; }
    String GetPatronymic() { return patronymic; }

    // функція, що демонструє застосування класу CName
    public static void main(String[] args)
    {
        CName nm1 = new CName(); // викликається конструктор без параметрів

        CName nm2; // просто оголошення посилання на об'єкт класу CName, пам'ять ще не виділена

        nm2 = new CName("Happy", "New", "Year!"); // створення об'єкту - виділення пам'яті

        // перевірка
        String str;
        str = nm1.GetName(); // str = ""
        str = nm1.GetSurname(); // str = ""

        str = nm2.GetName(); // str = "Happy"
        str = nm2.GetSurname(); // str = "New"
        str = nm2.GetPatronymic(); // str = "Year!"
    }
}

3. Які існують області зберігання даних в програмах на Java?

У Java для зберігання даних (об’єктів) існує 5 різних сховищ:

  • регістри. У цьому випадку дані зберігаються всередині процесора. У регістрах дані обробляються швидше за все. Однак, кількість регістрів є строго обмежена. Компілятор використовує регістри в міру необхідності. У Java немає безпосередніх команд, щоб зберігати усі дані тільки в регістрах. Навіть у потужних мовах C/C++ команди-вказівки для розміщення даних у регістрах носять тільки рекомендаційний характер;
  • стек. Для програми стек розміщується в загальній оперативній пам’яті (RAM). Стек організовується з використанням покажчиків стеку. Стек працює за принципом LIFO (Last-In-First-Out) – останній прийшов, перший вийшов. Така організація є зручною, коли потрібно виділяти пам’ять для локальних функцій (методів) різних рівнів вкладень. У стеку розміщуються тільки покажчики на об’єкти. Самі об’єкти розміщуються в “купі” (heap або “куча”). Покажчик стеку рухається вниз якщо потрібно виділити пам’ять, і вверх, якщо пам’ять звільняється. Таким чином, за швидкодією, стек поступається тільки регістрам. Однак, стек не володіє такою гнучкістю як “купа”, тому що компілятору потрібно знати життєвий цикл даних, розміщених в стеку;
  • “купа” або “куча” (heap). Це є сховище загального призначення яке розміщується в оперативній пам’яті (RAM). Тут зберігаються усі об’єкти (екземпляри об’єктів) Java. “Купа” є більш гнучкою порівняно зі стеком. Тому що компілятор не витрачає додаткових зусиль на визначення тривалості існування об’єктів, що знаходяться в “купі”. У програмі створення об’єкту відбувається з використанням оператора new. У результаті виділяється пам’ять з “купи”. Однак, виділення пам’яті пам’яті з “купи” займає більше часу ніж в стеку. Слід зауважити, що потужні мови C/C++ підтримують явне створення об’єктів як в стеку, так і в “купі”;
  • постійне сховище. У програмах часто використовуються дані, які є незмінними. До таких даних відносяться константи (наприклад, рядкові константи). Ці дані доцільно вбудовувати прямо в код програми. Інколи константи розміщуються в постійній статичній пам’яті (ROM);
  • зовнішнє сховище. Зовнішнім сховищем можуть бути довготривалі (persistent) носії інформації, наприклад, жорсткий диск даного комп’ютера або носії інформації віддалених комп’ютерів мережі. Цей вид зберігання даних дозволяє зберігати об’єкти на носіях інформації, а потім відновлювати їх для зберігання в оперативній пам’яті.

4. В якій області пам’яті зберігаються об’єкти та посилання на об’єкти?

Об’єкти зберігаються в “купі” (heap). Посилання на об’єкти зберігаються в стеку.



5. Як у Java створюються та зберігаються масиви об’єктів?

На відміну від C/C++ масив у Java обов’язково ініціалізується. Доступ за межами масиву є неможливий.
Щоб створити масив об’єктів потрібно використати запис на зразок:

ClassName arrayObj[];

або

ClassName[] arrayObj;

де

  • ClassName – ім’я деякого класу, який служить типом для масиву об’єктів arrayObj;
  • arrayObj – ім’я масиву об’єктів.

У вищенаведеному описі оголошується масив посилань arrayObj на об’єкти класу ClassName.
Щоб виділити пам’ять для масиву arrayObj з 10 елементів типу ClassName, потрібно написати:

arrayObj = new ClassName[10];

Це можна зробити іншим способом, одразу при оголошенні масиву:

ClassName arrayObj = new ClassName[10];

Пам’ять виділяється тільки для масиву посилань. Для об’єктів пам’ять ще не виділена. Щоб виділити пам’ять для кожного об’єкту потрібно використати приблизно такий код:

for (int i=0; i<arrayObj.length; i++)
{
    arrayObj[i] = new ClassName();
}

У вищенаведеному прикладі виділяється пам’ять для кожного об’єкту в масиві об’єктів arrayObj. Для визначення довжини масиву використовується властивість length, що є загальнодоступною для усіх видів масивів.

6. Приклад оголошення та ініціалізації масиву об’єктів

Нехай задано клас CLine, реалізація якого описується в п. 2. Приклад демонструє використання масиву з n об’єктів типу CLine. Значення n задається програмно (n = 10).

public class CTestLine
{
    public static void main(String[] args)
    {
        int n = 10;
        CLine[] arrayLines = new CLine[n]; // виділення пам'яті для масиву посилань

        // виділення пам'яті для кожного елементу масиву
        for (int i=0; i<arrayLines.length; i++)
        {
            // виділення пам'яті та ініціалізація кожного окремого елементу масиву
            arrayLines[i] = new CLine(i*2, i*1.5, i+2.2, i-1);
        }

        // використання масиву, обчислення сумарної довжини усіх відрізків
        double sumLength = 0;
        for (int i=0; i<arrayLines.length; i++)
            sumLength+=arrayLines[i].Length();

        System.out.println("Sum = " + sumLength); // Sum = 45.45345204172149
    }
}


Зв’язані теми