Инкапсуляция в Java это принцип ООП, который объединяет данные (поля) и методы работы с ними в одном классе-«капсуле» и скрывает внутреннее устройство объекта от пользователя. На практике ее реализуют так: поля закрывают модификатором private, а доступ к ним дают через public-методы (геттеры и сеттеры), в которых можно проверить корректность данных. Привет! Сегодняшнюю лекцию посвятим инкапсуляции и начнем ее сразу с примеров :)

Кратко

  • Инкапсуляция это объединение данных (полей) и методов работы с ними в одной «капсуле», классе.
  • Сокрытие реализации: пользователю дают простой интерфейс (методы), а внутреннюю работу прячут.
  • Сокрытие данных: поля закрывают модификатором private и дают доступ через геттеры и сеттеры.
  • В Java «инкапсуляция» обычно означает и объединение, и сокрытие вместе.
  • Сеттеры позволяют проверять данные и не допускать некорректного состояния объекта (например, отрицательный возраст).
  • Плюсы: контроль состояния объекта, удобный интерфейс, свобода менять внутренний код без вреда для пользователей.
Перед тобой привычный автомат с газировкой. У меня к тебе один вопрос: а как он работает? Попробуй ответить подробно: откуда вываливается стакан, как поддерживается температура внутри, где хранится лед, как автомат понимает, какой сироп добавить и т.д. Вероятнее всего, ответов на эти вопросы у тебя нет. Хорошо, возможно не все пользуются такими автоматами, в нынешнее время они не настолько популярны. Попробуем привести другой пример. Что-нибудь, чем ты точно пользуешься много раз каждый день. О, есть идея! Поисковиком Google пользуются, не зная его внутреннего устройства: пример сокрытия Расскажи, как работает поисковик Google. Как именно он ищет информацию по тем словам, которые ты ввел? Почему наверху находятся эти результаты, а не другие? Хотя ты пользуешься гуглом каждый день, скорее всего, ты этого не знаешь. Но это не важно. Ведь тебе и не нужно этого знать. Ты можешь вводить запросы в поисковик не задумываясь, как именно он работает. Ты можешь купить газировку в автомате, не зная как он устроен. Ты можешь водить машину, не вникая в работу двигателя внутреннего сгорания, и вообще не зная физику даже на школьном уровне. Все это возможно благодаря одному из главных принципов объектно-ориентированного программирования, инкапсуляции. Читая разные статьи на эту тему, наверняка ты сталкивался с тем, что в программировании есть два распространенных понятия, инкапсуляция и сокрытие. И под словом «инкапсуляция» авторы понимают то одно, то другое (так уж сложилось). Мы разберем оба термина, чтобы у тебя было полное понимание. Кратко все три механизма удобно свести в таблицу:
ПонятиеЧто означаетКак реализуется в Java
Инкапсуляция (объединение)данные и методы работы с ними лежат в одной капсулекласс с полями и методами
Сокрытие реализациискрыть внутреннюю работу, дать простой интерфейсpublic-методы наружу, логика внутри
Сокрытие данныхзащитить поля от прямого доступаprivate-поля + геттеры и сеттеры

Инкапсуляция: объединение данных и методов

Изначальное значение слова «инкапсуляция» в программировании это объединение данных и методов работы с этими данными в одной упаковке («капсуле»). В Java в роли упаковки-капсулы выступает класс. Класс содержит в себе и данные (поля класса), и методы для работы с этими данными. Класс как капсула: объединяет данные (поля) и методы работы с ними Тебе это кажется очевидным, но в других концепциях программирования все устроено иначе. Например, в функциональном программировании данные строго отделены от операций над ними. В ООП же (объектно-ориентированном программировании) программы состоят из классов-капсул, которые являются одновременно и данными, и функциями для работы с ними.

Сокрытие реализации

Теперь поговорим о сокрытии. Как же так получается, что мы пользуемся всякими сложными механизмами без понимания, как они устроены и на чем основана их работа? Все просто: их создатели предоставили простой и удобный интерфейс. На автомате с газировкой интерфейс это кнопки на панели. Нажав одну кнопку, ты выбираешь объем стакана. Нажав вторую, выбираешь сироп. Третья отвечает за добавление льда. И это все, что тебе нужно сделать. Неважно, как именно автомат устроен внутри. Главное, он устроен так, что для получения газировки пользователю нужно нажать три кнопки. То же и с автомобилем. Неважно, что там происходит у него внутри. Главное, при нажатии правой педали автомобиль едет вперед, а при нажатии левой тормозит. Именно в этом заключается суть сокрытия. Все «внутренности» программы скрываются от пользователя. Для него эта информация является лишней, ненужной. Пользователю необходим конечный результат, а не внутренний процесс. Давай для примера посмотрим на класс Auto:

