Эккель Брюс
Шрифт:
A@lbab50a А@сЗс749 A@150bd4d 12 3 4 *///•-
Резюме
Такой сложный механизм инициализации, как конструктор, показывает, насколько важное внимание в языке уделяется инициализации. Когда Бьерн Страуструп разрабатывал С++, в первую очередь он обратил внимание на то, что низкая продуктивность С связана с плохо продуманной инициализацией, которой была обусловлена значительная доля ошибок. Аналогичные проблемы возникают и при некорректной финализации. Так как конструкторы позволяют гарантировать соответствующие инициализацию и завершающие действия по очистке (компилятор не позволит создать объект без вызова конструктора), тем самым обеспечивается полная управляемость и защищенность программы.
В языке С++ уничтожение объектов играет очень важную роль, потому что объекты, созданные оператором new, должны быть соответствующим образом разрушены. В Java память автоматически освобождается сборщиком мусора, и аналоги деструкторов обычно не нужны. В таких случаях сборщик мусора Java значительно упрощает процесс программирования и к тому же добавляет так необходимую безопасность при освобождении ресурсов. Некоторые сборщики мусора могут проводить завершающие действия даже с такими ресурсами, как графические и файловые дескрипторы. Однако сборщики мусора добавляют издержки во время выполнения программы, которые пока трудно реально оценить из-за сложившейся исторически медлительности интерпретаторов Java. И хотя в последнее время язык Java намного улучшил свою производительность, проблема его «задумчивости» все-таки наложила свой отпечаток на возможность решения языком некоторого класса задач.
Так как для всех объектов гарантированно используются конструкторы, на последние возлагаются дополнительные обязанности, не описанные в этой главе. В частности, гарантия конструирования действует и при создании новых классов с использованием композиции или наследования, и для их поддержки требуются некоторые дополнения к синтаксису языка. Композиция и наследование, а также их влияние на конструкторы, рассматриваются в следующих главах.
Управление доступом
Важнейшим фактором объектно-ориентированной разработки является отделение переменных составляющих от постоянных.
Это особенно важно для библиотек. Пользователь {программист-клиент) библиотеки зависит от неизменности некоторого аспекта вашего кода. С другой стороны, создатель библиотеки должен обладать достаточной свободой для проведения изменений и улучшений, но при этом изменения не должны нарушить работоспособность клиентского кода.
Желанная цель может быть достигнута определенными договоренностями: Например, программист библиотеки соглашается не удалять уже существующие методы класса, потому что это может нарушить структуру кода программи-ста-клиента. В то же время обратная проблема гораздо острее. Например, как создатель библиотеки узнает, какие из полей данных используются программи-стом-клиентом? Это же относится и к методам, являющимся только частью реализации класса, то есть не предназначенным для прямого использования программистом-клиентом. А если создателю библиотеки понадобится удалить старую реализацию и заменить ее новой? Изменение любого из полей класса может нарушить работу кода программиста-клиента. Выходит, у создателя библиотеки «связаны руки», и он вообще ничего не вправе менять.
Для решения проблемы в Java определены спецификаторы доступа (access specifiers), при помощи которых создатель библиотеки указывает, что доступно программисту-клиенту, а что нет. Уровни доступа (от полного до минимального) задаются следующими ключевыми словами: public, protected, доступ в пределах пакета (не имеет ключевого слова) и private. Из предыдущего абзаца может возникнуть впечатление, что создателю библиотеки лучше всего хранить все как можно «секретнее», а открывать только те методы, которые, по вашему мнению, должен использовать программист-клиент. И это абсолютно верно, хотя и выглядит непривычно для людей, чьи программы на других языках (в особенности это касается С) «привыкли» к остутствию ограничений. К концу этой главы вы наглядно убедитесь в полезности механизма контроля доступа в Java.
Однако концепция библиотеки компонентов и контроля над доступом к этим компонентам — это еще не все. Остается понять, как компоненты связываются в объединенную цельную библиотеку. В Java эта задача решается ключевым словом package (пакет), и спецификаторы доступа зависят от того, находятся ли классы в одном или в разных пакетах. Поэтому для начала мы разберемся, как компоненты библиотек размещаются в пакетах. После этого вы сможете в полной мере понять смысл спецификаторов доступа.
Пакет как библиотечный модуль
Пакет содержит группу классов, объединенных в одном пространстве имен.
Например, в стандартную поставку Java входит служебная библиотека, оформленная в виде пространства имен java,util. Один из классов java.util называется ArrayList. Чтобы использовать класс в программе, можно использовать его полное имя java. util. Array List. Впрочем, полные имена слишком громоздки, поэтому в программе удобнее использовать ключевое слово import. Если вы собираетесь использовать всего один класс, его можно указать прямо в директиве import: