Тема 8. Инкапсуляция - BelyiZ/JavaCourses GitHub Wiki

Содержание:

  1. Пакеты
    1. Импорт
  2. Модификаторы доступа
    1. Доступы к классам
    2. Геттеры и сеттеры
      1. Геттеры
      2. Сеттеры
  3. Вложенные классы
  4. Список литературы/курсов

Как мы говорили ранее, инкапсуляция - это сокрытие данных. В Java существует несколько различных вариантов ограничений, так называемых модификаторов доступа. Но перед тем как к ним перейти нужно рассмотреть другую функциональность - пакеты.

Пакеты

Пакеты в Java - это сущность объединяющая схожие по функционалу классы. Каждый созданный класс располагается в каком-то пакете. Но и пакеты могут располагаться в других пакетах. Таким образом можно создавать древовидную структуру. По факту, пакеты нужны для группировки классов.

Структура пакетов в точности повторяет структуру папок, в которых располагаются классы. Одна директория - одни пакет. Единственным отличием является форматирование пути к пакету. В отличие от папок, где используется "/", в пакетах применяется точка. Например, у нас имеется такая структура файлов и папок:

ru 
 |- java
     |- course
         |- football
             |- Player.java
             |- Ball.java
             |- Team.java
         |- basketball
             |- Player.java
             |- Coach.java
             |- Team.java
         |- tennis

Как мы видим, есть 6 разных java-файлов распределенных по три в двух директориях; всего папок 7. Допустим, что каждый из java-файлов содержит по одному классу (обычно так и бывает, но дальше я покажу исключения). В данном случае, каждый класс находится в каком-то из пакетов: класс Ball лежит в пакете ru.java.course.football, а Coach вru.java.course.basketball.

Каждый класс в Java имеет свой пакет. Указываться он должен в самой первой строке файла. Сначала указывается ключевое слово package и затем полный путь к пакету. Например, package ru.java.course.football.

Полный путь к пакету является и частью названия класса. Например, чтобы использовать класс String на самом деле нужно использовать его полное название (java.lang.String), но в предыдущих темах мы этого не делали. И тут за нас эту проблему решала используемая среда разработки, она автоматически импортировала нужные нам классы и пакеты.

Импорт

Импортом в Java называется указание полных путей для интересующих нас классов или пакетов. Список импорта нужно писать сразу после указания пакета:

package ru.java.course.football;

import java.lang.String;
import java.lang.Integer;
import java.util.*;

Как мы видим, для объявления импорта используется ключевое слово import, а затем полное имя класса. Если мы хотим импортировать все классы из пакета, то вместо названия класса указываем *. Теперь в коде внутри файла можно использовать короткие имена импортированных классов String и Integer. Для каждого класса автоматически импортируются другие из того же пакета и к ним можно обращаться напрямую.

Импортировать несколько классов с одним и тем же именем нельзя. Даже если они находятся в разных пакетах. Например, мы можем импортировать класс ru.java.course.football.Player и обращаться к нему по короткому имени. Но если захотим использовать класс ru.java.course.basketball.Player, то везде в коде нужно будет писать его полное название.

Модификаторы доступа

Итак, переходим к самому главному - сокрытие данных. Для определения уровня допуска в Java применяются модификаторы доступа. Всего из три: public, protected, private. Еще один, четвертый вариант, называется package local (или package private) и применяется по умолчанию при отсутствии модификатора.

Модификаторы доступа можно указывать для классов, полей и методов. Например:

package ru.java.course.football;

public class Player {

    private String name;
    int age;

    protected void score() {
        // ...
    }
}

Модификатор public указывает на то, что данный ресурс будет доступен из любого места программы. То есть класс Player является публичным классом и использовать его можно из любого другого класса в данной программе. Для публичных данных инкапсуляция отсутствует, эти данные могут читаться и изменяться другими ресурсами, никаких ограничений нет.

Чтобы немного уменьшить круг мест с доступом к ресурсу, в данном случае - методу, применяется ключевое словоprotected. Теперь метод score() могут вызвать только из классов, находящихся в том же пакете, что и Player, или из его классов-наследников. Из всех остальных мест это сделать не получится - произойдет ошибка компиляции.

По умолчанию, при отсутствии модификатора доступа, ресурс доступен только внутри текущего пакета. В нашем примере поле age будет доступно, только из классов пакета ru.java.course.football.

И, наконец, самый строгий из модификаторов - private. Он разрешает обращаться к ресурсу только в рамках одного класса (или java-файла, если модификатор используется для class).

