Еще одно интервью, еще один вопрос, на который нужен лучший ответ. На этот раз строительные блоки Java ООП.

Инкапсуляция

Упаковка членов/методов этих членов в один компонент (в основном и объект) и ограничение доступа к компонентам.

Таким образом, внутреннее представление объекта скрыто от просмотра за пределами определения объекта, в то время как вместо этого предоставляется функциональность Getter/Setter.

Зачем использовать инкапсуляцию?

  • Геттеры и сеттеры могут разрешать разные уровни доступа — например, get может быть общедоступным, но набор может быть защищен.
  • Позволяет легко добавить дополнительные функции (например, проверку) позже.
  • Скрытие внутреннего представления свойства при предоставлении свойства с использованием альтернативного представления или передача метода получения/установки в виде лямбда-выражений, а не значений.
  • Изоляция общедоступного интерфейса от изменений — позволяет общедоступному интерфейсу оставаться постоянным, пока изменяется реализация, не затрагивая существующих потребителей.
  • Разрешение подклассу изменять семантику поведения свойства и его раскрытия путем переопределения методов получения/установки.

Наследование

Наследование — это возможность класса приобретать члены и методы другого класса, создавая между ними иерархию «потомок-родитель». Чтобы правильно использовать новый тип объекта, нам нужно следовать известным принципам SOLID:

  1. Единственная ответственность — у класса может быть не более одной причины для изменения. Это помогает нам организовать структуру нашего проекта и разделить различные функции на разные классы.
  2. Принцип открытости-закрытости. Класс должен быть построен таким образом, чтобы нам было легко extends его extends, когда мы хотим добавить еще один параметр/условное поведение. Только если это невозможно, мы должны рассмотреть возможность рефакторинга.
  3. Подстановка Лисков — расширение второго принципа. Если класс B является подтипом класса A, то мы должны иметь возможность заменить A на B, не нарушая поведения нашей программы. Другими словами, переопределенный метод подкласса должен принимать те же значения входных параметров, что и метод суперкласса.
  4. Разделение интерфейсов: большие интерфейсы должны быть разделены на более мелкие. Таким образом, мы можем гарантировать, что реализующие классы должны заботиться только о тех методах, которые имеют отношение к ним.
  5. Инверсия зависимостей. Модули высокого уровня, обеспечивающие сложную логику, должны легко использоваться повторно и не должны подвергаться изменениям в модулях низкого уровня, предоставляющих служебные функции. На самом деле это комбинация 2 + 3 принципов. Мы можем реализовать 2-й принцип, вводя интерфейсы, для которых вы можете предоставить разные реализации. Реализации должны следовать принципу замены Лискова. Таким образом, мы можем заменить их другими подклассами того же интерфейса без нарушения работы приложения.

Полиморфизм

Полиморфизм — это возможность создавать переменную/метод/объект с более чем одним набором символов.

  1. перегрузка метода — объект может иметь более одной реализации одного и того же метода — с различным типом и количеством параметров. Решение о том, какой метод использовать, принимается во время компиляции, поэтому это делает перегрузку метода отличным примером статической привязки. Частным случаем перегрузки является знак «+», который объединяет строки/добавление чисел — это единственная перегрузка оператора.
  2. переопределение метода: создание нескольких классов, расширяющих родительский класс, дает нам возможность перезаписать унаследованный метод с аналогичной функциональностью. Пожалуйста, обратите внимание-
  • Вы не можете переопределить частные, статические и окончательные методы.
  • Signature-методы должны иметь одинаковую сигнатуру, но возвращаемый тип метода подкласса может быть дочерним по отношению к исходному возвращаемому типу.
  • Метод переопределения не может генерировать исключение выше по иерархии (суперкласс исключения, которое генерирует родительский класс).
  • Переопределяющий метод не может уменьшить область доступа переопределенного метода — доступ остается прежним или расширяется.

В то время как перегрузка метода определяется во время компиляции, переопределение метода определяется типом объекта во время выполнения (динамическая привязка).

Интерфейс

Интерфейс — это набор контрактов, в большинстве случаев нереализованных методов, которые должны соблюдаться реализующими классами.

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

До Java 8 интерфейсы не могли иметь статические методы. Статические методы — это методы в Java, которые можно вызывать без создания объекта класса. Подробнее о том, зачем использовать статические методы, читайте здесь.

Абстракция

Абстракция – это возможность определять набор свойств и функций, не беспокоясь о реализации. Абстрактный класс не может быть создан (но объект может быть типизирован как один с конструктором подкласса) и используется путем создания наследующего подкласса (который может быть создан). Подкласс может расширять только один класс.

Абстрактный класс может:

  1. Определить методы (наследующий подкласс будет использовать их или переопределять)
  2. Определите абстрактные методы (должен быть реализован наследующий подкласс).
  3. Обеспечьте общий интерфейс, который позволяет подклассу быть взаимозаменяемым со всеми другими подклассами.

Мы можем создать абстракцию данных (новый тип данных) и предоставлять доступ только к операциям и параметрам взаимодействия с типом данных. Это дает нам свободу изменять код и повышать тип данных с течением времени, пока клиент этого не заметит. Мы также можем создать абстракцию управления, которая представляет собой определяющий набор операторов, которые мы повторяем как единое целое.

Если мы хотим получить доступ к определенному члену конкретного подкласса, которого нет в абстрактном классе, мы должны сначала выполнить приведение к этому подклассу: ((SubClass)instanceName).uniqueMethod()

Интерфейс против абстракции

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

Обратите внимание, что вызов методов через имя их интерфейса немного медленнее, чем вызов их через имя их абстрактного класса.

Когда использовать интерфейсы

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

Когда использовать абстрактные классы

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

милый старик кормит стаю енотов зимней ночью ❤