What is the difference between throw e and throw new Exception(e)?

10,511

Solution 1

If you don't need to adjust the exception type, you rethrow (throw further) the same instance without any changes:

catch (IOException e) {
    throw e;
}

If you do need to adjust the exception type, you wrap e (as a cause) into a new exception of the type required.

catch (IOException e) {
    throw new IllegalArgumentException(e);
}

I consider all other scenarios a code smell. Your second snippet is a good example of it.


Here are answers to the questions that might pop up.

Why would I want to rethrow an exception?

You can let it go. But if it happens, you won't be able to do anything at this level.

When we catch an exception in a method, we are still in that method and have access to its scope (e.g. local variables and their state). Before we rethrow the exception, we can do whatever we need to (e.g. log a message, send it somewhere, make a snapshot of the current state).

Why would I want to adjust an exception?

As a rule of thumb,

Higher layers should catch lower-level exceptions and, in their place, throw exceptions that can be explained in terms of the higher-level abstraction.

Effective Java - 2nd Edition - Item 61: Throw exceptions appropriate to the abstraction

In other words, at some point, an obscure IOException should be transformed into a perspicuous MySpecificBusinessRuleException.

I called it "adjusting the exception type", smart guys call it exception translation (exception chaining, in particular).


To make it clear, let's have some foolish examples.

class StupidExample1 {
    public static void main(String[] args) throws IOException {
        try {
            throw new IOException();
        } catch (IOException e) {
            throw new IOException(new IOException(e));
        }
    }
}

results in a verbose stack trace like

Exception in thread "main" java.io.IOException: java.io.IOException: java.io.IOException
    at StupidExample1.main(XXX.java:XX)
Caused by: java.io.IOException: java.io.IOException
    ... 1 more
Caused by: java.io.IOException
    at StupidExample1.main(XXX.java:XX)

which can (and should) be effectively reduced to

Exception in thread "main" java.io.IOException
    at StupidExample1.main(XXX.java:XX)

Another one:

class StupidExample2 {
    public static void main(String[] args) {
        takeString(new String(new String("myString")));
    }

    static void takeString(String s) { }
}

It's obvious that new String(new String("myString")) is a wordy version of "myString" and should be refactored to the latter.

Solution 2

catch (IOException e) {
    throw e;
}

You will see the original exception with the original stacktrace only. You won't see this "rethrow" line in the stacktrace so it's kind of transparent.

catch (IOException e) {
    throw new IllegalStateException(e);
}