Ограничивать доступ стоит не только для того, чтобы скрыть некоторые данные от других объектов, но и, например, для запрета вызова внутренних, служебных методов объекта.

Доступы к классам

В Java каждый публичный класс должен находиться в отдельном файле, название которого совпадает с названием класса. В одном таком файле может находиться только один публичный класс. Но это не единственная особенность применения модификаторов доступа к классам.

Начнем с того, что классы, как и методы, и поля могут быть приватными, публичными и защищенными. Для этого нужно установить нужный модификатор доступа перед ключевым словом class. Например,

protected class MainClass {

}

Но на верхнем уровне в файле могут только публичные классы и доступные внутри пакета (без модификатора). А вот приватные и защищенные бывают только вложенными (см. ниже в этой теме)

Геттеры и сеттеры

Наиболее популярным подходом для инкапсуляции является создание геттеров и сеттеров. Это специальные методы, которые предоставляют доступ к приватным полям класса. Геттер возвращает значение некоторого поля, а сеттер позволяет его менять. Рассмотрим пример кода:

package ru.java.course.football;

import java.lang.String;

public class Player {

    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
}

Геттеры

Методы getName() и getAge() называются геттерами, потому что всегда начинаются с префикса get (для boolean полей принято начинать название геттера с префикса is). Наименование таких методов всегда строится одинаково. Сначала идет вышеупомянутый префикс, затем название поля с заглавной буквы.

Главная задача геттеров - контроль всех запросов к полю объекта. Зачастую тело метода выглядит как на примере выше. Но иногда может содержать несложную логику, например имя игрока может быть доступно всем, но в сокращенном виде Иванов И.К.. Так же при наличии геттера такую логику можно реализовать в дальнейшем, не переписывая код, который использует это поле.

Сеттеры

Сеттеры созданы из тех же побуждений, что и геттеры, только контролируют они не чтение значения поля, а его изменение. В примере выше сеттером является метод setName(String). Здесь приведен пример простейшей реализации, данная реализация позволяет менять значение приватного поля name из всех мест программы. А вот для поля age такой возможности уже нет. Получать возраст игрока может любой другой объект, а вот изменять его извне уже не получится.

Термин "сеттер" для таких методов произошел от постоянного префикса в названии - set. Полное название строится по шаблону: set + название поля с заглавной буквы. Такой метод всегда имеет один параметр с типом равным типу редактируемого поля.

В сеттере, например, можно проверять корректность данных, или модифицировать новое значение перед сохранением его в объекте. Но такой функционал редко встречается и может ухудшить читаемость кода. Для сложно логики рекомендуется создавать отдельный метод.

Места определения классов

Классы в Java можно определять в нескольких местах. Ранее мы рассматривали только публичные классы расположенные в разных файлах. Но есть еще несколько вариантов: приватные, вложенные и анонимные классы.

Файлы классов

В одном файле не может больше одного public-класса. Ограничений на количество классов с другими модификаторами нет. Для классов они работают так же, как и для полей. Можно, например, определять private-классы и использовать их только внутри текущего файла. Название файла должно быть точно таким же, как и у публичного класса внутри него.

Вложенные классы

Классы могут содержать в себе не только поля и методы, но и другие классы. Такие классы называются вложенными. И так же как поля и методы, вложенные классы могут быть статичными.

Рассмотрим пример:

public class Main {

    protected class Included {

    }

    public static class IncludedStatic {

    }

}

Можно увидеть три класса: публичный Main, защищенный Included и публичный статичный IncludedStatic. Последние два являются вложенными. Класс IncludedStatic доступен отовсюду из нашей программы и создать его экземпляр можно так: new Main.IncludedStatic();. Далее созданным объектом можно пользоваться, как и всеми другими. Немного по-другому обстоят дела с классом Included. Так как он не статичный, доступ к нему возможен только через объект класса Main. Таким образом для создания его экземпляра нужно выполнить, например, такой код: new Main().new Included();;

Вложенные классы могут быть как публичными, так и приватными, подходящими для использования только в рамках основного класса.

#Список литературы/курсов

  1. https://javarush.ru/groups/posts/1969-principih-inkapsuljacii
  2. https://ravesli.com/urok-115-inkapsulyatsiya-gettery-i-settery/
  3. https://younglinux.info/oopython/encapsulation
  4. https://ru.hexlet.io/courses/js-introduction-to-oop/lessons/encapsulation/theory_unit
  5. https://skillbox.ru/media/code/oop_chast_3_modifikatory_dostupa_inkapsulyatsiya/

Тема 7. Класс Object и его методы | Оглавление | Тема 9. Наследование и static