- •Содержание
- •1. Введение
- •2. Создание и уничтожение объектов
- •3. Методы, общие для всех объектов
- •4. Классы и интерфейсы
- •5. Замена конструкций на языке c
- •6. Методы
- •7. Общие вопросы программирования
- •8. Исключения
- •9. Потоки
- •10. Сериализация
- •11. Литература
- •Глава 1
- •Глава 2 Создание и уничтожение объектов
- •Рассмотрите возможность замены конструкторов статическими методами генерации.
- •Свойство синглтона обеспечивайте закрытым конструктором
- •Отсутствие экземпляров обеспечивает закрытый конструктор
- •Не создавайте дублирующих объектов
- •Уничтожайте устаревшие ссыпки (на объекты)
- •Остерегайтесь методов flnalize
- •Глава 3 Методы, общие для всех объектов
- •Переопределяя метод equals, соблюдайте общие соглашения
- •Переопределяя метод equals, всегда переопределяйте hashCode
- •Всегда переопределяйте метод toStrlng
- •Подумайте над реализацией интерфейса ComparabIe
- •Глава 4 Классы и интерфейсы
- •Сводите к минимуму доступность классов и членов
- •Предпочитайте постоянство
- •Предпочитайте компоновку наследованию
- •Предпочитайте интерфейсы абстрактным классам.
- •Используйте интерфейсы только для определения типов
- •Предпочитайте статические классы-члены нестатическим
- •Глава 5
- •3Амена констрvкций на языке с
- •3Аменяйте структуру классом
- •3Амеияйте объедииеиие иерархией классов
- •/* Помещает данные в одно из полей объединения ... */
- •3Аменяйте конструкцию enum классом
- •Указатель на функцию заменяйте кпассом и интерфейсом
- •Глава 6 Методы
- •Проверяйте достоверность параметров
- •При необходимости создавайте резервные копии
- •Тщательно проектируйте сигнатуру метода
- •Перегружая методы, соблюдайте осторожность
- •Возвращайте массив нулевой длины, а не null
- •Для всех открытых элементов арi пишите dос - комментарии
- •Глава 7 Общие вопросы программирования
- •Сводите к минимуму область видимости локальных переменных
- •Изучите библиотеки и пользуйтесь ими
- •Не используйте строку там, где более уместен иной тип
- •При конкатенации строк опасайтесь потери производительности
- •Для ссыпки на объект используйте его интерфейс
- •Соблюдайте осторожность при использовании машинно-зависимых методов
- •Соблюдайте осторожность при оптимизации
- •Глава 8 Исключения
- •Используйте исключения лишь в исключительных ситуациях
- •Применяйте обрабатываемые исключения для восстановления, для программных ошибок используйте исключения времени выполнения
- •Избегайте ненужных обрабатываемых исключений
- •Предпочитайте стандартные исключения
- •Инициируйте исключения, соответствующие абстракции
- •Для каждого метода документируйте все инициируемые исключения
- •В описание исключения добавляйте информацию о сбое
- •Добивайтесь атомарности методов по отношению к сбоям
- •Не игнорируйте исключений
- •Глава 9 Потоки
- •Синхронизируйте доступ потоков к совместно используемым изменяемым данным
- •Избегайте избыточной синхронизации
- •Никогда не вызывайте метод wait вне цикла
- •Не попадайте в зависимость от планировщика потоков
- •При работе с потоками документируйте уровень безопасности
- •Избегайте группировки потоков
- •Глава 10 Сериализация
- •Соблюдайте осторожность при реализации интерфейса SerializabIe
- •Рассмотрите возможность использования специализированной сериализованной формы
- •Метод readObject должен создаваться с защитой
- •При необходимости создавайте метод readResolue
Добивайтесь атомарности методов по отношению к сбоям
После того как объект инициирует исключение, обычно необходимо, Ч1'обы он оставался во вполне определенном, пригодном для дальнейшей обработки состоянии, даже несмотря на то, что сбой произошел непосредственно в процессе ВЫl1Dлнения -операции. Особенно это касается обрабатываемых исключений, когда предполагается, что клиент будет восстанавливать работоспособность программы. Вообще говоря, вызов метода, завершившийся сбоем, должен оставлять обрабатываемый объект в том же состоянии, в каком тот был перед вызовом. Метод, обладающий таким свойством, называют атомарным по отношению к сбою (failure atomic).
Добиться такого эффекта можно несколькими способами'. Простейший способ заключается в создании неизменяемых объектов (статья 13). Если объект неизменяемый, получение атомарности не требует усилий. Если операция заканчивается сбоем, это может помешать созданию нового объекта, но никогда не оставит уже имеющийся объект в неопределенном состоянии, поскольку состояние каждого неизменяемого объекта согласуется в момент его создания и после этого уже не меняется.
для методов, работающих с изменяемыми объектами, атомарность по отношению к сбою чаще всего достигается путем проверки правильности параметров перед выполнением операции (статья 23). Благодаря этому, любое исключение будет инициироваться до того, как начнется модификация объекта. В качестве примера рассмотрим метод Staok.рор из статьи 5:
public Objeot рор() {
if (size == 0)
throw new EmptyStaokExoeption();
173
Object result = elements[-size];
elements[size] = null; // Убираем устаревшую ссылку
return result;
Если убрать начальную проверку размера, метод все равно будет инициировать исключение при попытке получить элемент из пустого стека. Однако при этом он будет оставлять поле size в неопределенном (отрицательном) состоянии. А это приведет к тому, что сбоем будет завершаться вызов любого метода в этом объекте. Кроме того, само исключение, инициируемое методом рор, не будет соответствовать текущему уровню абстракции (статья 43).
Другой прием, который тесно связан с предыдущим и позволяет добиться атомарности по отношению к сбоям, заключается в упорядочении вычислений таким образом, чтобы все фрагменты кода, способные повлечь сбой, предшествовали первому фрагменту, который модифицирует объект. Такой прием является естественным расширением предыдущего в случаях, когда невозможно произвести проверку аргументов, не выполнив хотя бы части вычислений. Например, рассмотрим случай с классом ТгееМар, элементы которого сортируются по не коему правилу. Для того чтобы в экземпляр ТгееМар можно было добавить элемент, последний должен иметь такой тип, который допускал бы сравнение с помощью процедур, обеспечивающих упорядочение ТгееМар. Попытка добавить элемент неправильного типа, естественно, закончится сбоем (и исключением ClassCastException), который произойдет в процессе поиска этого элемента в дереве, но до того, как в этом дереве что-либо будет изменено.
Третий, редко встречающийся прием, заключается в написании специального кода восстановления (recovery code), который перехватывает сбой, возникающий в ходе выполнения операции, и заставляет объект вернуться в то состояние, в котором он находился в момент, предшествующей началу операции. Этот прием используется главным образом для структур, записываемых в базу данных.
Наконец, последний прием, позволяющий добиться атомарности метода, заключается в том, чтобы выполнять операцию на временной копии объекта, и как только операция будет завершена, замещать содержимое объекта содержимым его временной копии. Такой прием подходит для случая, когда вычисления могут быть выполнены намного быстрее, если поместить данные во временную структуру. Например, метод Collections.sort перед выполнением сортировки загружает полученный список в некий массив с тем, чтобы облегчить доступ к элементам во время внутреннего цикла сортировки. Это сделано для повышения производительности, однако имеет и другое дополнительное преимущество - гарантию того, что предоставленный методу список останется нетронутым, если процедура сортировки завершится сбоем.
К сожалению, не всегда можно достичь атомарности по отношению к отказам.
Например, если два потока одновременно, без должной синхронизации пытаются модифицировать некий объект, последний может остаться в неопределенном состоянии. А потому после перехвата исключения ConcurrentModificationException нельзя
174
полагаться на то, что объект все еще при годен к использованию. Ошибки (в отличие от исключений), как правило, невосстановимы, и потому методам не нужно даже пытаться сохранять атомарность в случае появления ошибки.
Даже там, где можно получить атомарность по отношению к сбоям, она не всегда желательна. Для некоторых операций она существенно увеличивает затраты ресурсов и Сложность вычислений. Вместе с тем очень часто это свойство достигается без особого Труда, если хорошо разобраться с проблемой. Как правило, любое исключение, добавленное в спецификацию метода, должно оставлять объект в том состоянии, в котором он находился до вызова метода. В случае нарушения этого правила в документации API Должно быть четко указано, в каком состоянии будет оставлен объект. К сожалению, множество имеющейся документации к API не стремится достичь этого идеала.