Error catch without try

I have a lot of custom exceptions that I'm throwing in a specific cases in the code, and I'd like to have one catch block at the bottom of the method to handle them all. All the exceptions are chi...

I have a lot of custom exceptions that I’m throwing in a specific cases in the code, and I’d like to have one catch block at the bottom of the method to handle them all.

All the exceptions are children of the Exception class CribbageException, so I’d like to have:

public void myMethod(){
     if (whatever){
          throw new CardException();
     }
     if (something else){
         throw new InvalidCardException();
     }
     if (scenario 3){
          throw new TwoCardsException();
     }
     catch (CribbageException e) {
          System.out.println(e.getMessage());
     }
}

But I’m getting a catch without try error.

Is there any way to use this type of exception handling?

Warlord's user avatar

Warlord

2,77016 silver badges21 bronze badges

asked Mar 8, 2014 at 22:42

swanhella's user avatar

2

Wrap all the throws inside a single try.

public void myMethod(){
    try {
        if (whatever){
             throw new CardException();
        }
        if (something else){
            throw new InvalidCardException();
        }
        if (scenario 3){
            throw new TwoCardsException();
        }
    }
    catch (CribbageException e) {
          System.out.println(e.getMessage());
    }
}

answered Mar 8, 2014 at 22:44

Graham Borland's user avatar

Graham BorlandGraham Borland

59.7k21 gold badges137 silver badges179 bronze badges

1

Jemal Ahmedov

The try/catch statement wraps a code block within a wrapper and catches any exception thrown from that same block. When building large applications it might get a bit tedious when you have to wrap lots of parts of the code within try/catch.

Instead of getting sick of try/catch, there is another way of checking if the executable throws an error, using custom error instances.

Building custom error class

interface ICustomErrorProps extends Error {
  status?: number;
  data?: any;
}

class CustomError {
  constructor(props: ICustomErrorProps) {
    this.status = props.status;
    this.message = props.message;
    this.data = props.data;
  }

  message: ICustomErrorProps["message"];
  status?: ICustomErrorProps["status"];
  data?: ICustomErrorProps["data"];
}

Enter fullscreen mode

Exit fullscreen mode

The code above is building a custom error class which expects, the usual properties that can be found in an error, e.g. status, message, data.

Building an Error Validator

By using custom class, it can be easily determined what type of response has been returned by checking the instance of the response. To illustrate how to do this, here is an ErrorValidator, which will determine the type of response.

type IResponse<T> = T | CustomError;

class ErrorValidator {
  constructor() {
    this.isError = this.isError.bind(this);
    this.isSuccess = this.isSuccess.bind(this);
  }

  public isError<T>(result: IResponse<T>): result is CustomError {
    return result instanceof CustomError;
  }

  public isSuccess<T>(result: IResponse<T>): result is T {
    return !this.isError(result);
  }
}

Enter fullscreen mode

Exit fullscreen mode

The IResponse type defines what type the response can be — in this case either success T or error CustomError.

The ErrorValidator has two functions, isError and isSuccess. The isError function is checking if the instanceof the object is the CustomError that was defined above.

The TypeScript type predicate result is CustomError will automatically cast the result to CustomError if the returned condition is true.

ErrorValidator in action

One way to see this in action is to build an abstraction for an HTTP client. The HTTP client can extend the ErrorValidator class so the validation functions can be easily accessible by the client instance.

Here is an example of an HTTP client:

class HttpClient extends ErrorValidator {
  public async request<T>(
    url: string,
    options?: RequestInit
  ): Promise<IResponse<T>> {
    return fetch(url, options)
      .then((response) => response.json())
      .then((result: T) => result)
      .catch((error) => new CustomError(error));
  }
}

Enter fullscreen mode

Exit fullscreen mode

The request function of the HttpClient is returning a promise of the IResponse type defined above. The catch of the fetch creates a new instance of the CustomError which later on can be validated.

Here is an example of how to consume the HttpClient:

interface IUserDetails {
  firstName: string;
  lastName: string;
  dob: Date;
}

async function init() {
  const httpClient = new HttpClient();

  const userDetails = await httpClient.request<IUserDetails>(
    "https://my-domain.com/user-details"
  );

  if (httpClient.isError(userDetails)) {
    console.log("An error occurred: ", userDetails.message);
    // Do something with the error
    return;
  }
  console.log("API Response data: ", userDetails);
  // Do something with the success
}
init();

Enter fullscreen mode

Exit fullscreen mode

The main trick to bear in mind when using the custom error classes is the instanceof operator. As this is a pure JavaScript operator the same approach can be taken without TypeScript. The only difference will be that it won’t apply static type checking.

I might invoke the wrath of Pythonistas (don’t know as I don’t use Python much) or programmers from other languages with this answer, but in my opinion most functions should not have a catch block, ideally speaking. To show why, let me contrast this to manual error code propagation of the kind I had to do when working with Turbo C in the late 80s and early 90s.

So let’s say we have a function to load an image or something like that in response to a user selecting an image file to load, and this is written in C and assembly:

enter image description here

I omitted some low-level functions but we can see that I’ve identified different categories of functions, color-coded, based on what responsibilities they have with respect to error-handling.

Point of Failure and Recovery

Now it was never hard to write the categories of functions I call the «possible point of failures» (the ones that throw, i.e.) and the «error recovery and report» functions (the ones that catch, i.e.).

Those functions were always trivial to write correctly before exception handling was available since a function that can run into an external failure, like failing to allocate memory, can just return a NULL or 0 or -1 or set a global error code or something to this effect. And error recovery/reporting was always easy since once you worked your way down the call stack to a point where it made sense to recover and report failures, you just take the error code and/or message and report it to the user. And naturally a function at the leaf of this hierarchy which can never, ever fail no matter how it’s changed in the future (Convert Pixel) is dead simple to write correctly (at least with respect to error handling).

Error Propagation

However, the tedious functions prone to human error were the error propagators, the ones that didn’t directly run into failure but called functions that could fail somewhere deeper in the hierarchy. At that point, Allocate Scanline might have to handle a failure from malloc and then return an error down to Convert Scanlines, then Convert Scanlines would have to check for that error and pass it down to Decompress Image, then Decompress Image->Parse Image, and Parse Image->Load Image, and Load Image to the user-end command where the error is finally reported.

This is where a lot of humans make mistakes since it only takes one error propagator to fail to check for and pass down the error for the entire hierarchy of functions to come toppling down when it comes to properly handling the error.

Further, if error codes are returned by functions, we pretty much lose the ability in, say, 90% of our codebase, to return values of interest on success since so many functions would have to reserve their return value for returning an error code on failure.

Reducing Human Error: Global Error Codes

So how can we reduce the possibility of human error? Here I might even invoke the wrath of some C programmers, but an immediate improvement in my opinion is to use global error codes, like OpenGL with glGetError. This at least frees the functions to return meaningful values of interest on success. There are ways to make this thread-safe and efficient where the error code is localized to a thread.

There are also some cases where a function might run into an error but it’s relatively harmless for it to keep going a little bit longer before it returns prematurely as a result of discovering a previous error. This allows for such a thing to happen without having to check for errors against 90% of function calls made in every single function, so it can still allow proper error handling without being so meticulous.

Reducing Human Error: Exception-Handling

However, the above solution still requires so many functions to deal with the control flow aspect of manual error propagation, even if it might have reduced the number of lines of manual if error happened, return error type of code. It wouldn’t eliminate it completely since there would still often need to be at least one place checking for an error and returning for almost every single error propagation function. So this is when exception-handling comes into the picture to save the day (sorta).

But the value of exception-handling here is to free the need for dealing with the control flow aspect of manual error propagation. That means its value is tied to the ability to avoid having to write a boatload of catch blocks throughout your codebase. In the above diagram, the only place that should have to have a catch block is the Load Image User Command where the error is reported. Nothing else should ideally have to catch anything because otherwise it’s starting to get as tedious and as error-prone as error code handling.

So if you ask me, if you have a codebase that really benefits from exception-handling in an elegant way, it should have the minimum number of catch blocks (by minimum I don’t mean zero, but more like one for every unique high-end user operation that could fail, and possibly even fewer if all high-end user operations are invoked through a central command system).

Resource Cleanup

However, exception-handling only solves the need to avoid manually dealing with the control flow aspects of error propagation in exceptional paths separate from normal flows of execution. Often a function which serves as an error propagator, even if it does this automatically now with EH, might still acquire some resources it needs to destroy. For example, such a function might open a temporary file it needs to close before returning from the function no matter what, or lock a mutex it needs to unlock no matter what.

