Рейтинговые книги
Читем онлайн Философия Java3 - Брюс Эккель

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 69 70 71 72 73 74 75 76 77 ... 132

В прошлом я твердо считал, что для разработки надежных программ необходимы и контролируемые исключения, и строгая статическая проверка типов. Однако опыт, полученный лично и со стороны1, с языками, более динамичными, чем статичными, привел меня к мысли, что на самом деле главные преимущества обусловлены следующими аспектами:

1. Унификация модели сообщения об ошибках посредством исключений (независимо от того, заставляет ли компилятор программиста их обрабатывать).

2. Проверка типов, не привязанная к тому, когда она проводится — на стадии компиляции или во время работы программы.

Вдобавок снижение ограничений времени компиляции весьма положительно отражается на продуктивности программиста. С другой стороны, для компенсации чрезмерной жесткости статической проверки типов необходимы рефлексия и параметризация, как вы убедитесь в некоторых примерах книги.

Некоторые уверяли меня, что все сказанное является кощунством, безнадежно испортит мою репутацию, приведет к гибели цивилизации и провалу большой доли программных проектов. Вера в то, что выявление ошибок на стадии компиляции спасет ваш проект, весьма сильна, но гораздо важнее сознавать ограничения того, на что способен компьютер. Стоит помнить:

«Хороший язык программирования помогает программистам писать хорошие программы. Ни один из языков программирования не может запретить своим пользователям писать плохие программы20».

В любом случае исчезновение когда-либо из Java контролируемых исключений весьма маловероятно. Это слишком радикальное изменение языка, и защитники их в Sun весьма сильны. История Sun неотделима от политики абсолютной обратной совместимости — фактически любое программное обеспечение Sun работает на любом оборудовании Sun, как бы старо оно ни было. Но, если вы чувствуете, что контролируемые исключения становятся для вас препятствием (особенно если вас заставляют обрабатывать исключение, а вы не знаете, как с ним поступить), существует несколько вариантов.

Передача исключений на консоль

В несложных программах, как во многих примерах данной книги, простейшим решением является передача исключения за пределы метода main(), на консоль. К примеру, при открытии файла для чтения (подробности вы вскоре узнаете) необходимо открыть и закрыть поток FilelnputStream, который возбуждает исключения. В небольшой программе можно поступить следующим образом (подобный подход характерен для многих примеров книги):

//• excepti ons/Mai nExcepti on.java

import java io *;

public class MainException {

// Передаем все исключения на консоль, public static void main(String[] args) throws Exception { // Открываем файл: FilelnputStream file =

new FilelnputStreamC'MainException.java");

// Используем файл // Закрываем файл-file.closeO.

}

} ///:-

Заметьте, что main() — такой же метод, как и все прочие; он тоже может иметь спецификацию исключений, и здесь типом исключения является Exception, базовый класс всех контролируемых исключений. Передавая его на консоль, вы освобождаетесь от необходимости написания предложений try-catch в теле метода main().

Преобразование контролируемых исключений в неконтролируемые

Рассмотренный выше подход хорош при написании метода main(), но в более общих ситуациях не слишком полезен. Подлинная проблема возникает при написании тела самого обычного метода, когда при вызове другого метода вы четко сознаете: «Понятия не имею, что делать с исключением дальше, но „съедать" мне его не хочется, так же как и печатать банальное сообщение». Проблема решается при помощи цепочек исключений. Управляемое исключение просто «заворачивается» в класс RuntimeException примерно так:

try {

// .. делаем что-нибудь полезное } са!сИ(НеЗнаюЧтоДелатьСЭтимКонтролируемымИсключением е) { throw new RuntimeException(e);

}

Решение идеально подходит для тех случаев, когда вы хотите «подавить» контролируемое исключение: вы не «съедаете» его, вам не приходится описывать его в своей спецификации исключений, и благодаря цепочке исключений вы не теряете информацию об исходном исключении.

Описанная методика позволяет игнорировать исключение и пустить его «всплывать» вверх по стеку вызова без необходимости писать блоки try-catch и (или) спецификации исключения. Впрочем, при этом вы все равно можете перехватить и обработать конкретное исключение, используя метод getCause(), как показано ниже:

// exceptions/TurnOffChecking.java // "Подавление" контролируемых исключений, import java io *;

import static net mindview.util.Print.*;

class WrapCheckedException {

void throwRuntimeException(int type) { try {

switch(type) {

case 0: throw new FileNotFoundExceptionO; case 1: throw new IOExceptionO; case 2- throw new RuntimeExceptionCTfle Я?"). default: return;

}

} catch(Exception e) {

// Превращаем в неконтролируемое: throw new RuntimeException(e);

}

}

}

