Java. Інкапсуляція даних в класі. Управління доступом в Java. Модифікатори доступу private, protected, public




Інкапсуляція даних в класі. Управління доступом в Java. Модифікатори доступу private, protected, public


Зміст


Пошук на інших ресурсах:

1. Що означає інкапсуляція в класі?

Інкапсуляція в класі означає надання відповідного доступу до членів класу з допомогою спеціальних модифікаторів доступу: private, protected, public. Доступ можна задавати як до даних, так і до методів.

  ⇑

2. Які існують категорії доступності членів даних класу в мові Java?

У мові Java виділяються 4 категорії доступності:

  • похідні класи (підкласи) в одному пакеті;
  • класи в одному пакеті, які не є похідними;
  • похідні класи (підкласи) в різних пакетах;
  • класи, які знаходяться в різних пакетах і не є похідними (не є підкласами).

  ⇑

3. Які переваги дає використання механізму інкапсуляції в класі?

Інкапсуляція даних у класі дає наступні переваги:

  • якщо дані класу є прихованими, то це убезпечує від зловживань чи випадкового доступу до них. Доступ до даних забезпечується з допомогою спеціально розроблених методів класу;
  • якщо дані (методи) класу є прихованими, то вони представляють свого роду “чорний ящик”. Взаємодія з даними забезпечується через відповідні методи класу;
  • гнучкість в наданні доступу до різних видів даних класу з допомогою відповідних модифікаторів;
  • у методах доступу до прихованих даних класу можна робити відповідні перевірки на коректність.

  ⇑



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

Для управління доступом до даних та методів класу, у Java використовуються наступні модифікатори доступу:

  • private. У цьому випадку дані (методи) є прихованими. Доступ до таких даних мають тільки методи класу. З екземпляру (об’єкту) класу елементи позначені як private є недоступні;
  • public. Якщо член класу оголошений з модифікатором доступу public, то цей член класу є доступний з будь-якого коду даного пакету та за межами пакету (для усіх методів усіх класів);
  • protected. Цей модифікатор використовується у випадку, якщо потрібно, щоб елемент класу був доступний за межами поточного пакету, але тільки класам, безпосередньо похідним (успадкованим) від даного класу.

Також елементи класу можуть бути оголошені без модифікатора доступу.

  ⇑

5. Яка загальна форма оголошення елементу класу з заданим модифікатором доступу?

Модифікатор доступу до конкретного члена даних чи методу встановлюється перед його оголошенням.

Загальна форма класу, в якому оголошуються один член даних класу та один метод наступна

class ClassName {
    // оголошення члена даних класу
    access_modifier type variable;

    // оголошення методу в класі
    access_modifier return_type MethodName(parameters)
    {
        // тіло методу
        // ...
    }
}

де

  • ClassName – ім’я оголошуваного класу;
  • access_modifier – один з модифікаторів доступу private, public, protected;
  • type – тип члена даних класу з іменем variable;
  • variable – член даних класу, який оголошений з типом type;
  • return_type – тип, який повертає метод класу з іменем MethodName();
  • parameters – параметри, які отримує метод MethodName().

  ⇑

6. Доступ до елементів класу, які оголошені з модифікатором доступу private. Приклад

Елементи класу, які оголошені з модифікатором доступу private, є прихованими. До них мають доступ тільки методи даного класу. З усіх інших частин програмного коду до private-членів класу немає доступу.

Такий спосіб приховування даних в класі є ефективним у випадках, якщо потрібно:

  • приховати деталі організації структур даних та реалізації даного класу;
  • уникнути можливі випадкові зміни даних в екземплярі класу;
  • забезпечити зручність доступу до даних в класі з допомогою спеціально розроблених методів. Тут мається наувазі розробка методів у класі які забезепечують читання/запис даних класу. Ці методи можуть включати відповідні перевірки на коректність при зміні даних в класі;
  • уникнути можливі зловживання даними в класі що може призвести до виникнення важковловимих помилок у програмі.

Приклад. Демонструється приклад доступу до private-змінної класу з допомогою методів доступу.

Оголошується клас, що реалізує день тижня. У класі оголошується прихований (private) член даних класу з іменем value. Для доступу до value у класі використовуються:

  • конструктор DayWeek(), який ініціалізує значення value=1. Конструктор оголошений без модифікатора. Це означає, що в межах пакету він має тип доступу public;
  • метод Get(), який повертає значення value. Оскільки метод оголошений в класі DayWeek, то в тілі методу є доступ до змінної value;
  • метод Set(), який встановлює нове значення value. Метод також оголошений в класі, тому має доступ до value. У методі здійснюється перевірка на коректність значення value в межах 1..7. Якщо задати інше значення, то значення value не зміниться;
  • метод GetName(), який повертає назву дня тижня в залежності від значення value.