For this, I might invoke the wrath of a lot of programmers from all sorts of languages, but I think the C++ approach to this is ideal. The language introduces destructors which get invoked in a deterministic fashion the instant an object goes out of scope. Because of this, C++ code which, say, locks a mutex through a scoped mutex object with a destructor need not manually unlock it, since it will be automatically unlocked once the object goes out of scope no matter what happens (even if an exception is encountered). So there’s really no need for well-written C++ code to ever have to deal with local resource cleanup.

In languages that lack destructors, they might need to use a finally block to manually clean up local resources. That said, it still beats having to litter your code with manual error propagation provided you don’t have to catch exceptions all over the freaking place.

Reversing External Side Effects

This is the most difficult conceptual problem to solve. If any function, whether it’s an error propagator or point of failure causes external side effects, then it needs to roll back or «undo» those side effects to return the system back into a state as though the operation never occurred, instead of a «half-valid» state where the operation halfway succeeded. I know of no languages that make this conceptual problem much easier except languages that simply reduce the need for most functions to cause external side effects in the first place, like functional languages which revolve around immutability and persistent data structures.

Here finally is arguably the among the most elegant solutions out there to the problem in languages revolving around mutability and side effects, because often this type of logic is very specific to a particular function and doesn’t map so well to the concept of «resource cleanup». And I recommend using finally liberally in these cases to make sure your function reverses side effects in languages that support it, regardless of whether or not you need a catch block (and again, if you ask me, well-written code should have the minimum number of catch blocks, and all catch blocks should be in places where it makes the most sense as with the diagram above in Load Image User Command).

Dream Language

However, IMO finally is close to ideal for side effect reversal but not quite. We need to introduce one boolean variable to effectively roll back side effects in the case of a premature exit (from a thrown exception or otherwise), like so:

bool finished = false;
try
{
    // Cause external side effects.
    ...

    // Indicate that all the external side effects were
    // made successfully.
    finished = true; 
}
finally
{
    // If the function prematurely exited before finishing
    // causing all of its side effects, whether as a result of
    // an early 'return' statement or an exception, undo the
    // side effects.
    if (!finished)
    {
        // Undo side effects.
        ...
    }
}

If I could ever design a language, my dream way of solving this problem would be like this to automate the above code:

transaction
{
    // Cause external side effects.
    ...
}
rollback
{
    // This block is only executed if the above 'transaction'
    // block didn't reach its end, either as a result of a premature
    // 'return' or an exception.

    // Undo side effects.
    ...
}

… with destructors to automate cleanup of local resources, making it so we only need transaction, rollback, and catch (though I might still want to add finally for, say, working with C resources that don’t clean themselves up). However, finally with a boolean variable is the closest thing to making this straightforward that I’ve found so far lacking my dream language. The second most straightforward solution I’ve found for this is scope guards in languages like C++ and D, but I always found scope guards a little bit awkward conceptually since it blurs the idea of «resource cleanup» and «side effect reversal». In my opinion those are very distinct ideas to be tackled in a different way.

My little pipe dream of a language would also revolve heavily around immutability and persistent data structures to make it much easier, though not required, to write efficient functions that don’t have to deep copy massive data structures in their entirety even though the function causes no side effects.

Conclusion

So anyway, with my ramblings aside, I think your try/finally code for closing the socket is fine and great considering that Python doesn’t have the C++ equivalent of destructors, and I personally think you should use that liberally for places that need to reverse side effects and minimize the number of places where you have to catch to places where it makes the most sense.

Это первая часть статьи, посвященной такому языковому механизму Java как исключения (вторая (checked/unchecked) вот). Она имеет вводный характер и рассчитана на начинающих разработчиков или тех, кто только приступает к изучению языка.

Также я веду курс «Scala for Java Developers» на платформе для онлайн-образования udemy.com (аналог Coursera/EdX).

1. Ключевые слова: try, catch, finally, throw, throws
2. Почему используем System.err, а не System.out
3. Компилятор требует вернуть результат (или требует молчать)
4. Нелокальная передача управления (nonlocal control transfer)
5. try + catch (catch — полиморфен)
6. try + catch + catch + …
7. try + finally
8. try + catch + finally
9. Вложенные try + catch + finally

1. Ключевые слова: try, catch, finally, throw, throws

Механизм исключительных ситуаций в Java поддерживается пятью ключевыми словами

  • try
  • catch
  • finally
  • throw
  • throws

«Магия» (т.е. некоторое поведение никак не отраженное в исходном коде и потому неповторяемое пользователем) исключений #1 заключается в том, что catch, throw, throws можно использовать исключительно с java.lang.Throwable или его потомками.

throws:
Годится

public class App {
    public static void main(String[] args) throws Throwable {}
}

Не годится

public class App {
    public static void main(String[] args) throws String {}
}

>> COMPILATION ERROR: Incompatible types: required 'java.lang.Throwable', found: 'java.lang.String'

catch:
Годится

public class App {
    public static void main(String[] args) {
        try {
        } catch (Throwable t) {}
    }
}

Не годится

public class App {
    public static void main(String[] args) {
        try {
        } catch (String s) {}
    }
}

>> COMPILATION ERROR: Incompatible types: required 'java.lang.Throwable', found: 'java.lang.String'

throw:
Годится

public class App {
    public static void main(String[] args) {
        // Error - потомок Throwable
        throw new Error();
    }
}

Не годится

public class App {
    public static void main(String[] args) {
        throw new String("Hello!");
    }
}

>> COMPILATION ERROR: Incompatible types: required 'java.lang.Throwable', found: 'java.lang.String'

Кроме того, throw требуется не-null аргумент, иначе NullPointerException в момент выполнения

public class App {
    public static void main(String[] args) {
        throw null;
    }
}

>> RUNTIME ERROR: Exception in thread "main" java.lang.NullPointerException

throw и new — это две независимых операции. В следующем коде мы независимо создаем объект исключения и «бросаем» его

public class App {
    public static void main(String[] args) {
        Error ref = new Error(); // создаем экземпляр
        throw ref;               // "бросаем" его
    }
}

>> RUNTIME ERROR: Exception in thread "main" java.lang.Error

Однако, попробуйте проанализировать вот это

public class App {
    public static void main(String[] args) {
        f(null);
    }
    public static void f(NullPointerException e) {
        try {
            throw e;
        } catch (NullPointerException npe) {
            f(npe);
        }
    }
}

>> RUNTIME ERROR: Exception in thread "main" java.lang.StackOverflowError

2. Почему используем System.err, а не System.out

System.out — buffered-поток вывода, а System.err — нет. Таким образом вывод может быть как таким

public class App {
    public static void main(String[] args) {
        System.out.println("sout");
        throw new Error();
    }
}
>> RUNTIME ERROR: Exception in thread "main" java.lang.Error
>> sout

Так и вот таким (err обогнало out при выводе в консоль)

public class App {
    public static void main(String[] args) {
        System.out.println("sout");
        throw new Error();
    }
}
>> sout
>> RUNTIME ERROR: Exception in thread "main" java.lang.Error

Давайте это нарисуем

                      буфер сообщений
                    +----------------+
                 +->| msg2 msg1 msg0 | --> out 
                /   +----------------+        
               /                                 +-> +--------+
ВАШЕ ПРИЛОЖЕНИЕ                                      | КОНСОЛЬ|
                                                +-> +--------+
                                               /
                 +------------------------> err
                 нет буфера, сразу печатаем

когда Вы пишете в System.err — ваше сообщение тут же выводится на консоль, но когда пишете в System.out, то оно может на какое-то время быть буферизированно. Stacktrace необработанного исключение выводится через System.err, что позволяет им обгонять «обычные» сообщения.

3. Компилятор требует вернуть результат (или требует молчать)

Если в объявлении метода сказано, что он возвращает НЕ void, то компилятор зорко следит, что бы мы вернули экземпляр требуемого типа или экземпляр типа, который можно неявно привести к требуемому

public class App { 
    public double sqr(double arg) { // надо double
        return arg * arg;           // double * double - это double  
    }
}

public class App { 
    public double sqr(double arg) { // надо double
        int k = 1;                  // есть int
        return k;                   // можно неявно преобразовать int в double
    }
}

// на самом деле, компилятор сгенерирует байт-код для следующих исходников 
public class App { 
    public double sqr(double arg) { // надо double
        int k = 1;                  // есть int
        return (double) k;          // явное преобразование int в double
    }
}

вот так не пройдет (другой тип)

public class App {
    public static double sqr(double arg) {
        return "hello!";
    }
}

>> COMPILATION ERROR: Incompatible types. Required: double. Found: java.lang.String

Вот так не выйдет — нет возврата

public class App {
    public static double sqr(double arg) {
    }
}

>> COMPILATION ERROR: Missing return statement

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

