Тема 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