Реалізація класу DayWeek наступна

// клас, що реалізує день тижня
class DayWeek {
    private int value; // прихована змінна класу

    // конструктор класу, ініціалізує змінну value
    DayWeek() {
        value = 1; // за замовчуванням - "Monday"
    }

    // методи доступу
    // метод, що повертає дані в класі
    public int Get() {
        return value;
    }

    // метод, що встановлює новий день тижня
    public void Set(int _value) {
        // у методі виконується перевірка на коректність значення _value
        if ((_value>=1)&&(_value<=7))
            value = _value;
    }

    // додатковий метод
    // повертає назву дня тижня, якому відповідає value
    public String GetName() {
        String day = "Monday";
        switch (value) {
            case 2: day = "Tuesday"; break;
            case 3: day = "Wednesday"; break;
            case 4: day = "Thursday"; break;
            case 5: day = "Friday"; break;
            case 6: day = "Saturday"; break;
            case 7: day = "Sunday"; break;
        }
        return day;
    }
}

Використати такий клас в іншому коді (методі) можна, наприклад, наступним чином

public class HiddenData {
    public static void main(String[] args) {
        // доступ до private-члена даних класу
        DayWeek dw = new DayWeek();
        int Day;
        String strDay;

        Day = dw.Get(); // Day = 1
        strDay = dw.GetName(); // strDay = "Monday"

        System.out.println("Day = " + Day);
        System.out.println("strDay = " + strDay);

        // встановити нове значення дня
        dw.Set(3);

        // перевірка
        Day = dw.Get(); // Day = 3
        strDay = dw.GetName(); // strDay = "Wednesday"

        System.out.println("Day = " + Day);
        System.out.println("strDay = " + strDay);
    }
}

Результат виконання вищенаведеної програми

Day = 1
strDay = Monday
Day = 3
strDay = Wednesday

  ⇑

7. Доступ до елементів класу, які оголошені з модифікатором доступу public. Приклади

До елементів класу, які оголошені з модифікатором доступу public є доступ з будь-якого програмного коду пакету, в якому оголошений даний клас.

Якщо потрібно мати доступ до public-елементів класу з інших пакетів, то цей клас також має бути оголошений з модифікатором public. Більш детально про особливості доступу до public-класів в пакетах описується в темі:

  ⇑

7.1. Приклад доступу до public-елементу класу в межах пакету

Задано клас A з членом даних value, який оголошений з модифікатором public. Нижче продемонстровано три можливі способи доступу до public-члена даних value:

  • з поточного класу A;
  • з класу B, який оголошений у даному пакеті;
  • з класу C, який оголошений у даному пакеті та є похідним від класу A.

 

// деякий клас
class A {
    public int value; // змінна, оголошена з модифікатором public

    // доступ з методу поточного класу
    void methodA() {
        value = 5;
    }
}

class B {
    // доступ з методу іншого класу даного пакету
    void methodB() {
        A objA = new A();
        objA.value = 10;
    }
}

// клас C успадковує клас A
class C extends A {
    // доступ з методу похідного класу
    void methodC() {
        value = 30;
    }
}

  ⇑

7.2. Приклад доступу до public-елемента класу з іншого пакету

Щоб доступитись до public-елемента класу з іншого пакету цей клас має бути також оголошений як public.

Нехай в пакеті Package1 задано клас з іменем DemoPackage1. У класі оголошується public-змінна value.

// модифікатор доступу public, пакет Package1
package Package1;

// Пакет Package1
// public-клас DemoPackage1
public class DemoPackage1 {
    public int value; // ця змінна є доступна за межами пакету
    public static void main(String[] args) {
    }
}

Тоді, доступ до з іншого пакету Package2 до змінної value може бути:

  • з допомогою використання екземпляру (об’єкту) класу Package1.DemoPackage1;
  • з допомогою класу, який є похідним від класу Package1.DemoPackage1.
// Пакет Package2
package Package2;

// підключення класу DemoPackage1 з пакету Package1.
import Package1.DemoPackage1;

// деякий клас в пакеті Package2
public class DemoPackage2 {
    public static void main(String[] args) {
        // створення екземпляру класу DemoPackage1,
        // який розміщується в пакеті Package1 - дозволено
        Package1.DemoPackage1 dp1 = new Package1.DemoPackage1();

        // доступ до public-змінної класу з іншого пакету, який оголошений як public
        dp1.value = 30;
    }
}

