Java использует три явных ключевых слова, характеризующих уровень доступа: public, private и protected. Их предназначение и употребление очень просты. Эти спецификаторы доступа определяют, кто имеет право использовать следующие за ними определения. Слово public означает, что последующие определения доступны всем. Наоборот, слово private значит, что следующие за ним предложения доступны только создателю типа, внутри его методов. Термин private — «крепостная стена» между вами и программистом-клиентом. Если кто-то попытается использовать private-члены, он будет остановлен ошибкой компиляции. Спецификатор protected действует схоже с private, за одним исключением — производные классы имеют доступ к членам, помеченным protected, но не имеют доступса к private-членам (наследование мы вскоре рассмотрим).
В Java также есть доступ «по умолчанию», используемый при отсутствии какого-либо из перечисленных спецификаторов. Он также иногда называется доступом в пределах пакета (package access), поскольку классы могут использовать дружественные члены других классов из своего пакета , но за его пределами те же дружественные члены приобретают статус private.
Повторное использование реализации
Созданный и протестированный класс должен (в идеале) представлять собой полезный блок кода. Однако оказывается, что добиться этой цели гораздо труднее, чем многие полагают; для разработки повторно используемых объектов требуется опыт и понимание сути дела. Но как только у вас получится хорошая конструкция, она будет просто напрашиваться на внедрение в другие программы. Многократное использование кода — одно из самых впечатляющих преимуществ объектно-ориентированных языков.
Проще всего использовать класс повторно, непосредственно создавая его объект, но вы можете также поместить объект этого класса внутрь нового класса. Мы называем это внедрением объекта. Новый класс может содержать любое количество объектов других типов, в любом сочетании, которое необходимо для достижения необходимой функциональности. Так как мы составляем новый класс из уже существующих классов, этот способ называется композицией (если композиция выполняется динамически, она обычно именуется агрегированием). Композицию часто называют связью типа «имеет» (has-a), как, например, в предложении «у автомобиля есть двигатель».
Автомобиль
Двигатель
(На UML-диаграммах композиция обозначается закрашенным ромбом. Я несколько упрощу этот формат: оставлю только простую линию, без ромба, чтобы обозначить связь 1.)
Композиция — очень гибкий инструмент. Объекты-члены вашего нового класса обычно объявляются закрытыми (private), что делает их недоступными для программистов-клиентов, использующих класс. Это позволяет вносить изменения в эти объекты-члены без модификации уже существующего клиентского кода. Вы можете также изменять эти члены во время исполнения программы, чтобы динамически управлять поведением вашей программы. Наследование, описанное ниже, не имеет такой гибкости, так как компилятор накладывает определенные ограничения на классы, созданные с применением наследования.
Наследование играет важную роль в объектно-ориентированном программировании, поэтому на нем часто акцентируется повышенное внимание, и новичок может подумать, что наследование должно применяться повсюду. А это чревато созданием неуклюжих и излишне сложных решений. Вместо этого при создании новых классов прежде всего следует оценить возможность композиции, так как она проще и гибче. Если вы возьмете на вооружение рекомендуемый подход, ваши программные конструкции станут гораздо яснее. А по мере накопления практического опыта понять, где следует применять наследование, не составит труда.
Наследование
Сама по себе идея объекта крайне удобна. Объект позволяет совмещать данные и функциональность на концептуальном уровне , то есть вы можете представить нужное понятие проблемной области прежде, чем начнете его конкретизировать применительно к диалекту машины. Эти концепции и образуют фундаментальные единицы языка программирования, описываемые с помощью ключевого слова class.
Но согласитесь, было бы обидно создавать какой-то класс, а потом проделывать всю работу заново для похожего класса. Гораздо рациональнее взять готовый класс, «клонировать» его, а затем внести добавления и обновления в полученный клон. Это именно то, что вы получаете в результате наследования, с одним исключением — если изначальный класс (называемый также базовым- классом, суперклассом или родительским классом) изменяется, то все изменения отражаются и на его «клоне» (называемом производным классом, унаследованным классом, подклассом или дочерним классом).
Читать дальше