public class App {
    public static double sqr(double arg) {
        if (System.currentTimeMillis() % 2 == 0) {
            return arg * arg; // если currentTimeMillis() - четное число, то все ОК
        }
        // а если нечетное, что нам возвращать?
    }
}

>> COMPILATION ERROR: Missing return statement

Компилятор отслеживает, что бы мы что-то вернули, так как иначе непонятно, что должна была бы напечатать данная программа

public class App {
    public static void main(String[] args) {
        double d = sqr(10.0); // ну, и чему равно d?
        System.out.println(d);
    }    
    public static double sqr(double arg) {
        // nothing
    }
}

>> COMPILATION ERROR: Missing return statement

Из-забавного, можно ничего не возвращать, а «повесить метод»

public class App {
    public static double sqr(double arg) {
        while (true); // Удивительно, но КОМПИЛИРУЕТСЯ!
    }
}

Тут в d никогда ничего не будет присвоено, так как метод sqr повисает

public class App {
    public static void main(String[] args) {
        double d = sqr(10.0);  // sqr - навсегда "повиснет", и 
        System.out.println(d); // d - НИКОГДА НИЧЕГО НЕ БУДЕТ ПРИСВОЕНО!
    }    
    public static double sqr(double arg) {
        while (true); // Вот тут мы на века "повисли"
    }
}

Компилятор пропустит «вилку» (таки берем в квадрат ИЛИ висим)

public class App {
    public static double sqr(double arg) {
        if (System.currentTimeMillis() % 2 == 0) {
            return arg * arg; // ну ладно, вот твой double
        } else {
            while (true);     // а тут "виснем" навсегда
        }
    }
}

Но механизм исключений позволяет НИЧЕГО НЕ ВОЗВРАЩАТЬ!

public class App {
    public static double sqr(double arg) {
        throw new RuntimeException();
    }
}

Итак, у нас есть ТРИ варианта для компилятора

public class App {
    public static double sqr(double arg) {// согласно объявлению метода ты должен вернуть double
        long time = System.currentTimeMillis();
        if (time % 2 == 0) {
            return arg * arg;             // ок, вот твой double
        } else if (time % 2 == 1) { {
            while (true);                 // не, я решил "повиснуть"
        } else {
            throw new RuntimeException(); // или бросить исключение
        }
    }
}

Но КАКОЙ ЖЕ double вернет функция, бросающая RuntimeException?
А НИКАКОЙ!

public class App {
    public static void main(String[] args) {
        // sqr - "сломается" (из него "выскочит" исключение),  
        double d = sqr(10.0);  // выполнение метода main() прервется в этой строчке и
                               // d - НИКОГДА НИЧЕГО НЕ БУДЕТ ПРИСВОЕНО!
        System.out.println(d); // и печатать нам ничего не придется!
    }    
    public static double sqr(double arg) {
        throw new RuntimeException(); // "бросаем" исключение
    }
}

>> RUNTIME ERROR: Exception in thread "main" java.lang.RuntimeException

Подытожим: бросаемое исключение — это дополнительный возвращаемый тип. Если ваш метод объявил, что возвращает double, но у вас нет double — можете бросить исключение. Если ваш метод объявил, что ничего не возвращает (void), но у вам таки есть что сказать — можете бросить исключение.

Давайте рассмотрим некоторый пример из практики.

Задача: реализовать функцию, вычисляющую площадь прямоугольника

public static int area(int width, int height) {...}

важно, что задание звучит именно так, в терминах предметной области — «вычислить площадь прямоугольника», а не в терминах решения «перемножить два числа»:

public static int area(int width, int height) {
    return width * height; // тут просто перемножаем
}

Вопрос: что делать, если мы обнаружили, что хотя бы один из аргументов — отрицательное число?
Если просто умножить, то мы пропустили ошибочные данные дальше. Что еще хуже, возможно, мы «исправили ситуацию» — сказали что площадь прямоугольника с двумя отрицательными сторонами -10 и -20 = 200.

Мы не можем ничего не вернуть

public static int area(int width, int height) {
    if (width < 0 || height < 0) {
        // у вас плохие аргументы, извините
    } else {
        return width * height;
    }
}

>> COMPILATION ERROR: Missing return statement

Можно, конечно, отписаться в консоль, но кто ее будет читать и как определить где была поломка. При чем, вычисление то продолжится с неправильными данными

public static int area(int width, int height) {
    if (width < 0 || height < 0) {
        System.out.println("Bad ...");
    }
    return width * height;
}

Можно вернуть специальное значение, показывающее, что что-то не так (error code), но кто гарантирует, что его прочитают, а не просто воспользуются им?

public static int area(int width, int height) {
    if (width < 0 || height < 0) {
        return -1; // специальное "неправильное" значение площади
    }
    return width * height;
}

Можем, конечно, целиком остановить виртуальную машину

public static int area(int width, int height) {
    if (width < 0 || height < 0) {
        System.exit(0);
    }
    return width * height;
}

Но «правильный путь» таков: если обнаружили возможное некорректное поведение, то
1. Вычисления остановить, сгенерировать сообщение-поломку, которое трудно игнорировать, предоставить пользователю информацию о причине, предоставить пользователю возможность все починить (загрузить белье назад и повторно нажать кнопку старт)

public static int area(int width, int height) {
    if (width < 0 || height < 0) {
        throw new IllegalArgumentException("Negative sizes: w = " + width + ", h = " + height);
    }
    return width * height;
}

4. Нелокальная передача управления (nonlocal control transfer)

Механизм исключительных ситуация (исключений) — это механизм НЕЛОКАЛЬНОЙ ПЕРЕДАЧИ УПРАВЛЕНИЯ.
Что под этим имеется в виду?
Программа, в ходе своего выполнения (точнее исполнения инструкций в рамках отдельного потока), оперирует стеком («стопкой») фреймов. Передача управления осуществляется либо в рамках одного фрейма

public class App {
    public static void main(String[] args) {
        // Пример: ОПЕРАТОР ПОСЛЕДОВАТЕЛЬНОСТИ
        int x = 42;    // первый шаг
        int y = x * x; // второй шаг
        x = x * y;     // третий шаг
        ...
    }
}

public class App {
    public static void main(String[] args) {
        // Пример: ОПЕРАТОР ВЕТВЛЕНИЯ
        if (args.length > 2) { первый шаг
            // второй шаг или тут
            ...
        } else {
            // или тут
            ...
        }
        // третий шаг
        ...
    }
}

public class App {
    public static void main(String[] args) {
        // Пример: ОПЕРАТОР ЦИКЛА do..while
        int x = 1;      
        do {
            ...
        } while (x++ < 10);
        ...
    }
}

и другие операторы.

Либо передача управления происходит в «стопке» фреймов между СОСЕДНИМИ фреймами

