Тема 8. Инкапсуляция - BelyiZ/JavaCourses GitHub Wiki
Содержание:
Как мы говорили ранее, инкапсуляция - это сокрытие данных. В 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();;
Вложенные классы могут быть как публичными, так и приватными, подходящими для использования только в рамках основного класса.
#Список литературы/курсов
- https://javarush.ru/groups/posts/1969-principih-inkapsuljacii
- https://ravesli.com/urok-115-inkapsulyatsiya-gettery-i-settery/
- https://younglinux.info/oopython/encapsulation
- https://ru.hexlet.io/courses/js-introduction-to-oop/lessons/encapsulation/theory_unit
- https://skillbox.ru/media/code/oop_chast_3_modifikatory_dostupa_inkapsulyatsiya/
Тема 7. Класс Object и его методы | Оглавление | Тема 9. Наследование и static