// клас, який успадковує (розширює) клас DemoPackage1 з пакету Package1
class DemoPackage3 extends DemoPackage1 {
    // метод, який має доступ до Package1.DemoPackage1.value
    void IncValue(int _delta) {
        value = value + _delta; // збільшити value на величину delta
    }
}

Слід зауважити, якщо в пакеті Package2 розміщується директива

import Package1.DemoPackage1;

то до класу DemoPackage1 з пакету Package1 можна звертатись будь-яким з двох способів:

  • з врахуванням імені пакету: Package1.DemoPackage1;
  • без використання імені пакету перед іменем класу: DemoPackage1.

  ⇑

8. Доступ до елементів класу, які оголошені з модифікатором доступу protected. Приклади

Розглядають два випадки доступу до protected-елемента класу:

  • в межах пакету, в якому клас з даним protected-елементом оголошений (в поточному пакеті);
  • з іншого пакету.

Якщо в деякому пакеті елемент класу оголошений з ключовим словом protected, то:

  • в поточному пакеті цей елемент є видимий з будь-якого коду;
  • в іншому пакеті цей елемент є видимий тільки у похідних класах.

Доведемо це на прикладах.

  ⇑

8.1. Приклад доступу до protected-члена даних класу в межах пакету

Задано клас A, в якому є оголошений protected-член даних value. У даному пакеті до елементу класу value можна мати доступ наступними способами:

  • з поточного класу A;
  • з класу B, який оголошений у даному пакеті;
  • з класу C, який оголошений у даному пакеті та є похідним від класу A.

 

// модифікатор доступу protected, пакет Package1
package Package1;

// деякий клас
class A {
    protected int value; // змінна, оголошена з модифікатором protected

    // доступ з методу поточного класу
    void methodA() {
        value = 5;
    }
}

class B {
    // доступ з методу іншого класу даного пакету
    void methodB() {
        A objA = new A();
        objA.value = 10;
    }
}

// клас C успадковує клас A
class C extends A {
    // доступ з методу похідного класу
    void methodC() {
        value = 30;
    }
}

З вищенаведеного коду можна зробити наступний висновок:

  • в межах пакету доступ до protected-елементів класу такий самий як і до public-елементів класу.

  ⇑

8.2. Приклад доступу до protected-члена даних класу з іншого пакету

Для того, щоб protected-елемент класу був доступний за межами даного пакету, цей клас має бути оголошений з модифікатором public.

Щоб доступитись до protected-елементу класу з класів інших пакетів потрібно, щоб ці класи були похідними від даного класу.

Розглянемо це на прикладі.

Нехай задано пакет з іменем Package1. У цьому пакеті реалізовано клас DemoPackage1, в якому оголошується protected-змінна value.

// модифікатор доступу protected,
// пакет Package1
package Package1;

// public-клас DemoPackage1
public class DemoPackage1 {
    // за межами пакету ця змінна є доступна тільки у похідних класах
    protected int value;

    public static void main(String[] args) {
    }
}

У нижченаведеному коді продемонстровано доступ з іншого пакету Package2 до protected-змінної value класу DemoPackage1 (пакет Package1).

// Пакет Package2
package Package2;

// підключення класу DemoPackage1 з пакету Package1.
import Package1.DemoPackage1;

// деякий клас в пакеті Package2
public class DemoPackage2 {
    public static void main(String[] args) {
        // створення екземпляру класу DemoPackage1,
        // який розміщується в пакеті Package1 - дозволено
        Package1.DemoPackage1 dp1 = new Package1.DemoPackage1();

        // доступ до protected-змінної класу з іншого пакету
        // dp1.value = 30; - ЗАБОРОНЕНО, помилка
    }
}

// клас, який успадковує (розширює) клас DemoPackage1 з пакету Package1
// цей клас має доступ до protected-змінної value
class DemoPackage3 extends DemoPackage1 {
    // метод, який має доступ до Package1.DemoPackage1.value
    void IncValue(int _delta) {
        value = value + _delta; // доступ ДОЗВОЛЕНО
    }
}

Якщо зняти коментар з рядка

dp1.value = 30;

то компілятор видасть помилку

The field DemoPackage1.value is not visible

  ⇑

9. Особливості доступу до елементів класу у випадку відсутності модифікатора доступу. Приклади

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

  • методи цього класу;
  • методи похідного підкласу, який оголошений в цьому самому пакеті;
  • методи будь-якого класу, який розміщений в цьому самому пакеті.

Заборонено доступ з інших пакетів до елементів класу, оголошених без модифікатора доступу.

Приклад 1. У прикладі демонструється доступ до змінної value класу A з методів інших класів. Змінна value класу A оголошена без модифікатора доступу.

// пакет Package1
// модифікатор доступу відсутній
package Package1;

class A {
    int value; // змінна, оголошена без модифікатора доступу

    // доступ з методу поточного класу
    void methodA() {
        value = 5; // доступ дозволено
    }
}

class B {
    // доступ з методу іншого класу даного пакету
    void methodB() {
        A objA = new A(); // екземпляр класу A
        objA.value = 10; // доступ дозволено
    }
}

class C extends A {
    // доступ з методу похідного класу
    void methodC() {
        value = 30; // доступ дозволено
    }
}

Приклад 2. Демонстрація заборони доступу з іншого пакету до змінної value класу DemoPackage1, яка оголошена без модифікатора доступу.

Задано два пакети з іменами Package1 та Package2. У пакеті Package1 оголошується public-клас з іменем DemoPackage1. У цьому класі оголошується змінна value без модифікатора доступу.

Текст пакету Package1 наступний:

// Пакет Package1
// клас DemoPackage1
public class DemoPackage1 {
    int value; // ця змінна є доступна тільки в цьому пакеті

    public static void main(String[] args) {
    }
}

В іншому пакеті Package2 оголошується клас DemoPackage2 та директива import, яка підключає пакет Package1.

// Пакет Package2
package Package2;

// підключення класу DemoPackage1 з пакету Package1.
import Package1.DemoPackage1;

// деякий клас в пакеті Package2
public class DemoPackage2 {
    public static void main(String[] args) {
        // створення екземпляру класу DemoPackage1,
        // який розміщується в пакеті Package1 - дозволено
        Package1.DemoPackage1 dp1 = new Package1.DemoPackage1();

        // неможливо доступитись до змінної іншого пакету,
        // яка оголошена без модифікатора
        // dp1.value = 10; - помилка, доступ до змінної dp1.value ЗАБОРОНЕНО!
    }
}

Як видно з прикладу, спроба викликати рядок

dp1.value = 10;

з пакету Package2 викличе помилку компілятора з наступним повідомленням

The field DemoPackage1.value is not visible

Це означає, що поле value класу DemoPackage1 є невидимим.

  ⇑

10. Який тип доступу мають члени класу за замовчуванням?

Перед оголошенням змінної, екземпляру чи методу в класі не обов’язково вказувати модифікатор доступу. Якщо модифікатор доступу відсутній перед оголошенням членів класу, то приймається доступ за замовчуванням. Для членів класу в Java встановлено загальнодоступний (public) доступ за замовчуванням.

  ⇑






11. Чи можна оголошувати конструктор класу з модифікатором доступу private?

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

Наприклад. Нехай задано клас з іменем CData. У класі оголошуються:

  • конструктор без параметрів з модифікатором доступу public;
  • прихований (private) конструктор з одним параметром. Цей конструктор буде використовуватись у методі UseCData(), який оголошений у цьому ж класі;
  • метод CData, який використовує прихований (private) конструктор CData() з одним параметром.

Текст оголошення та використання класу має вигляд:

// клас, що містить прихований конструктор
class CData {
    int d; // член даних d має доступ public за замовчуванням

    // public-конструктор, видимий за межами класу
    CData() {
        d = 0;
    }

    // прихований конструктор, видимий тільки в межах класу
    private CData(int _d) {
        d = _d;
    }

    // метод, що використовує прихований (private) конструктор класу
    public void UseCData(int _d) {
        CData DD = new CData(_d); // використати private-конструктор класу
        System.out.println("DD = " + DD.d);
    }
}

public class TestClass {
    public static void main(String[] args) {
        // створити екземпляр класу CData
        CData D1 = new CData(); // виклик public-конструктора без параметру

        // Заборонено викликати private-конструктор класу CData
        //CData D2 = new CData(2);

        // доступ до члена даних d
        D1.d = 30;

        System.out.println("D1.d = " + D1.d);
        D1.UseCData(55); // вивести на екран DD=55
    }
}

Як видно з оголошення класу, у методі UseCData() викликається прихований (private) конструктор з одним параметром

...
CData DD = new CData(_d); // використати private-конструктор класу
...

У результаті виконання вищенаведеного коду, на екран буде виведено:

D1.d = 30
DD = 55

  ⇑


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

  ⇑