  • вызов метода: создаем новый фрейм, помещаем его на верхушку стека и переходим в него
  • выход из метода: возвращаемся к предыдущему фрейму (через return или просто кончились инструкции в методе)

return — выходим из ОДНОГО фрейма (из фрейма #4(метод h()))

public class App {
    public static void main(String[] args) {
        System.err.println("#1.in");
        f(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println("#1.out"); // вернулись
    } // выходим из текущего фрейма, кончились инструкции

    public static void f() {
        System.err.println(".   #2.in");
        g(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println(".   #2.out");  //вернулись
    } // выходим из текущего фрейма, кончились инструкции

    public static void g() {
        System.err.println(".   .   #3.in");
        h(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println(".   .   #3.out"); // вернулись
    } // выходим из текущего фрейма, кончились инструкции

    public static void h() {
        System.err.println(".   .   .   #4.in");
        if (true) {
            System.err.println(".   .   .   #4.RETURN");
            return; // выходим из текущего фрейма по 'return'
        }
        System.err.println(".   .   .   #4.out"); // ПРОПУСКАЕМ
    }
}

>> #1.in
>> .   #2.in
>> .   .   #3.in
>> .   .   .   #4.in
>> .   .   .   #4.RETURN
>> .   .   #3.out
>> .   #2.out
>> #1.out

throw — выходим из ВСЕХ фреймов

public class App {
    public static void main(String[] args) {
        System.err.println("#1.in");
        f(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println("#1.out"); // ПРОПУСТИЛИ!
    }

    public static void f() {
        System.err.println(".   #2.in");
        g(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println(".   #2.out"); // ПРОПУСТИЛИ!
    }

    public static void g() {
        System.err.println(".   .   #3.in");
        h(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println(".   .   #3.out"); // ПРОПУСТИЛИ!
    }

    public static void h() {
        System.err.println(".   .   .   #4.in");
        if (true) {
            System.err.println(".   .   .   #4.THROW");
            throw new Error(); // выходим со всей пачки фреймов ("раскрутка стека") по 'throw'
        }
        System.err.println(".   .   .   #4.out"); // ПРОПУСТИЛИ!
    }
}

>> #1.in
>> .   #2.in
>> .   .   #3.in
>> .   .   .   #4.in
>> .   .   .   #4.THROW
>> RUNTIME ERROR: Exception in thread "main" java.lang.Error

При помощи catch мы можем остановить летящее исключение (причина, по которой мы автоматически покидаем фреймы).
Останавливаем через 3 фрейма, пролетаем фрейм #4(метод h()) + пролетаем фрейм #3(метод g()) + фрейм #2(метод f())

public class App {
    public static void main(String[] args) {
        System.err.println("#1.in");
        try {
            f(); // создаем фрейм, помещаем в стек, передаем в него управление
        } catch (Error e) { // "перехватили" "летящее" исключение
            System.err.println("#1.CATCH");  // и работаем
        }
        System.err.println("#1.out");  // работаем дальше
    }

    public static void f() {
        System.err.println(".   #2.in");
        g(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println(".   #2.out"); // ПРОПУСТИЛИ!
    }

    public static void g() {
        System.err.println(".   .   #3.in");
        h(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println(".   .   #3.out"); // ПРОПУСТИЛИ!
    }

    public static void h() {
        System.err.println(".   .   .   #4.in");
        if (true) {
            System.err.println(".   .   .   #4.THROW");
            throw new Error(); // выходим со всей пачки фреймов ("раскрутка стека") по 'throw'
        }
        System.err.println(".   .   .   #4.out"); // ПРОПУСТИЛИ!
    }
}

>> #1.in
>> .   #2.in
>> .   .   #3.in
>> .   .   .   #4.in
>> .   .   .   #4.THROW
>> #1.CATCH
>> #1.out

Обратите внимание, стандартный сценарий работы был восстановлен в методе main() (фрейм #1)

Останавливаем через 2 фрейма, пролетаем фрейм #4(метод h()) + пролетаем фрейм #3(метод g())

public class App {
    public static void main(String[] args) {
        System.err.println("#1.in");
        f(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println("#1.out"); // вернулись и работаем
    }

    public static void f() {
        System.err.println(".   #2.in");
        try {
            g(); // создаем фрейм, помещаем в стек, передаем в него управление
        } catch (Error e) { // "перехватили" "летящее" исключение
            System.err.println(".   #2.CATCH");  // и работаем
        }
        System.err.println(".   #2.out");  // работаем дальше
    }

    public static void g() {
        System.err.println(".   .   #3.in");
        h(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println(".   .   #3.out"); // ПРОПУСТИЛИ!
    }

    public static void h() {
        System.err.println(".   .   .   #4.in");
        if (true) {
            System.err.println(".   .   .   #4.THROW");
            throw new Error(); // выходим со всей пачки фреймов ("раскрутка стека") по 'throw'
        }
        System.err.println(".   .   .   #4.out"); // ПРОПУСТИЛИ!
    }
}

>> #1.in
>> .   #2.in
>> .   .   #3.in
>> .   .   .   #4.in
>> .   .   .   #4.THROW
>> .   #2.CATCH
>> .   #2.out
>> #1.out

Останавливаем через 1 фрейм (фактически аналог return, просто покинули фрейм «другим образом»)

public class App {
    public static void main(String[] args) {
        System.err.println("#1.in");
        f(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println("#1.out"); // вернулись и работаем
    }

    public static void f() {
        System.err.println(".   #2.in");
        g(); // создаем фрейм, помещаем в стек, передаем в него управление
        System.err.println(".   #2.out"); // вернулись и работаем
    }

    public static void g() {
        System.err.println(".   .   #3.in");
        try {
            h(); // создаем фрейм, помещаем в стек, передаем в него управление
        } catch (Error e) { // "перехватили" "летящее" исключение
            System.err.println(".   .   #3.CATCH");  // и работаем
        }
        System.err.println(".   .   #3.out");  // работаем дальше
    }

    public static void h() {
        System.err.println(".   .   .   #4.in");
        if (true) {
            System.err.println(".   .   .   #4.THROW");
            throw new Error(); // выходим со всей пачки фреймов ("раскрутка стека") по 'throw'
        }
        System.err.println(".   .   .   #4.out"); // ПРОПУСТИЛИ!
    }
}

>> #1.in
>> .   #2.in
>> .   .   #3.in
>> .   .   .   #4.in
>> .   .   .   #4.THROW
>> .   .   #3.CATCH
>> .   .   #3.out
>> .   #2.out
>> #1.out

Итак, давайте сведем все на одну картинку

// ---Используем RETURN--- // ---Используем THROW---
// Выход из 1-го фрейма    // Выход из ВСЕХ (из 4) фреймов
#1.in                        #1.in
.   #2.in                    .   #2.in
.   .   #3.in                .   .   #3.in
.   .   .   #4.in            .   .   .   #4.in
.   .   .   #4.RETURN        .   .   .   #4.THROW
.   .   #3.out               RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error
.   #2.out                            
#1.out                              

// ---Используем THROW+CATCH---
// Выход из 3-х фреймов      // Выход из 2-х фреймов      // Выход из 1-го фрейма
#1.in                        #1.in                        #1.in
.   #2.in                    .   #2.in                    .   #2.in
.   .   #3.in                .   .   #3.in                .   .   #3.in
.   .   .   #4.in            .   .   .   #4.in            .   .   .   #4.in
.   .   .   #4.THROW         .   .   .   #4.THROW         .   .   .   #4.THROW
#1.CATCH                     .   #2.CATCH                 .   .   #3.CATCH
#1.out                       .   #2.out                   .   .   #3.out
                             #1.out                       . #2.out
                                                          #1.out

5. try + catch (catch — полиморфен)

Напомним иерархию исключений

                    Object
                      |
                  Throwable
                  /      
              Error     Exception
                            |
                    RuntimeException

То, что исключения являются объектами важно для нас в двух моментах
1. Они образуют иерархию с корнем java.lang.Throwable (java.lang.Object — предок java.lang.Throwable, но Object — уже не исключение)
2. Они могут иметь поля и методы (в этой статье это не будем использовать)

По первому пункту: catch — полиморфная конструкция, т.е. catch по типу Parent перехватывает летящие экземпляры любого типа, который является Parent-ом (т.е. экземпляры непосредственно Parent-а или любого потомка Parent-а)

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            if (true) {throw new RuntimeException();}
            System.err.print(" 1");
        } catch (Exception e) { // catch по Exception ПЕРЕХВАТЫВАЕТ RuntimeException
            System.err.print(" 2");
        }
        System.err.println(" 3");
    }
}

>> 0 2 3

Даже так: в блоке catch мы будем иметь ссылку типа Exception на объект типа RuntimeException

public class App {
    public static void main(String[] args) {
        try {
            throw new RuntimeException();
        } catch (Exception e) {
            if (e instanceof RuntimeException) {
                RuntimeException re = (RuntimeException) e;
                System.err.print("Это RuntimeException на самом деле!!!");              
            } else {
                System.err.print("В каком смысле не RuntimeException???");              
            }            
        }
    }
}

>> Это RuntimeException на самом деле!!!

catch по потомку не может поймать предка

public class App {
    public static void main(String[] args) throws Exception { // пока игнорируйте 'throws'
        try {
            System.err.print(" 0");
            if (true) {throw new Exception();}
            System.err.print(" 1");
        } catch (RuntimeException e) {
            System.err.print(" 2");              
        }
        System.err.print(" 3");              
    }
}

>> 0 
>> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Exception

catch по одному «брату» не может поймать другого «брата» (Error и Exception не находятся в отношении предок-потомок, они из параллельных веток наследования от Throwable)

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            if (true) {throw new Error();}
            System.err.print(" 1");
        } catch (Exception e) {
            System.err.print(" 2");              
        }
        System.err.print(" 3");              
    }
}

>> 0 
>> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error

По предыдущим примерам — надеюсь вы обратили внимание, что если исключение перехвачено, то JVM выполняет операторы идущие ПОСЛЕ последних скобок try+catch.
Но если не перехвачено, то мы
1. не заходим в блок catch
2. покидаем фрейм метода с летящим исключением

А что будет, если мы зашли в catch, и потом бросили исключение ИЗ catch?

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            if (true) {throw new RuntimeException();}
            System.err.print(" 1");
        } catch (RuntimeException e) {     // перехватили RuntimeException
            System.err.print(" 2");
            if (true) {throw new Error();} // но бросили Error
        }
        System.err.println(" 3");          // пропускаем - уже летит Error
    }
}

>> 0 2
>> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error

В таком случае выполнение метода тоже прерывается (не печатаем «3»). Новое исключение не имеет никакого отношения к try-catch

Мы можем даже кинуть тот объект, что у нас есть «на руках»

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            if (true) {throw new RuntimeException();}
            System.err.print(" 1");
        } catch (RuntimeException e) { // перехватили RuntimeException
            System.err.print(" 2");
            if (true) {throw e;}       // и бросили ВТОРОЙ раз ЕГО ЖЕ
        }
        System.err.println(" 3");      // пропускаем - опять летит RuntimeException
    }
}

>> 0 2
>> RUNTIME EXCEPTION: Exception in thread "main" java.lang.RuntimeException

И мы не попадем в другие секции catch, если они есть

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            if (true) {throw new RuntimeException();}
            System.err.print(" 1");
        } catch (RuntimeException e) {     // перехватили RuntimeException
            System.err.print(" 2");
            if (true) {throw new Error();} // и бросили новый Error
        } catch (Error e) { // хотя есть cath по Error "ниже", но мы в него не попадаем
            System.err.print(" 3");
        }
        System.err.println(" 4");
    }
}

