There is no difference in behaviour between your two code samples. (In particular, stacktraces are recorded when an exception is created, not when it is thrown, so a rethrown exception will still have the original stack trace). Usually, people therefore use the simpler idiom.
That is not to say rethrowing doesn't have its uses. For instance, if you wanted to handle all exceptions except FooBarExceptions, you could write:
try {
// bad code
} catch (FooBarException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
}
Or if the decision to handle an exception is more involved than simply checking its type, you can simply catch it, and rethrow if it turns out you can't handle it:
for (int attempts = 0; attemps < 6; attempts++) {
try {
return crankyMethod();
} catch (Exception e) {
if (fatal(e)) {
throw e;
} else {
// try again
continue;
}
}
}
It is worth noting that when people say rethrow, some mean to throw a different exception, as in the following example:
for (int i = 0; i < array.length; i++) {
try {
process(array[i]);
} catch (Exception e) {
throw new RuntimeException("Could not process element at index " + i, e);
}
}
The advantage of this pattern is to decorate the original exception with additional information that might be relevant (in the above example: what data could not be processed). Note that the original exception is passed to the constructor of the new one, so its stack trace is not lost.