public class Auto {

   public void gas() {

       /*внутри автомобиля происходят какие-то сложные вещи
       в результате которых он едет вперед*/
   }

   public void brake() {

       /*внутри автомобиля происходят какие-то сложные вещи
       в результате которых он тормозит*/
   }

   public static void main(String[] args) {

       Auto auto = new Auto();

       //Как все выглядит для пользователя

       //нажал одну педаль - поехал
       auto.gas();

       //нажал другую педаль - затормозил
       auto.brake();
   }
}
Вот как выглядит сокрытие реализации в Java-программе. Все как в реальной жизни: пользователю предоставлен интерфейс (методы). Если ему нужно, чтобы автомобиль в программе выполнил действие, достаточно вызвать нужный метод. А уж что там происходит внутри этих методов это информация лишняя, главное, чтобы все работало как надо.

Сокрытие данных

Здесь мы говорили про сокрытие реализации. Кроме него в Java есть еще сокрытие данных. О нем мы писали в лекции про геттеры и сеттеры, но не будет лишним напомнить. Например, у нас есть класс Cat:

public class Cat {

   public String name;
   public int age;
   public int weight;

   public Cat(String name, int age, int weight) {
       this.name = name;
       this.age = age;
       this.weight = weight;
   }

   public Cat() {
   }

   public void sayMeow() {
       System.out.println("Мяу!");
   }


}
Возможно, ты запомнил из прошлой лекции, в чем проблема этого класса? Если нет, давай вспомним. Проблема в том, что его данные (поля) открыты для всех, и другой программист легко может создать в программе безымянного кота с весом 0 и возрастом -1000 лет:

public static void main(String[] args) {

   Cat cat = new Cat();
   cat.name = "";
   cat.age = -1000;
   cat.weight = 0;

}
В такой ситуации можно пристально следить за тем, не создает ли кто-то из твоих коллег объектов с неправильным состоянием, но гораздо лучше было бы исключить саму возможность создавать такие «неправильные объекты». Без сокрытия данных можно создать объект с некорректным состоянием С сокрытием данных нам помогают:
  1. модификаторы доступа (private, protected, package default);
  2. геттеры и сеттеры.
Туда можем, например, заложить проверку, не пытается ли кто-то присвоить коту отрицательное число в качестве возраста. Как мы говорили ранее, авторы разных статей об инкапсуляции имеют в виду либо инкапсуляцию (объединение данных и методов), либо сокрытие, либо и то, и другое. В Java присутствуют оба механизма (в других ООП-языках это не обязательно так), так что последний вариант будет наиболее правильным.

Преимущества инкапсуляции

Использование инкапсуляции дает нам несколько важных преимуществ:
  1. Контроль за корректным состоянием объекта. Примеры этому были выше: благодаря сеттеру и модификатору private, мы обезопасили нашу программу от котов с весом 0.

  2. Удобство для пользователя за счет интерфейса. Мы оставляем «снаружи» для доступа пользователя только методы. Ему достаточно вызвать их, чтобы получить результат, и совсем не нужно вникать в детали их работы.

  3. Изменения в коде не отражаются на пользователях. Все изменения мы проводим внутри методов. На пользователя это не повлияет: он как писал auto.gas() для газа машины, так и будет писать. А то, что мы поменяли что-то в работе метода gas() для него останется незаметным: он, как и раньше, просто будет получать нужный результат.
Больше о разграничении доступа к полям и методам класса можно прочитать в документации Oracle (Controlling Access to Members of a Class).

Читайте также

Вопросы и ответы

Что такое инкапсуляция в Java?

Это принцип ООП, который объединяет данные (поля) и методы работы с ними в одном классе-«капсуле», а также скрывает внутреннее устройство объекта от пользователя.

Чем инкапсуляция отличается от сокрытия?

Инкапсуляция в узком смысле это объединение данных и методов в классе. Сокрытие это ограничение доступа к внутренностям (реализации и данным). В Java под инкапсуляцией обычно понимают оба механизма вместе.

Что такое сокрытие данных?

Это защита полей объекта от прямого доступа: поля делают private, а читать и менять их дают только через геттеры и сеттеры, где можно проверить корректность значений.

Как реализовать инкапсуляцию в Java?

Закрыть поля модификатором private, а доступ к ним организовать через public-методы (геттеры и сеттеры). Внутреннюю логику держать внутри методов, наружу отдавать только интерфейс.

Зачем нужны геттеры и сеттеры?

Они дают контролируемый доступ к private-полям: в сеттере можно проверить входные данные и не допустить некорректного состояния объекта, например отрицательного возраста.

Какие преимущества дает инкапсуляция?

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