>> 0 2
>> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error 

Обратите внимание, мы не напечатали «3», хотя у нас летит Error а «ниже» расположен catch по Error. Но важный момент в том, что catch имеет отношение исключительно к try-секции, но не к другим catch-секциям.

Как покажем ниже — можно строить вложенные конструкции, но вот пример, «исправляющий» эту ситуацию

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            if (true) {throw new RuntimeException();}
            System.err.print(" 1");
        } catch (RuntimeException e) { // перехватили RuntimeException
            System.err.print(" 2.1");
            try {
                System.err.print(" 2.2");
                if (true) {throw new Error();} // и бросили новый Error
                System.err.print(" 2.3");
            } catch (Throwable t) {            // перехватили Error
                System.err.print(" 2.4");                 
            }
            System.err.print(" 2.5");
        } catch (Error e) { // хотя есть cath по Error "ниже", но мы в него не попадаем
            System.err.print(" 3");
        }
        System.err.println(" 4");
    }
}

>> 0 2.1 2.2 2.4 2.5 4

6. try + catch + catch + …

Как вы видели, мы можем расположить несколько catch после одного try.

Но есть такое правило — нельзя ставить потомка после предка! (RuntimeException после Exception)

public class App {
    public static void main(String[] args) {
        try {
        } catch (Exception e) {
        } catch (RuntimeException e) {
        }
    }
}

>> COMPILATION ERROR: Exception 'java.lang.RuntimeException' has alredy been caught

Ставить брата после брата — можно (RuntimeException после Error)

public class App {
    public static void main(String[] args) {
        try {
        } catch (Error e) {
        } catch (RuntimeException e) {
        }
    }
}

Как происходит выбор «правильного» catch? Да очень просто — JVM идет сверху-вниз до тех пор, пока не найдет такой catch что в нем указано ваше исключение или его предок — туда и заходит. Ниже — не идет.

public class App {
    public static void main(String[] args) {
        try {
            throw new Exception();
        } catch (RuntimeException e) {
            System.err.println("catch RuntimeException");
        } catch (Exception e) {
            System.err.println("catch Exception");
        } catch (Throwable e) {
            System.err.println("catch Throwable");
        }
        System.err.println("next statement");
    }
}

>> catch Exception
>> next statement

Выбор catch осуществляется в runtime (а не в compile-time), значит учитывается не тип ССЫЛКИ (Throwable), а тип ССЫЛАЕМОГО (Exception)

public class App {
    public static void main(String[] args) {
        try {
            Throwable t = new Exception(); // ссылка типа Throwable указывает на объект типа Exception
            throw t;
        } catch (RuntimeException e) {
            System.err.println("catch RuntimeException");
        } catch (Exception e) {
            System.err.println("catch Exception");
        } catch (Throwable e) {
            System.err.println("catch Throwable");
        }
        System.err.println("next statement");
    }
}

>> catch Exception
>> next statement

7. try + finally

finally-секция получает управление, если try-блок завершился успешно

public class App {
    public static void main(String[] args) {
        try {
            System.err.println("try");
        } finally {
            System.err.println("finally");
        }
    }
}

>> try
>> finally

finally-секция получает управление, даже если try-блок завершился исключением

public class App {
    public static void main(String[] args) {
        try {
            throw new RuntimeException();
        } finally {
            System.err.println("finally");
        }
    }
}

>> finally
>> Exception in thread "main" java.lang.RuntimeException

finally-секция получает управление, даже если try-блок завершился директивой выхода из метода

public class App {
    public static void main(String[] args) {
        try {
            return;
        } finally {
            System.err.println("finally");
        }
    }
}

>> finally

finally-секция НЕ вызывается только если мы «прибили» JVM

public class App {
    public static void main(String[] args) {
        try {
            System.exit(42);
        } finally {
            System.err.println("finally");
        }
    }
}

>> Process finished with exit code 42

System.exit(42) и Runtime.getRuntime().exit(42) — это синонимы

public class App {
    public static void main(String[] args) {
        try {
            Runtime.getRuntime().exit(42);
        } finally {
            System.err.println("finally");
        }
    }
}

>> Process finished with exit code 42

И при Runtime.getRuntime().halt(42) — тоже не успевает зайти в finally

public class App {
    public static void main(String[] args) {
        try {
            Runtime.getRuntime().halt(42);
        } finally {
            System.err.println("finally");
        }
    }
}

>> Process finished with exit code 42

exit() vs halt()
javadoc: java.lang.Runtime#halt(int status)
… Unlike the exit method, this method does not cause shutdown hooks to be started and does not run uninvoked finalizers if finalization-on-exit has been enabled. If the shutdown sequence has already been initiated then this method does not wait for any running shutdown hooks or finalizers to finish their work.

Однако finally-секция не может «починить» try-блок завершившийся исключение (заметьте, «more» — не выводится в консоль)

public class App {
    public static void main(String[] args) {
        try {
            System.err.println("try");
            if (true) {throw new RuntimeException();}
        } finally {
            System.err.println("finally");
        }
        System.err.println("more");
    }
}

>> try
>> finally
>> Exception in thread "main" java.lang.RuntimeException

Трюк с «if (true) {…}» требуется, так как иначе компилятор обнаруживает недостижимый код (последняя строка) и отказывается его компилировать

public class App {
    public static void main(String[] args) {
        try {
            System.err.println("try");
            throw new RuntimeException();
        } finally {
            System.err.println("finally");
        }
        System.err.println("more");
    }
}

>> COMPILER ERROR: Unrechable statement 

И finally-секция не может «предотвратить» выход из метода, если try-блок вызвал return («more» — не выводится в консоль)

public class App {
    public static void main(String[] args) {
        try {
            System.err.println("try");
            if (true) {return;}
        } finally {
            System.err.println("finally");
        }
        System.err.println("more");
    }
}

>> try
>> finally

Однако finally-секция может «перебить» throw/return при помощи другого throw/return

public class App {
    public static void main(String[] args) {
        System.err.println(f());
    }
    public static int f() {
        try {
            return 0;
        } finally {
            return 1;
        }
    }
}

>> 1
public class App {
    public static void main(String[] args) {
        System.err.println(f());
    }
    public static int f() {
        try {
            throw new RuntimeException();
        } finally {
            return 1;
        }
    }
}

>> 1
public class App {
    public static void main(String[] args) {
        System.err.println(f());
    }
    public static int f() {
        try {
            return 0;
        } finally {
            throw new RuntimeException();
        }
    }
}

>> Exception in thread "main" java.lang.RuntimeException
public class App {
    public static void main(String[] args) {
        System.err.println(f());
    }
    public static int f() {
        try {
            throw new Error();
        } finally {
            throw new RuntimeException();
        }
    }
}

>> Exception in thread "main" java.lang.RuntimeException

finally-секция может быть использована для завершающего действия, которое гарантированно будет вызвано (даже если было брошено исключение или автор использовал return) по окончании работы

// open some resource
try {
    // use resource
} finally {
    // close resource
}

Например для освобождения захваченной блокировки

Lock lock = new ReentrantLock();
...
lock.lock();
try {
    // some code
} finally {
    lock.unlock();
}

Или для закрытия открытого файлового потока

InputStream input = new FileInputStream("...");
try {
    // some code
} finally {
    input.close();
}

Специально для этих целей в Java 7 появилась конструкция try-with-resources, ее мы изучим позже.

Вообще говоря, в finally-секция нельзя стандартно узнать было ли исключение.
Конечно, можно постараться написать свой «велосипед»