You will see created IllegalStateException and its stacktrace with "caused by" original exception information and stacktrace. You are setting (about to be) the thrown exception as the cause of the newly created IOException. The upper layer will see IllegalStateException and that will be possible to catch (you won't catch that catching cause exception).

catch (IOException e) {
     throw new IOException();
}

You will see only the current stacktrace of the IOException creation, no the cause added.

Solution 3

Well, basically, throw e will "rethrow" (pass) all original values (because the same Exception instance, same object is will be used)- that will cause, it will pass also some code-flow- which maybe should be hidden, for example due the security reasons.

If you will re-create the exception, you will get - or better to say "you can get" - another stacktrace in the place using the new Exception instance.

So, I would say, in this case , you have option to mask some data (Eg. you will log exceptions into a special log, but you will want to pass other diagnostic data to the end-user).

Let's check the following a bit to the details:

  • I created one class as just only simple generator of Exceptions
  • another class allows to rethrow or recreate exception
  • afterwards, I am just printing the stacktrace and comparing results

Exception generator

public class ExceptionsThrow {
    public static void throwNewException() throws Exception {
        throw new Exception("originally thrown message");
    }
}

Class for rethrow/ recreate exceptions

  public class Exceptions {

        public static void reThrowException() throws Exception {
            try {
                ExceptionsThrow.throwNewException();
            } catch (Exception e) {
                //re-throw existing exception
                throw e;
            }
        }

        public static void reCreateNewException() throws Exception {
            try {
                ExceptionsThrow.throwNewException();
            } catch (Exception e) {
                //recreation of the exception > new instance is thrown
                throw new Exception(e);
            }
        }
    }

Testing code example:

try {
     Exceptions.reThrowException();
} catch (Exception e) {
    System.out.println("1st RETHROW");
    e.printStackTrace();
    System.out.println("===========");
}

try {
    Exceptions.reCreateNewException();
} catch (Exception e) {
    System.out.println("2nd RECREATE");
    e.printStackTrace();
    System.out.println("===========");
}

And the output finally:

1st RETHROW
java.lang.Exception: originally thrown message
    at test.main.stackoverflow.ExceptionsThrow.throwNewException(ExceptionsThrow.java:5)
    at test.main.stackoverflow.Exceptions.reThrowException(Exceptions.java:7)
    at test.main.MainTest.main(MainTest.java:110)
java.lang.Exception: java.lang.Exception: originally thrown message===========
2nd RECREATE

    at test.main.stackoverflow.Exceptions.reCreateNewException(Exceptions.java:17)
    at test.main.MainTest.main(MainTest.java:118)
Caused by: java.lang.Exception: originally thrown message
    at test.main.stackoverflow.ExceptionsThrow.throwNewException(ExceptionsThrow.java:5)
    at test.main.stackoverflow.Exceptions.reCreateNewException(Exceptions.java:15)
    ... 1 more
===========

In this case you can see mostly the same data, but also some additional, you can see the original message, because I have used the same Exception to built the new one - but its not necessary to create new Exception instance as this, so then you can mask original cause, or you don't need to expose the logic of the application, for instance let’s check one more example:

  • I will take only the cause from the original exception, but it will override the data
  • as you can see, a newly created exception doesn't contain the full stacktrace, as the origin

So:

public static void maskException() throws Exception {
    try {
        ExceptionsThrow.throwNewException();
    } catch (Exception e) {
        throw new Exception("I will dont tell you",e.getCause());
    }
}

And the result:

===========
3rd mask
java.lang.Exception: I will don't tell you
    at test.main.stackoverflow.Exceptions.maskException(Exceptions.java:25)
    at test.main.MainTest.main(MainTest.java:126)
===========

So, I would say, recreate the exception with the same instance is a little bit pointless, but there can be cases once Exception transformation is needed (retype to another Exception type (then actually you should be also able to use java casting), or you want mask data

In the real world, I can remember the really often issue, when some web portals, handled exceptions in PHP scripts only by this case of printing, and then usually, when the connection with the database was not working correctly, connection strings (including database address and credentials in plaintext) were visible in the web browser, for example. :)

Solution 4

This example doesn't make much sense in this context, because you're throwing the same exception and not doing anything else. Logging it at least will make much more sense. You're catching an exception to handle it or log it. If you cannot handle it, rethrow it (case 1) or wrap to something else (case 2).

Case 1:

public class Main {

    // forced to handle or rethrow again
    public static void main(String[] args) throws IOException {
        read();
    }

    public static void read() throws IOException {
        try {
            readInternal();
        } catch (IOException e) {
            throw e;
        }
    }

    private static void readInternal() throws IOException {
        throw new IOException("Output error");
    }
}

In the output you'll see something like:

Exception in thread "main" java.io.IOException: Output error
    at com.alex.java.Main.readInternal(Main.java:26)
    at com.alex.java.Main.read(Main.java:19)
    at com.alex.java.Main.main(Main.java:14)
**Case 2:**

The pattern below allows you to change the type of the exception and keep the original exception details as well:

try {
   // Some code here
} catch (IOException e) {
    throw new IllegalStateException(e);
}

This case often happens when you would like to substitute a Checked Exception with an Unchecked exception preserving the origination of the issue and keep all the information (what called exception chaining).

Regular use-cases:

  • You cannot handle a Checked Exception and you don't want to rethrow it to the caller. Rethrowing checked exceptions will force the caller to handle it. This is not what you want to do if there are no regular cases for recovery.
  • Exceptions like IOException are rarely useful to the client. You need to send something more specific and clear in the scope of your business domain.

IOException wrapped to Unchecked Exception like DocumentReadException will shed light on the actual situation and will not force the callers to handle it:

public class Main {

    public static void main(String[] args) {
        read();
    }

    public static void read() {
        try {
            readInternal();
        } catch (IOException e) {
            // log and wrap the exception to a specific business exception
            logger.error("Error reading the document", e);
            throw new DocumentReadException(e);
        }
    }

    private static void readInternal() throws IOException {
        throw new IOException("Output error");
    }
}

The output will be similar to:

Exception in thread "main" java.lang.IllegalArgumentException: Error reading the document
    at com.alex.java.Main.read(Main.java:21)
    at com.alex.java.Main.main(Main.java:14)
Caused by: java.io.IOException: Output error
    at com.alex.java.Main.readInternal(Main.java:26)
    at com.alex.java.Main.read(Main.java:19)
    ... 1 more

As you can see from the stack trace, the root cause was a logger to help you find out the original problem and the business domain exception was sent to a user.

Solution 5

When you throw an exception, you are doing something very similar to creating or declaring an instance.

throw e declares an instance of the IOException (assigns a memory space on the device without storing data), so you are rethrowing it on the catch block as you are declaring a pre-made exception.

throw new IOException e initalizes a new instance of the IOException, so you are creating a new exception on the catch block as it does not reference other exception.

However

You don't usually throw exceptions on the catch block. In most cases you would:

  1. Warn the compiler that you are going to throw an exception on a method (throws IOException after the parameter declaration)
  2. Throw the exception (throw new IOException("Warning text") on the body of the method) whenever the data which is introduced cannot be validated anyway.
  3. On the try/catch blocks, put the summoning or calling to the method on the try block; on the catch block, put an error message.

An example code would look like this:

public static void myMethod() throws IOException{
int prueba=0;
if(prueba>9 //some condition){
    //do a thing
}else
    throw new IOException("This number is not correct");
}//end of method
try{
    myClass.myMethod();
}catch{
    System.out.println("An error has occurred");
}//end of try/catch

This is the way I've been learning to do exceptions properly, but I hope I answered your question.

Share:
10,511
Vinaya Nayak
Author by

Vinaya Nayak

Updated on June 29, 2022

Comments

  • Vinaya Nayak
    Vinaya Nayak almost 2 years

    Consider:

    try  {
        // Some code here
    } catch (IOException e) {
        throw e;
    } catch (Exception e) {
        throw e;
    }
    

    What is the difference between throw e and throw new Exception(e)?

    try  {
       // Some code here
    } catch (IOException e) {
       throw new IOException(e);
    } catch (Exception e) {
       throw new Exception(e);
    }
    
  • Tas
    Tas about 5 years
    When something is referred to as "transparent" it means it's very clear; however, in your example it appears that since the stack trace would omit the re-throw, it's actually more vague or muddy. Is that correct?
  • Paŭlo Ebermann
    Paŭlo Ebermann about 5 years
    @Tas this is transparent as in "invisible".
  • Phantom Lord
    Phantom Lord about 3 years
    In internet this is the best answer