Главная страница
Навигация по странице:

  • // Эскиз системы с предоставлением услуг public abstract class Foo { // Ставит ключ типа String в соответствие объекту Class

  • // 3агружает названия классов и ключи из файла Properties, // транслирует названия в объекты Class, используя // Class. forName, и сохраняет их соответствие ключам

  • // Синглтон с полем типа final public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { // ...

  • // Синглтон со статическим методом генерации java.io.*;public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis() { // ...

  • // Метод, сохраняющий свойство синглтона private Object readResolve() throws ObjectStreamException { /*

  • Effective Java tmprogramming Language GuideJ o s h u a b lo c h


    Скачать 1.05 Mb.
    НазваниеEffective Java tmprogramming Language GuideJ o s h u a b lo c h
    Дата03.04.2018
    Размер1.05 Mb.
    Формат файлаpdf
    Имя файлаBlokh_Dzh_-_Java_Effektivnoe_programmirovanie.pdf
    ТипДокументы
    #40178
    страница2 из 25
    1   2   3   4   5   6   7   8   9   ...   25
    Java 2 Platform, Standard Edition,
    v1.2
    1.2
    Java 2 Platform, Standard Edition,
    v1.3
    1.3
    Java 2 Platform, Standard Edition,
    v1.4
    1.4
    Примеры по возможности являются полными, однако предпочтение отдается незавершенности, а удобству чтения. В примерах широко используются классы пакеТ08 java.util и java. 10. Чтобы скомпилировать пример, вам потребуется добавить один или оба оператора import:
    Import java.ut11.*;
    Import В примерах опущены детали. Полные версии всех примеров содержатся на сайте этой книги
    (http://java.sun.com/docs/books/effective). При желании любой из них можно скомпилировать и запустить. Технические термины в этой книге большей частью используются в том виде, как они определены в
    "The Jаvа п п, п п"
    [JLS]. Однако некоторые термины заслуживают отдельного упоминания. Язык Java поддерживает четыре группы типов интерфейсы

    (interface), классы

    (class), массивы
    (аrrау) и простые типы
    (primitive). Первые три группы называются ссылочными типами
    (reference type). Экземпляры классов и массивов '- это объекты, значения простых типов таковыми не являются. К членам класса (members) относятся его поля
    (fields), методы
    (methods),
    классы-члены
    (member classes) и
    интерфейсы-члены
    (mernber interfaces). Сигнатура метода (signature) состоит из его названия и типов, которые имеют его формальные параметры. Т ил значения, возвращаемого методом, в сигнатуру не входит. Некоторые термины в этой книге используются вином значении, чем в
    "The Jаvа . п п. Так, наследование"
    (inheritance) применяется как синоним образования подклассов"
    (subclassing). Вместо использования для интерфейсов термина "наследование" в книге констатируется, что некий класс реализует
    (implement) интерфейс или что один интерфейс является расширением другого (extend) для описания' уровня доступа, который применяется, когда ничего больше не указано, в книге используется описательный термин доступ только в пределах пакета"
    (package-private) вместо формально правильного термина доступ по умолчанию"
    (default access) [JLS,
    6.6.1]. В этой книге встречается несколько технических терминов, которых нет в
    "The Jаvа Language
    Specification". Термин внешний АР"
    (exported API), или просто АР относится к классам, интерфейсам, конструкторам, членами сериализованным формам, с помощью которых программист получает доступ к классу, интерфейсу или пакету. (Термин
    API, являющийся сокращением от
    application programming interface - программный интерфейс приложения, используется вместо термина интерфейс"

    (interface). Это позволяет избежать путаницы с одноименной конструкцией языка Java.) Программист, который пишет программу, применяющую некий API, называется пользователем
    (user) указанного API. Класс, в реализации которого используется некий API, называется клиентом
    (client) этого API.
    Классы, интерфейсы, конструкторы, члены и сериализованные формы называются элементами АР
    (API element). Внешний
    API образуется из элементов
    API, которые доступны за пределами пакета, где этот
    API был определен. Указанные элементы может использовать любой клиент, автор АР берет на себя их поддержку. Неслучайно документацию именно к этим элементам генерирует утилита
    J
    avadoc при запуске в режиме по умолчанию. В общих чертах, внешний АР пакета состоит из открытых
    (pubIic) и защищенных
    (protected) членов, а также из конструкторов всех открытых классов и интерфейсов в пакете.
    Глава Создание и уничтожение
    объектов
    В
    этой главе речь идет о создании и уничтожении объектов как и когда создавать объекты, как убедиться в том, что' объекты уничтожались своевременно, а также как управлять операциями по очистке, которые должны предшествовать уничтожению объекта. Рассмотрите возможность замены конструкторов статическими методами генерации.
    Обычно для того, чтобы клиент мог получать экземпляр класса, ему предоставляется открытый
    (pubIic) конструктор. Есть и другой, менее известный прием, который должен быть в арсенале любого программиста. Класс может иметь открытый
    статический метод генерации
    (static factory method), который является статическим методом, возвращающим экземпляр класса. Пример такого метода возьмем из класса
    Boolean являющего оболочкой для простого типа boolean). Приведенный ниже статический метод генерации, который был добавлен в версию
    1.4, преобразует значение boolean в ссылку на объект
    Boolean: public static Boolean valueOf(boolean Ь) {
    Return (Ь Boolean. TRUE: Boolean. Статические методы генерации могут быть предоставлены клиентам класса не только вместо конструкторов, но ив дополнение к ним. Замена открытого конструктора статическим методом генерации имеет свои достоинства и недостатки.
    Первое преимущество статического метода генерации состоит в том, что, в отличие от конструкторов, он имеет название. В то время как параметры конструктора сами по себе не дают описания возвращаемого объекта, статический метод генерации с хорошо подобранным названием может упростить работу с классом и, как следствие, сделать соответствующий программный код клиента более понятным. Например, конструктор Biglnteger(int, int, Random), который возвращает
    Biglnteger, являющийся, вероятно, простым числом (prime), лучше было бы представить как статический метод генерации с названием Biglnteger.ргоЬаЫеРrime. (В конечном счете этот статический метод был добавлен в версию
    1.4.) Класс может иметь только один конструктор с заданной сигнатурой. Известно, что программисты обходят данное ограничение, создавая конструкторы, чьи списки параметров отличаются лишь порядком следования типов. Это плохая идея. Человек, использующий подобный
    API, не сможет запомнить, для чего нужен один конструктора для чего другой, ив конце концов, по ошибке вызовет не тот конструктор. Те, кто читает' программный код, в котором применяются такие конструкторы, не смогут понять, что же он делает, если не будут сверяться с сопроводительной документацией на этот класс. Поскольку статические методы генерации имеют имена, к ним не относится ограничение конструкторов, запрещающее иметь в классе более одного метода с заданной сигнатурой. Соответственно в ситуациях, когда очевидно, что в классе должно быть несколько конструкторов с одной и той же сигнатурой, следует рассмотреть возможность замены одного или нескольких конструкторов статическими методами генерации. Тщательно выбранные названия будут подчеркивать их различия. Второе преимущество статических методов генерации заключается в том, что, в отличие от конструкторов, они не обязаны при каждом вызове создавать новый объект. Это позволяет использовать для неизменяемого класса (статья
    13) предварительно созданный экземпляр либо кэшировать экземпляры класса по мере их создания, а затем раздавать их повторно, избегая создания ненужных дублирующих объектов. Подобный прием иллюстрирует метод Boolean. valueOf(boolean): он не создает объектов. Эта методика способна значительно повысить производительность программы, если часто возникает необходимость в создании одинаковых объектов, особенно в тех случаях, когда создание объектов требует больших затрат. Способность статических методов генерации возвращать при повторных вызовах тот же самый объект можно использовать и для того, чтобы в любой момент времени четко контролировать, какие экземпляры объекта еще существуют. На это есть две причины. Во-первых, это позволяет гарантировать, что некий класс является синглтоном (статья
    2).
    Во-вторых, это дает возможность убедиться в том, что у неизменяемого класса не появилось двух одинаковых экземпляров a.equals(b) тогда и только тогда, когда а==Ь. Если класс предоставляет' такую гарантию, его клиенты могут использовать оператор == вместо метода equals(Object), что приводит к существенному повышению производительности программы. Подобную оптимизацию реализует шаблон перечисления типов, описанный в статье
    21, частично ее реализует также метод String.intern.
    Третье преимущество статического метода генерации заключается в том, что, в отличие от конструктора, он может вернуть объект, который соответствует не только заявленному типу возвращаемого значения, но и любому его подтипу. Это предоставляет вам значительную гибкость в выборе класса для возвращаемого объекта. Например, интерфейс АР может возвращать объект, не декларируя его класс как pubIic. Сокрытие реализации классов может привести к созданию очень компактного
    API. Этот прием идеально подходит для конструкций, построенных на интерфейсах, где интерфейсы для статических методов генерации задают собственный тип возвращаемого значения. Например, архитектура Collections Framework имеет двадцать полезных реализаций интерфейсов коллекции неизменяемые коллекции, синхронизированные коллекции и т. д. С помощью статических методов генерации большинство этих реализаций сводится в единственный класс j
    ауа.util.Collections, для которого невозможно создать экземпляр. Все классы, соответствующие возвращаемым объектами, не, являются открытыми. АР Collections Framework имеет гораздо меньшие размеры, чем это было бы, если бы в нем были представлены двадцать отдельных открытых классов для всех возможных реализаций. Сокращен не только объем этого
    API, но и его "концептуальная нагрузка. Пользователь знает, что возвращаемый объект имеет в точности тот
    API, который указан в соответствующем интерфейсе, и ему нет нужды читать дополнительные документы поэтому классу. Более того, применение статического метода генерации дает клиенту право обращаться к возвращаемому объекту, используя его собственный интерфейса не интерфейс класса реализации, что обычно является хорошим приемом (статья
    34). Скрытым может быть не только класс объекта, возвращаемого открытым статическим методом генерации. Сам класс может меняться от вызова к вызову в зависимости оттого, какие значения параметров передаются статическому методу генерации. Это может быть любой класс, который является подтипом по отношению к возвращаемому типу, заявленному в интерфейсе. Класс возвращаемого объекта может также меняться от версии к версии, что повышает удобство сопровождения программы. В момент написания класса, содержащего статический метод генерации, класс, соответствующий возвращаемому объекту, может даже не существовать. Подобные гибкие статические методы генерации лежат в основе систем с предоставлением услуг
    (service provider framework), например ]
    ауа Cryptography Extension ОСЕ. Система с предоставлением услуг - это такая система, в которой поставщик может создавать различные реализации интерфейса
    API, доступные пользователям этой системы. Чтобы сделать эти реализации доступными для применения, предусмотрен механизм регистрации (register). Клиенты могут пользоваться указанным
    API, не беспокоясь о том, с какой из его реализаций они имеют дело. В Упомянутой системе ]CE системный администратор регистрирует класс реализации, редактируя хорошо известный файл Properties: делает в нем запись, которая с13лзыветT некий ключ- строку с именем соответствующего класса. Клиенты же используют статический метод генерации, который получает этот ключ в качестве параметра.
    По схеме, восстановленной из файла
    Properties, статический метод генерации находит объекта затем создает экземпляр соответствующего класса с помощью метода
    Class. new lnstance.' Этот прием демонстрируется в следующем фрагменте import java.util.*;
    // Эскиз системы с предоставлением услуг
    public abstract class Foo {
    // Ставит ключ типа
    String в соответствие объекту
    Class
    private static Map implementations = null;
    // При первом вызове инициализирует карту соответствия private static synchronized void initMapIfNecessary() {
    if (im plementations == null) {
    im plementations = new HashMap();
    // 3агружает названия классов и ключи из файла
    Properties,
    // транслирует названия в объекты Class, используя
    // Class. forName, и сохраняет их соответствие ключам
    // ...
    }
    }
    public static Foo getInstance(String key) {
    initMapIfNecessary();
    Class c = (Class) implementations.g et(key);
    if (c == null)
    return new DefaultFoo();
    try {
    return (Foo) c.new Instance();
    } catch (Exception e) {
    return new DefaultFoo();
    } }
    public static void main(String [] arg s) {
    System.out.println(getInstance("NonexistentFoo"));
    } }
    class DefaultFoo extends Foo {
    }
    Основной недостаток статических методов генерации заключается в том, что классы, не имеющие открытых или защищенных конструкторов, не могут иметь подклассов. Это же касается классов, которые возвращаются открытыми статическими методами генерации, носами открытыми не являются. Например, в архитектуре Collections
    F
    ramework невозможно создать подкласс ни для одного из классов реализации. Сомнительно, что в такой маскировке может быть благо, поскольку поощряет программистов использовать не наследование, а композицию (статья 14). Второй недостаток статических методов генерации состоит в том, что их трудно отличить от других статических методов. В документации АР они не выделяются так, как это делается для конструкторов. Более того, статические методы генерации представляют собой отклонение от нормы. Поэтому иногда из документации к классу сложно понять, как создать экземпляр класса, в котором вместо конструкторов
    клиенту предоставлены статические методы генерации. Указанный недостаток может быть смягчен, если придерживаться стандартных соглашений, касающихся именования. Эти соглашения продолжают совершенствоваться, но два названия статических методов генерации стали уже общепринятыми

    valueOf' - возвращает экземпляр, который имеет тоже значение, что и его параметры. Статические методы генерации с таким названием фактически являются операторами преобразования типов.

    getlnstance - возвращает экземпляр, который описан параметрами, однако говорить о том, что он будет иметь тоже значение, нельзя. В случае с синглтоном этот метод возвращает единственный экземпляр данного класса. Это название является общепринятым в системах с предоставлением услуг. Подведем итоги. И статические методы генерации, и открытые конструкторы имеют свою область применения. Имеет смысл изучить их достоинства и недостатки. Прежде чем создавать конструкторы, рассмотрите возможность использования статических методов генерации, поскольку последние часто оказываются лучше. Если вы проанализировали обе возможности и не нашли достаточных доводов в чью-либо пользу, вероятно, лучше всего создать конструктор, хотя бы потому, что этот подход является нормой.
    С вой ст во си н гл тона обеспечивайте закрытым конструктором bСuнглтон (singleton) - это класс, для которого экземпляр создается только один раз [Саmmа95, стр. Синглтоны обычно представляют некоторые компоненты системы, которые действительно являются уникальными, например видеодисплей или файловая система. Для реализации синглтонов используются два подхода. Оба они основаны на создании закрытого
    (private) конструктора и открытого (public) статического члена, который обеспечивает клиентам доступ к единственному экземпляру этого класса. В первом варианте открытый статический член является полем типа final:
    // Синглтон с полем типа final
    public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {
    // ...
    }
    // ... // Остальное опущено public static void main(String[] args) {
    System.out.println(Elvis.INSTANCE);
    }
    }
    Закрытый конструктор вызывается только один раз для инициализации поля Elvis.INST Отсутствие открытых или защищенных конструкторов гарантирует вселенную с одним Elvis": после инициализации класса Elvis будет существовать ровно один экземпляр Elvis - не больше и не меньше. И клиент не может ничего с этим поделать. Во втором варианте вместо открытого статического поля типа final создается открытый статический метод генерации
    // Синглтон со статическим методом генерации java.io.*;
    public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {
    // ...
    }
    // ... // Остальное опущено Все вызовы статического метода Elvis. getInstance возвращают ссылку на один и тот же объект, и никакие другие экземпляры Elvis никогда не будут созданы. Основное преимущество первого подхода заключается в том, что из декларации членов, составляющих класс, понятно, что этот класс является синглтоном: открытое статическое поле имеет типа потому оно всегда будет содержать ссылку на один и тот же объект. Первый вариант, по сравнению со вторым, может также иметь некоторое преимущество в производительности. Однако хорошая реализация JVM должна свести это преимущество к минимуму благодаря встраиванию (in!
    ining) вызовов для статического метода генерации во втором варианте. Основное преимущество второго подхода заключается в том, что он позволяет вам отказаться отрешения сделать класс синглтоном, не меняя при этом его АР. Статический метод генерации для синглтона возвращает единственный экземпляр этого класса, однако это можно легко изменить и возвращать, скажем, свой уникальный экземпляр для каждого, потока, обращающегося к этому методу. Таким образом, если вы абсолютно уверены, что данный класс навсегда останется синглтоном, имеет смысл использовать первый вариант. Если же вы хотите отложить решение поэтому вопросу, примените второй вариант. Если требуется сделать класс синглтона сериализуемым (см. главу 10), недостаточно добавить к его декларации implements Serializable. Чтобы дать синглтону нужные гарантии, необходимо также создать метод readResolve (статья 57). В противном случае каждая десериализация сериализованного экземпляра будет приводить к созданию нового экземпляра, что в нашем примере станет причиной
    обнаружения ложных Elvis. Во избежание этого добавьте в класс Elvis следующий метод readResolve:
    // Метод, сохраняющий свойство синглтона
    private Object readResolve() throws ObjectStreamException {
    /*
    1   2   3   4   5   6   7   8   9   ...   25


    написать администратору сайта