public class App {
    public static void main(String[] args) {
        System.err.println(f());
    }
    public static int f() {
        long rnd = System.currenttimeMillis();
        boolean finished = false;
        try {
            if (rnd % 3 == 0) {
                throw new Error();
            } else if (rnd % 3 == 1) {
                throw new RuntimeException();
            } else {
                // nothing
            }
            finished = true;
        } finally {
            if (finished) {
                // не было исключений
            } else {
                // было исключение, но какое?
            }
        }
    }
}

Не рекомендуемые практики
— return из finally-секции (можем затереть исключение из try-блока)
— действия в finally-секции, которые могут бросить исключение (можем затереть исключение из try-блока)

8. try + catch + finally

Нет исключения

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            // nothing
            System.err.print(" 1");
        } catch(Error e) {
            System.err.print(" 2");
        } finally {
            System.err.print(" 3");
        }
        System.err.print(" 4");
    }
}

>> 0 1 3 4

Не заходим в catch, заходим в finally, продолжаем после оператора

Есть исключение и есть подходящий catch

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            if (true) {throw new Error();}
            System.err.print(" 1");
        } catch(Error e) {
            System.err.print(" 2");
        } finally {
            System.err.print(" 3");
        }
        System.err.print(" 4");
    }
}

>> 0 2 3 4

Заходим в catch, заходим в finally, продолжаем после оператора

Есть исключение но нет подходящего catch

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            if (true) {throw new RuntimeException();}
            System.err.print(" 1");
        } catch(Error e) {
            System.err.print(" 2");
        } finally {
            System.err.print(" 3");
        }
        System.err.print(" 4");
    }
}

>> 0 3
>> RUNTIME ERROR: Exception in thread "main" java.lang.RuntimeException

Не заходим в catch, заходим в finally, не продолжаем после оператора — вылетаем с неперехваченным исключением

9. Вложенные try + catch + finally

Операторы обычно допускают неограниченное вложение.
Пример с if

public class App {
    public static void main(String[] args) {
        if (args.length > 1) {
            if (args.length > 2) {
                if (args.length > 3) {
                    ...
                }
            }
        }
    }
}

Пример с for

public class App {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            for (int j = 0; j < 10; i++) {
                for (int k = 0; k < 10; k++) {
                    ...
                }
            }
        }
    }
}

Суть в том, что try-cacth-finally тоже допускает неограниченное вложение.
Например вот так

public class App {
    public static void main(String[] args) {
        try {
            try {
                try {
                    ...
                } catch (Exception e) {
                } finally {}
            } catch (Exception e) {
            } finally {}
        } catch (Exception e) {
        } finally {}
    }
}

Или даже вот так

public class App {
    public static void main(String[] args) {
        try {
            try {
                ...
            } catch (Exception e) {
                ...
            } finally {
                ...
            }
        } catch (Exception e) {
            try {
                ...
            } catch (Exception e) {
                ...
            } finally {
                ...
            }
        } finally {
            try {
                ...
            } catch (Exception e) {
                ...
            } finally {
                ...
            }
        }
    }
}

Ну что же, давайте исследуем как это работает.

Вложенный try-catch-finally без исключения

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            try {
                System.err.print(" 1");
                // НИЧЕГО
                System.err.print(" 2");
            } catch (RuntimeException e) {
                System.err.print(" 3"); // НЕ заходим - нет исключения
            } finally {                 
                System.err.print(" 4"); // заходим всегда
            }
            System.err.print(" 5");     // заходим - выполнение в норме
        } catch (Exception e) {
            System.err.print(" 6");     // НЕ заходим - нет исключения
        } finally {
            System.err.print(" 7");     // заходим всегда
        }
        System.err.print(" 8");         // заходим - выполнение в норме
    }
}

>> 0 1 2 4 5 7 8

Мы НЕ заходим в обе catch-секции (нет исключения), заходим в обе finally-секции и выполняем обе строки ПОСЛЕ finally.

Вложенный try-catch-finally с исключением, которое ПЕРЕХВАТИТ ВНУТРЕННИЙ catch

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            try {
                System.err.print(" 1");
                if (true) {throw new RuntimeException();}
                System.err.print(" 2");
            } catch (RuntimeException e) {
                System.err.print(" 3"); // ЗАХОДИМ - есть исключение
            } finally {                 
                System.err.print(" 4"); // заходим всегда
            }
            System.err.print(" 5");     // заходим - выполнение УЖЕ в норме
        } catch (Exception e) {
            System.err.print(" 6");     // не заходим - нет исключения, УЖЕ перехвачено
        } finally {
            System.err.print(" 7");     // заходим всегда
        }
        System.err.print(" 8");         // заходим - выполнение УЖЕ в норме
    }
}

>> 0 1 3 4 5 7 8

Мы заходим в ПЕРВУЮ catch-секцию (печатаем «3»), но НЕ заходим во ВТОРУЮ catch-секцию (НЕ печатаем «6», так как исключение УЖЕ перехвачено первым catch), заходим в обе finally-секции (печатаем «4» и «7»), в обоих случаях выполняем код после finally (печатаем «5»и «8», так как исключение остановлено еще первым catch).

Вложенный try-catch-finally с исключением, которое ПЕРЕХВАТИТ ВНЕШНИЙ catch

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            try {
                System.err.print(" 1");
                if (true) {throw new Exception();}
                System.err.print(" 2");
            } catch (RuntimeException e) {
                System.err.print(" 3"); // НЕ заходим - есть исключение, но НЕПОДХОДЯЩЕГО ТИПА
            } finally {                 
                System.err.print(" 4"); // заходим всегда
            }
            System.err.print(" 5");     // не заходим - выполнение НЕ в норме
        } catch (Exception e) {
            System.err.print(" 6");     // ЗАХОДИМ - есть подходящее исключение
        } finally {
            System.err.print(" 7");     // заходим всегда
        }
        System.err.print(" 8");         // заходим - выполнение УЖЕ в норме
    }
}

>> 0 1 4 6 7 8

Мы НЕ заходим в ПЕРВУЮ catch-секцию (не печатаем «3»), но заходим в ВТОРУЮ catch-секцию (печатаем «6»), заходим в обе finally-секции (печатаем «4» и «7»), в ПЕРВОМ случае НЕ выполняем код ПОСЛЕ finally (не печатаем «5», так как исключение НЕ остановлено), во ВТОРОМ случае выполняем код после finally (печатаем «8», так как исключение остановлено).

Вложенный try-catch-finally с исключением, которое НИКТО НЕ ПЕРЕХВАТИТ

public class App {
    public static void main(String[] args) {
        try {
            System.err.print(" 0");
            try {
                System.err.print(" 1");
                if (true) {throw new Error();}
                System.err.print(" 2");
            } catch (RuntimeException e) {
                System.err.print(" 3"); // НЕ заходим - есть исключение, но НЕПОДХОДЯЩЕГО ТИПА
            } finally {                 
                System.err.print(" 4"); // заходим всегда
            }
            System.err.print(" 5");     // НЕ заходим - выполнение НЕ в норме
        } catch (Exception e) {
            System.err.print(" 6");     // не заходим - есть исключение, но НЕПОДХОДЯЩЕГО ТИПА
        } finally {
            System.err.print(" 7");     // заходим всегда
        }
        System.err.print(" 8");         // не заходим - выполнение НЕ в норме
    }
}

>> 0 1 4 7
>> RUNTIME EXCEPTION: Exception in thread "main" java.lang.Error

Мы НЕ заходим в ОБЕ catch-секции (не печатаем «3» и «6»), заходим в обе finally-секции (печатаем «4» и «7») и в обоих случаях НЕ выполняем код ПОСЛЕ finally (не печатаем «5» и «8», так как исключение НЕ остановлено), выполнение метода прерывается по исключению.

Контакты

Я занимаюсь онлайн обучением Java (вот курсы программирования) и публикую часть учебных материалов в рамках переработки курса Java Core. Видеозаписи лекций в аудитории Вы можете увидеть на youtube-канале, возможно, видео канала лучше систематизировано в этой статье.

Мой метод обучения состоит в том, что я

  1. показываю различные варианты применения
  2. строю усложняющуюся последовательность примеров по каждому варианту
  3. объясняю логику двигавшую авторами (по мере возможности)
  4. даю большое количество тестов (50-100) всесторонне проверяющее понимание и демонстрирующих различные комбинации
  5. даю лабораторные для самостоятельной работы

Данная статье следует пунктам #1 (различные варианты) и #2(последовательность примеров по каждому варианту).

skype: GolovachCourses
email: GolovachCourses@gmail.com


posted 12 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:



  • Quote
  • Report post to moderator

i got this error:

‘catch’ without ‘try’

can anyone help me to solve this error..


posted 12 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:



  • Quote
  • Report post to moderator

Look like you have a catch block without a try block. Add a try block.


posted 12 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:



  • Quote
  • Report post to moderator

What isn’t clear about the error? You can’t have a catch block without a try:

Maybe your braces are not balanced correctly, but that’s just a guess, because I can’t see your source code.

lowercase baba

Posts: 13086

Chrome
Java
Linux


posted 12 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:



  • Quote
  • Report post to moderator

Sylvester Gabriel wrote:i got this error:

‘catch’ without ‘try’

can anyone help me to solve this error..

The compiler should tell you the exact line where the catch that is causing the error is. Make sure you have a try block before it.

There are only two hard things in computer science: cache invalidation, naming things, and off-by-one errors

Marshal

Posts: 77295


posted 12 years ago

  • Mark post as helpful


  • send pies

    Number of slices to send:

    Optional ‘thank-you’ note:



  • Quote
  • Report post to moderator

You may have a try with something after it and before the first catch.

Member Avatar

15 Years Ago

any suggestions?

<%@ page language="java" %>

<%@ page import="trader.webservices.data.LeadDAO" %>
<%@ page import="trader.aabacrm.LeadsList" %>
<%@ page import="trader.webservices.data.*" %>
<%@ page import="trader.webservices.presentation.Lead" %>
<%@ page import="trader.webservices.data.UserInfoDAO" %>
<%@ page import="trader.webservices.data.UserDataVO" %>
<%@ page import="trader.webservices.data.ButtonDAO" %>
<%@ page import="trader.webservices.constants.UserConstants" %>
<%@ page import="trader.webservices.util.Utils" %>
<%@ page import="java.math.*" %>


<%
    RequestDispatcher dispatcher = request.getRequestDispatcher("/jsp/ErrorPage.jsp");

    UserDataVO userData = null;

    String username = request.getParameter("username");
    String password = request.getParameter("password");

    boolean loginOkay = false;
    boolean trialOkay = true;
    boolean pageError = false;

    if ((username != null) && (password != null)) {
        loginOkay = UserInfoDAO.verifyLogin(username,password);
        if (loginOkay) trialOkay = UserInfoDAO.checkTrialOkay(username);
    }

    if (loginOkay && trialOkay) {
        try{
            /**
            * Get accountInfo based on accountName
            */
            if (username != null) {
                userData = UserInfoDAO.getUserInfo(username);
            }
            if (userData != null) {
                userData.setHeaderLogo(UserInfoDAO.getHeaderLogo(userData));
                userData.setMaxMB(UserInfoDAO.getMaxMB(userData.getAccountID()));
                UserInfoDAO.getCriticalPathData(userData); 

            } else {
                pageError = true;
            }
        }catch(Exception e){
            request.setAttribute("pageError","An error occured trying to log into control panel.");
            dispatcher.forward(request, response);
        }
    }
    String mainpageText = UserInfoDAO.getMainpageText(userData);
%>

<%
// preparse results of the transfer to emc and other specific pages that send back to here, if applicable
String otherResults = "";

try {
  if (request.getParameter("lastAction").equals("emcTransfer")) {
    int leadsUpdated = Integer.parseInt(request.getParameter("lead_upd"));
    int leadsCreated = Integer.parseInt(request.getParameter("lead_new"));
    int leadsErrored = Integer.parseInt(request.getParameter("lead_err"));
    otherResults = "You transferred " + (leadsUpdated+leadsCreated) + " leads. " + leadsUpdated + " updated, " + leadsCreated + " new.";
    if (leadsErrored > 0) {
        otherResults = otherResults + " " + leadsErrored + " leads were duplicates or could not be transferred to the Email Marketing Center.";
    }
  }
} catch (Exception e) {
  otherResults = "";
}
%>





    <%

        try
        {
            System.out.println("The domainName string is: " + domainName);
             //Get accountInfo based on domainName
            domainName = UserInfoDAO.removeHostRedirection(domainName);
            userData = UserInfoDAO.getUserInfo(domainName);
        }
        catch(Exception e)
        {
            System.out.println(e.getMessage());
            response.sendRedirect("../ErrorPageCPanel.jsp");
        }

        //for beta controlpanel, get the domain name from the form post (this is how it always works in old cp)
        if (userData == null) {
            userData = UserInfoDAO.getUserInfo(request.getParameter("username"));
        }

        if (userData!=null) {               
            //get HashMap from biz layer of leads/contacts
            int agentId = userData.getAgentID();
            int adverId = userData.getAdverID();
            //System.out.println("agentId:" + agentId);
            //System.out.println("adverId:" + adverId);
            LeadsList leads = null;
            HashMap currLead = null;

            String agentOrBroker = null;
            int agentOrAdverId = 0;
            if (adverId == 0) { //agent site
                agentOrBroker = "agent";
                agentOrAdverId = agentId;
            } 
            else {
                agentOrBroker = "broker";
                agentOrAdverId = adverId;
            }


            if (sOrderBy.equals("")) {
                leads = new LeadsList(agentOrBroker,agentOrAdverId);
            } 
            else {
                if (bOrderDesc) 
                {
                    String orderBy[] = sOrderBy.split(",");
                    String expandedOrderBy = orderBy[0] + " DESC";
                    for (int i = 1; i < orderBy.length; i++) 
                    {
                        expandedOrderBy += "," + orderBy[i] + "DESC";
                    }
                    leads = new LeadsList(agentOrBroker,agentOrAdverId,expandedOrderBy);
                } 
                else {
                    leads = new LeadsList(agentOrBroker,agentOrAdverId,sOrderBy);
                }
            }

            %>







<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Control Panel - Website &amp; Content</title>
<link rel="stylesheet" type="text/css" media="screen" href="/JAABA/skins/aaDefault/styles/cp-contentwell.css" />
</head>


<body>

        <jsp:include page="../Includes/controlPanel/sessionInfo.jsp" >
            <jsp:param name="username" value="<%=userData.getAcctName()%>" />
        </jsp:include>

        <!--This sets up the javascript array that holds the leads. -->
        <script language="JavaScript">

                int unViewedCt = 0;
                  out.write("var jsLeads = new Array();n");
                    for (int i=0; i< leads.size(); i++) {
                      currLead = (HashMap)leads.get(i);

                        out.write("jsLeads["+ i +"] = new Array();n");
                        out.write("jsLeads[" + i + "][0] = '" + currLead.get("PK_LEADID") + "';n");
                        out.write("jsLeads[" + i + "][1] = '" + currLead.get("CON_ID") + "';n");
                        out.write("jsLeads[" + i + "][2] = " + currLead.get("BO_VISIBLE") + ";n"); 
                        //could be null in the table but is NVL'd in the view
        //              Integer currVis =(Integer) currLead.get("BO_VISIBLE");
        //              System.out.println("currVis:" + currVis);
        //              out.write("jsLeads[" + i + "][2] = " + Utils.getStringNull((((Integer)currLead.get("BO_VISIBLE")).toString()),"0") + ";n");

                        if (new Integer(currLead.get("BO_VIEWED").toString()).intValue() == 0 ) {

                            unViewedCt++;
                        }
                    }
        </script>            




<div id="cWell" class="clearfix">

<h1>Welcome to your New Control Panel</h1>

<div id="cWellMain">

<%=mainpageText%>

<!-- START OLD SCHOOL NEWS BOX -->
<table cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td colspan="2" align="CENTER" bgcolor="#FFCF63"><font face="Arial,Helvetica,Sans-Serif" size="4"><b>Real Estate News</b></td></tr><tr><td><font face="Arial,Helvetica,Sans-Serif" size="3"><b>Consumer News</b></font><br>

<br><a href="news-sample.html" title="Consumers aren't being cocky when they expect to reap profits from the sale of their home, even in a down market. "><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><b>Consumers: "What Housing Bust?"</b></font><br>
<font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><i>JUN 25, 2007</i></font></a><br>
<br><a href="javascript:submit_form_with_qs('newsstory=222917');" title="How do you really measure square footage in a condominium unit? "><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><b>Housing Counsel: The Quagmire Called Square Footage</b></font><br><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><i>JUN 25, 2007</i></font></a><br>
<br><a href="javascript:submit_form_with_qs('newsstory=222919');" title="A study prepared for the Mortgage Bankers Association of America estimates that incidence of home loan fraud jumped by 30"><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><b>Mortgage Fraud Rising Fast; Florida and California Now the Hot Spots</b></font><br><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><i>JUN 25, 2007</i></font></a><br>
<br><a href="javascript:submit_form_with_qs('newspage=Consumer');"><font face="Arial,Helvetica,Sans-Serif" size="2" color="#993333"><b><i>More Consumer News</i></b></font></a><br>
</td><td><font face="Arial,Helvetica,Sans-Serif" size="3"><b>Commercial News</b></font><br>
<br><a href="javascript:submit_form_with_qs('newsstory=222920');" title="An entire team working for you -- even when you sleep? "><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><b>A Day In The Life Of A Virtually Supported Agent</b></font><br><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><i>JUN 25, 2007</i></font></a><br>
<br><a href="javascript:submit_form_with_qs('newsstory=222921');" title="Working from home can help real estate agents be more productive if they know how to make their surroundings function well"><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><b>Ten Ways To Work Real Estate More Effectively From Home</b></font><br><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><i>JUN 25, 2007</i></font></a><br>

<br><a href="javascript:submit_form_with_qs('newsstory=222822');" title="The California Association of Realtors (CAR) opposes private transfer taxes and has sought legislation that would prohibit them. "><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><b>Legislative Support for Private Transfer Taxes</b></font><br><font face="Arial,Helvetica,Sans-Serif" size="1" color="#993333"><i>JUN 22, 2007</i></font></a><br>
<br><a href="javascript:submit_form_with_qs('newspage=Commercial');"><font face="Arial,Helvetica,Sans-Serif" size="2" color="#993333"><b><i>More Commercial News</i></b></font></a><br>
  </td></tr></table>
<!-- END OLD SCHOOL NEWS BOX -->

</div><!-- end cWellMain -->


<div id="cWellSide">


<div id="stats">
    <h3><%= unViewedCt++ %></h3>
    <p>stats go here</p>
</div>

<div class="ad">
<div id="ad1"><p>To view product &amp; service opportunities from <strong>Agent Advantage</strong>, please install the <a href="http://www.adobe.com/go/gntray_dl_getflashplayer" target="_blank">Flash Plug-in</a>.</p></div>
</div><!-- end ad -->

<div class="ad">
<div id="ad2"><p>To view product &amp; service opportunities from <strong>Agent Advantage</strong>, please install the <a href="http://www.adobe.com/go/gntray_dl_getflashplayer" target="_blank">Flash Plug-in</a>.</p></div>
</div><!-- end ad -->

</div><!-- end cWellSide -->

</div><!-- END cWell -->
</body>
</html>

thanx….

Edited

10 Years Ago
by diafol because:

fixed formatting


Recommended Answers

I don’t think you’re in the right forum. It looks like you want this one. And when you need help it’s best to be specific about what problem you’re having. Just asking for suggestions and pasting reams of code won’t get you much. I’d probably be able to help …

Jump to Post

It’s no biggie. But I’m sure that there are a few arrogant people around here that love to flame anyone who makes an innocent mistake. Better me helping you out than them driving you away, right? :)

