Інкапсуляція даних в класі. Управління доступом в 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

 


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