class SomeOtherException extends Exception {}

public class TurnOffChecking {

public static void main(String[] args) {

WrapCheckedException wee = new WrapCheckedExceptionO: // Можно вызвать throwRuntimeExceptionO без блока try, // и позволить исключению RuntimeException покинуть метод-wee. throwRuntimeExceptionO); // Или перехватить исключение: for (int i =0; i <4; i++) try {

if(i < 3)

wee throwRuntimeException(i);

else

throw new SomeOtherExceptionO: } catch(SomeOtherException e) {

print("SomeOtherException. " + e); } catch(RuntimeException re) {

try { продолжение &

throw re.getCauseO, } catch(FileNotFoundException e) {

print("FileNotFoundException. " + e); } catch(IOException e) {

print("IOException- " + e); } catch(Throwable e) {

print("Throwable. " + e);

}

}

}

} /* Output-

Fi1eNotFoundException: java.iо.Fi1eNotFoundException

IOException: java.io.IOException

Throwable: java.lang.RuntimeException. Где Я?

SomeOtherExcepti on: SomeOtherExcepti on

*///:-

Метод WrapCheckedException.throwRuntimeException() содержит код, генерирующий различные типы исключений. Они перехватываются и «заворачиваются» в объекты RuntimeException, становясь таким образом «причиной» этих исключений.

При взгляде на класс TurnOffChecking нетрудно заметить, что вызвать метод throwRuntimeException() можно и без блока try, поскольку он не возбуждает никаких контролируемых исключений. Но когда вы будете готовы перехватить исключение, у вас будет возможность перехватить любое из них — достаточно поместить свой код в блок try. Начинаете вы с перехвата исключений, которые, как вы знаете, могут явно возникнуть в коде блока try, — в нашем случае первым делом перехватывается SomeOtherException. В конце вы перехватываете Runtime-Exception и заново возбуждаете исключение, являющееся его причиной (получая последнее методом getCause(), «завернутое» исключение). Так извлекаются изначальные исключения, обрабатываемые в своих предложениях catch.

Методика «заворачивания» управляемых исключений в объекты Runtime-Exception встречается в некоторых примерах книги. Другое возможное решение — создание собственного класса, производного от RuntimeException. Перехватывать такое исключение не обязательно, но, если вы захотите, такая возможность существует.

Основные правила обработки исключений

Используйте исключения для того, чтобы:

• обработать ошибку на текущем уровне (избегайте перехватывать исключения, если вы не знаете, как с ними поступить);

• исправить проблему и снова вызвать метод, возбудивший исключение;

• предпринять все необходимые действия и продолжить выполнение без повторного вызова метода;

• попытаться найти альтернативный результат вместо того, который должен был бы произвести вызванный метод;

• сделать все возможное в текущем контексте и заново возбудить это же исключение, перенаправив его на более высокий уровень;

• сделать все, что можно в текущем контексте, и возбудить новое исключение, перенаправив его на более высокий уровень;

• завершить работу программы;

• упростить программу (если используемая вами схема обработки исключений делает все только сложнее, значит, она никуда не годится);

• добавить вашей библиотеке и программе безопасности (сначала это поможет в отладке программы, а в дальнейшем окупится ее надежностью).

Резюме

Исключения являются неотъемлемой частью программирования на Java; существует некий барьер, который невозможно преодолеть без умения работать с ними. По этой причине исключения были представлены именно в этой части книги — многими библиотеками (скажем, библиотекой ввода/вывода) просто невозможно нормально пользоваться без обработки исключений.

Одно из преимуществ обработки исключений состоит в том, что она позволяет сосредоточиться на решаемой проблеме, а затем обработать все ошибки в описанном коде в другом месте. Хотя исключения обычно описываются как средство передачи информации и восстановления после ошибок на стадии выполнения, я сильно сомневаюсь, что «восстановление» часто реализуется на практике. По моей оценке, это происходит не более чем в 10% случаев, и даже тогда в основном сводится к раскрутке стека к заведомо стабильному состоянию вместо реального выполнения действий по восстановлению. На мой взгляд, ценность исключений в основном обусловлена именно передачей информации. Java фактически настаивает, что программа должна сообщать обо всех ошибках в виде исключений, и именно это обстоятельство обеспечивает Java большое преимущество перед языками вроде С++, где программа может сообщать об ошибках разными способами (а то и не сообщать вовсе).

1 ... 69 70 71 72 73 74 75 76 77 ... 132
На этой странице вы можете бесплатно читать книгу Философия Java3 - Брюс Эккель бесплатно.
Похожие на Философия Java3 - Брюс Эккель книги

Оставить комментарий