im thinking its somewhere between lines 163-185..

Don’t you get a line number …

Jump to Post

The same response I gave in the JSP forum for this question. Move all of this scriptlet stuff out of the JSP into one, or more, beans.

Jump to Post

All 9 Replies

Member Avatar

15 Years Ago

I don’t think you’re in the right forum. It looks like you want this one. And when you need help it’s best to be specific about what problem you’re having. Just asking for suggestions and pasting reams of code won’t get you much. I’d probably be able to help you, but I have no idea what you want… :(

Member Avatar


mimsc

0



Junior Poster in Training


15 Years Ago

my bad…Im getting a «catch without try»..and all my brackets seem to be lined up…im thinking its somewhere between lines 163-185..not sure whats wrong though…I’ll try posting in the other spot…sorry bout that

Member Avatar

15 Years Ago

It’s no biggie. But I’m sure that there are a few arrogant people around here that love to flame anyone who makes an innocent mistake. Better me helping you out than them driving you away, right? :)

im thinking its somewhere between lines 163-185..

Don’t you get a line number with the error? Can you point it out? Lines 163-185 don’t contain a catch at all. This is a syntax error so it should be pretty easy to locate with help from the compiler.

Member Avatar


mimsc

0



Junior Poster in Training


15 Years Ago

unfortunately this is what I get:

org.apache.jasper.JasperException: PWC6033: Unable to compile class for JSP

PWC6199: Generated servlet error:
string:///cp_002dfrontpage_jsp.java:300: ‘catch’ without ‘try’

Member Avatar

15 Years Ago

It says line 300. That’s probably right where the offending catch is and you can look back up to find the matching try. I’d help, but I don’t have a way of matching the error’s line 300 to the line numbers in the code you posted.

Member Avatar


masijade

1,351



Industrious Poster



Team Colleague



Featured Poster


15 Years Ago

The same response I gave in the JSP forum for this question. Move all of this scriptlet stuff out of the JSP into one, or more, beans.

Member Avatar


mimsc

0



Junior Poster in Training


15 Years Ago

appreciate it fellas…..

Member Avatar

12 Years Ago

your problem is the catch (Exception e) statement that catches your 1st try..
it is called as the Unreachable code problem.

catch (Exception e) is a method of the exception class which is the supreclass to all exceptions and handles all exceptions. so this method shold come as last catch….
if it comes b4 other catches other catches will be ignored as catch (Exception e) statement handles all exceptions… so you get that error

it is called as the Unreachable code problem.


for more details on what i mean check this link

http://www.tutorialhero.com/tutorial-73-catch_multiple_exceptions_in_java.php

Member Avatar


masijade

1,351



Industrious Poster



Team Colleague



Featured Poster


12 Years Ago

And if those tutorials advocate widespread use of scriptlets they are absolutely worthless. Advocating practices that lead to unscaleable and nearly unmaintainable code is not acceptable.

Edit: Not to mention that this thread is two years dead. Killing this zombie now.

Edited

12 Years Ago
by masijade because:

n/a


Reply to this topic

Be a part of the DaniWeb community

We’re a friendly, industry-focused community of developers, IT pros, digital marketers,
and technology enthusiasts meeting, networking, learning, and sharing knowledge.

In this post, we will see about can we have try without catch block in java.

Table of Contents

  • What happens when you have return statement in try block:
  • What happens if you have return statement in finally block too
  • What if exception is thrown in try block

Yes,  we can have try without catch block by using finally block.

You can use try with finally. As you know finally block always executes even if you have exception or return statement in try block except in case of System.exit().

Lets understand with the help of example.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

package org.arpit.java2blog;

public class TryWithoutCatchMain {

public static void main(String args[])

{

  try

  {

   System.out.println(«Executing try block»);

  }

  finally

  {

   System.out.println(«Executing finally block»);

  }

}

}

When you execute above program, you will get following output:

Executing try block

Executing finally block

What happens when you have return statement in try block:

If you have return statement in try block, still finally block executes.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

package org.arpit.java2blog;

public class TryWithoutCatchMain {

public static void main(String args[])

{

  System.out.println(print());

}

public static String print()

{

  try

  {

   System.out.println(«Executing try block»);

   return «Return from try block»;

  }

  finally

  {

   System.out.println(«Executing finally block»);

  }

}

}

When you execute above program, you will get following output:

Executing try block

Executing finally block

Return from try block

What happens if you have return statement in finally block too

It overrides whatever is returned by try block. Lets understand with the help of example:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

package org.arpit.java2blog;

public class TryWithoutCatchMain {

public static void main(String args[])

{

  System.out.println(print());

}

public static String print()

{

  try

  {

   System.out.println(«Executing try block»);

   return «Return from try block»;

  }

  finally

  {

   System.out.println(«Executing finally block»);

   return «Return from finally block»;

  }

}

}

When you execute above program, you will get following output:

Executing try block

Executing finally block

Return from finally block

What if exception is thrown in try block

If exception is thrown in try block, still finally block executes.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

package org.arpit.java2blog;

public class TryWithoutCatchMain {

public static void main(String args[])

{

  System.out.println(print());

}

public static int  print()

{

  try

  {

   throw new NullPointerException();

  }

  finally

  {

   System.out.println(«Executing finally block»);

  }

}

}

When you execute above program, you will get following output:

Executing finally block

Exception in thread «main» java.lang.NullPointerException

at org.arpit.java2blog.TryWithoutCatchMain.print(TryWithoutCatchMain.java:14)

at org.arpit.java2blog.TryWithoutCatchMain.main(TryWithoutCatchMain.java:7)

As you can see that even if code threw NullPointerException, still finally block got executed.
You can go through top 50 core java interview questions for more such questions.

Понравилась статья? Поделить с друзьями:

Читайте также:

  • Error cat value
  • Error cat pet simulator
  • Error case open warning
  • Error case label not within a switch statement
  • Error case label does not reduce to an integer constant

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии