51
Заметьте, что класс нельзя объявить как private (что сделает класс недоступным для окружающих, использовать он сможет только «сам себя») или protected
[2]
. Поэтому у вас есть лишь такой выбор при задании доступа к классу: в пределах пакета или открытый (public). Если вы хотите перекрыть доступ к классу для всех, объявите все его конструкторы со спецификатором private, соответственно, запретив кому бы то ни было создание объектов этого класса. Только вы сами, в статическом методе своего класса, сможете создавать такие объекты. Пример:
//. access/Lunch.java // Спецификаторы доступа для классов. // Использование конструкторов, объявленных private, // делает класс недоступным при создании объектов.
class Soupl {
private SouplО {}
// (1) Разрешаем создание объектов в статическом методе: public static Soupl makeSoupO { return new SouplO;
}
}
class Soup2 {
private Soup2() {}
// (2) Создаем один статический объект и // по требованию возвращаем ссылку на него, private static Soup2 psl = new Soup2(): public static Soup2 accessO { return psl:
}
public void f() {}
}
// В файле может быть определен только один public-класс: public class Lunch {
void testPrivateO {
// Запрещено, т.к конструктор объявлен приватным: //! Soupl soup = new SouplO:
}
void testStaticO {
Soupl soup = Soupl.makeSoupO;
}
void testSingletonO {
Soup2.access О f();
}
)
До этого момента большинство методов возвращало или void, или один из примитивных типов, поэтому определение:
public static Soupl makeSoupO { return new SouplO:
} на первый взгляд смотрится немного странно. Слово Soupl перед именем метода (makeSbup) показывает, что возвращается методом. В предшествующих примерах обычно использовалось обозначение void, которое подразумевает, что метод не имеет возвращаемого значения. Однако метод также может возвращать ссылку на объект; в данном случае возвращается ссылка на объект класса Soupl.
Классы Soupl H'Soup2 наглядно показывают, как предотвратить прямое создание объектов класса, объявив все его конструкторы со спецификатором private. Помните, что без явного определения хотя бы одного конструктора компилятор сгенерирует конструктор по умолчанию (конструктор без аргументов). Определяя конструктор по умолчанию в программе, вы запрещаете его автоматическое создание. Если конструктор объявлен со спецификатором private, никто не сможет создавать объекты данного класса. Но как же тогда использовать этот класс? Рассмотренный пример демонстрирует два способа. В классе Soupl определяется статический метод, который создает новый объект Soupl и возвращает ссылку на него. Это бывает полезно в ситуациях, где вам необходимо провести некоторые операции над объектом перед возвратом ссылки на него, или при подсчете общего количества созданных объектов Soupl (например, для ограничения их максимального количества).
В классе Soup2 использован другой подход — в программе всегда создается не более одного объекта этого класса. Объект Soup2 создается как статическая приватная переменная, пэтому он всегда существует только в одном экземпляре и его невозможно получить без вызова открытого метода access().
Резюме
В любых отношениях важно установить ограничения, которые соблюдаются всеми сторонами. При создании библиотеки вы устанавливаете отношения с пользователем библиотеки (программистом-клиентом), который создает программы или библиотеки более высокого уровня с использованием ваших библиотек.
Если программисты-клиенты предоставлены сами себе и не ограничены никакими правилами, они могут делать все, что им заблагорассудится, с любыми членами класса — даже теми, доступ к которым вам хотелось бы ограничить. Все детали реализации класса открыты для окружающего мира.
В этой главе рассматривается процесс построения библиотек из классов; во-первых, механизм группировки классов внутри библиотеки и, во-вторых, механизм управления доступом к членам класса.
По оценкам проекты на языке С начинают «рассыпаться» примерно тогда, когда код достигает объема от 50 до 100 Кбайт, так как С имеет единое «пространство имен»; в системе возникают конфликты имен, создающие массу неудобств. В Java ключевое слово package, схема именования пакетов и ключевое слово import обеспечивают полный контроль над именами, так что конфликта имен можно легко избежать.
Существует две причины для ограничения доступа к членам класса. Первая — предотвращение использования клиентами внутренней реализации класса, не входящей во внешний интерфейс. Объявление полей и методов со спецификатором private только помогает пользователям класса, так как они сразу видят, какие члены класса для них важны, а какие можно игнорировать. Все это упрощает понимание и использование класса.
Вторая, более важная причина для ограничения доступа — возможность изменения внутренней реализации класса, не затрагивающего программистов- клиентов. Например, сначала вы реализуете класс одним способом, а затем выясняется, что реструктуризация кода позволит повысить скорость работы. Отделение интерфейса от реализации позволит сделать это без нарушения работоспособности существующего пользовательского кода, в котором этот класс используется.
Открытый интерфейс класса — это то, что фактическивидитего пользователь, поэтому очень важно «довести до ума» именно эту, самую важную, часть класса в процессе анализа и разработки. И даже при этом у вас остается относительная свобода действий. Даже если идеальный интерфейс не удалось построить с первого раза, вы можетедобавитьв него новые методы — без удаления уже существующих методов, которые могут использоваться программистами- клиентами.
[1]
Использовать Java-интерпретатор не обязательно. Существует несколько компиляторов, создающих единый исполняемый файл.
[2]
На самом деле доступ private или protected могут иметьвнутренние классы,но это особый случай (см. главу 8).
Повторное использование классов
В
озможность повторного использования кода принадлежит к числу важнейших преимуществ Java. Впрочем, по-настоящему масштабные изменения отнюдь не сводятся к обычному копированию и правке кода.