Difference Between Throw and Throws in Java – Java中Throw和Throws的区别

最后修改: 2018年 8月 23日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

In this tutorial, we’ll take a look at the throw and throws in Java. We’ll explain when we should use each of them.

在本教程中,我们将看一下Java中的throwthrows。我们将解释何时应该使用它们中的每一个。

Next, we’ll show some examples of their basic usage.

接下来,我们将展示一些它们的基本用法的例子。

2. Throw and Throws

2.投掷投掷

Let’s start with a quick introduction. These keywords are related to exception-handling. Exceptions are raised when the normal of flow of our application is disrupted.

让我们先简单介绍一下。这些关键词都与异常处理有关。当我们的应用程序的正常流程被破坏时,就会产生异常。

There may be a lot of reasons. A user could send the wrong input data. We can lose a connection or other unexpected situation may occur. Good exceptions handling is a key to keep our application working after an appearance of those unpleasant moments.

可能有很多原因。一个用户可能发送了错误的输入数据。我们可能会失去一个连接或发生其他意外情况。良好的异常处理是在这些不愉快的时刻出现后保持我们的应用程序工作的关键。

We use throw keyword to explicitly throw an exception from the code. It may be any method or static block. This exception must be a subclass of Throwable. Also, it can be a Throwable itself. We can’t throw multiple exceptions with a single throw.

我们使用 throw关键字来明确地从代码中抛出一个异常。它可以是任何方法或静态块。这个异常必须是Throwable的一个子类。此外,它也可以是一个Throwable本身。我们不能用一个throw抛出多个异常。

Throws keyword can be placed in the method declaration. It denotes which exceptions can be thrown from this method. We must handle these exceptions with try-catch.

Throws关键字可以放在方法声明中。它表示哪些异常可以从这个方法中抛出。我们必须用try-catch来处理这些异常。

These two keywords aren’t interchangeable!

这两个关键词是不能互换的!

3. Throw in Java

3.抛出在Java中的应用

Let’s take a look at a basic example with throwing an exception from the method.

让我们看一下一个基本的例子,从方法中抛出一个异常。

First of all, imagine that we’re writing a simple calculator. One of the basic arithmetic operations is division. Due to that, we were asked to implement this feature:

首先,想象一下,我们正在编写一个简单的计算器。其中一个基本的算术操作是除法。由于这个原因,我们被要求实现这个功能。

public double divide(double a, double b) {
    return a / b;
}

Because we can’t divide by zero, we need to add some modifications to our existing code. Seems like it’s a good moment for raising an exception.

因为我们不能除以0,我们需要对现有的代码进行一些修改。似乎这是个引发异常的好时机。

Let’s do this:

让我们来做这个。

public double divide(double a, double b) {
    if (b == 0) {
        throw new ArithmeticException("Divider cannot be equal to zero!");
    }
    return a / b;
}

As you can see, we have used ArithmeticException with perfectly fits our needs. We can pass a single String constructor parameter which is exception message.

正如你所看到的,我们使用了ArithmeticException,完全符合我们的需求。我们可以传递一个单一的String构造参数,也就是异常信息。

3.1. Good Practices

3.1.良好做法

We should always prefer the most specific exception. We need to find a class that fits the best for our exceptional event. For example, throw NumberFormatException instead of IllegalArgumentException. We should avoid throwing an unspecific Exception.

我们应该总是选择最特殊的异常。我们需要找到一个最适合我们的特殊事件的类。例如,抛出NumberFormatException,而不是IllegalArgumentException。我们应该避免抛出一个不具体的Exception

For example, there is an Integer class in java.lang package. Let’s take a look at the one of the factory method declaration:

例如,在java.lang包中有一个Integer类。让我们来看看其中的工厂方法声明。

public static Integer valueOf(String s) throws NumberFormatException

It’s a static factory method which creates Integer instance from String. In case of wrong input String, the method will throw NumberFormatException.

这是一个静态的工厂方法,它从字符串创建Integer实例。如果输入错误的String,该方法将抛出NumberFormatException.

A good idea is to define our own, more descriptive exception. In our Calculator class that could be for example DivideByZeroException. 

一个好主意是定义我们自己的、更具描述性的异常。在我们的计算器类中,可以是DivideByZeroException。

Let’s take a look at sample implementation:

让我们来看看实施样本。

public class DivideByZeroException extends RuntimeException {

    public DivideByZeroException(String message) {
        super(message);
    }
}

3.2. Wrapping an Existing Exception

3.2.包裹一个现有的异常

Sometimes we want to wrap an existing exception into the exception defined by us.

有时我们想把一个现有的异常包裹到我们定义的异常中。

Let’s start with defining our own exception:

让我们从定义我们自己的例外开始。

public class DataAcessException extends RuntimeException {
    
    public DataAcessException(String message, Throwable cause) {
        super(message, cause);
    }
}

The constructor takes two parameters: exception message, and a cause, which may be any subclass of Throwable. 

构造函数需要两个参数:异常信息和原因,它可以是任何Throwable的子类。

Let’s write a fake implementation for findAll() function:

让我们为findAll()函数写一个假实现。

public List<String> findAll() throws SQLException {
    throw new SQLException();
}

Now, in SimpleService let’s call a repository function, which can result in SQLException:

现在,在SimpleService中让我们调用一个存储库函数,这可能导致SQLException:

public void wrappingException() {
    try {
        personRepository.findAll();
    } catch (SQLException e) {
        throw new DataAccessException("SQL Exception", e);
    }
}

We are re-throwing SQLException wrapped into our own exception called DataAccessException. Everything is verified by the following test:

我们正在重新抛出SQLException包装成我们自己的异常,称为DataAccessException。一切都由下面的测试来验证。

@Test
void whenSQLExceptionIsThrown_thenShouldBeRethrownWithWrappedException() {
    assertThrows(DataAccessException.class,
      () -> simpleService.wrappingException());
}

There are two reasons to do this. First of all, we use exception wrapping, because the rest of the code does not need to to know about every possible exception in the system.

这样做有两个原因。首先,我们使用异常包装,因为代码的其他部分不需要知道系统中每一个可能的异常。

Also higher level components do not need to know about bottom level components, nor the exceptions they throw.

另外,高层组件不需要知道底层组件的情况,也不需要知道它们抛出的异常。

3.3. Multi-Catch with Java

3.3.用Java进行多重抓取

Sometimes, the methods that we use can throw many of different exceptions.

有时,我们使用的方法可以抛出许多不同的异常。

Let’s take a look at more extensive try-catch block:

让我们来看看更广泛的try-catch块。

try {
    tryCatch.execute();
} catch (ConnectionException | SocketException ex) {
    System.out.println("IOException");
} catch (Exception ex) {
    System.out.println("General exception");
}

The execute method can throw three exceptions: SocketException, ConnectionException, Exception. The first catch block will catch ConnectionException or SocketException. The second catch block would catch Exception or any other subclass of Exception. Remember, that we should always catch a more detailed exception first.

execute方法可以抛出三种异常。SocketException, ConnectionException, Exception. 第一个捕捉块将捕捉ConnectionExceptionSocketException。第二个捕获块将捕获Exception或任何其他Exception的子类。记住,我们总是应该先捕获一个更详细的异常。

We can swap the order of our catch blocks. Then, we’d never catch SocketException and ConnectionException because everything will go to the catch with Exception.

我们可以调换捕获块的顺序。这样,我们就不会抓到SocketExceptionConnectionException,因为所有的东西都会被抓到Exception中。

4. Throws in Java

4.在Java中抛出

We add throws to the method declaration.

我们在方法声明中加入throws

Let’s take a look at one of our previous method declaration:

让我们来看看我们之前的一个方法声明。

public static void execute() throws SocketException, ConnectionException, Exception

The method may throw multiple exceptions. They are comma-separated at the end of a method declaration. We can put both, checked and unchecked exceptions in the throws. We have described the difference between them below.

该方法可以抛出多个异常。它们在方法声明的末尾以逗号分隔。我们可以在throws中同时放入已检查和未检查的异常。我们在下面描述了它们之间的区别。

4.1. Checked and Unchecked Exceptions

4.1.已检查和未检查的例外情况

A checked exception means that it’s checked at the compile time. Note, that we must handle this exception. Otherwise, a method must specify an exception by using throws keyword.

一个被检查的异常意味着它在编译时被检查。注意,我们必须处理这个异常。否则,一个方法必须通过使用throws关键字指定一个异常。

The most common checked exceptions are IOException, FileNotFoundException, ParseException. FileNotFoundException may be thrown when we create FileInputStream from File. 

最常见的检查异常是IOException, FileNotFoundException, ParseException。当我们从文件中创建FileInputStream时,可能会抛出FileNotFoundException

There’s a short example:

有一个简短的例子。

File file = new File("not_existing_file.txt");
try {
    FileInputStream stream = new FileInputStream(file);
} catch (FileNotFoundException e) {
    e.printStackTrace();
}

We can avoid using try-catch block by adding throws to the method declaration:

我们可以通过在方法声明中添加throws 来避免使用try-catch块。

private static void uncheckedException() throws FileNotFoundException {
    File file = new File("not_existing_file.txt");
    FileInputStream stream = new FileInputStream(file);
}

Unfortunately, a higher level function still has to handle this exception. Otherwise, we have to put this exception in method declaration with throws keyword.

不幸的是,一个更高级别的函数仍然必须处理这个异常。否则,我们必须在方法声明中用throws关键字将这个异常写入。

As the opposite, unchecked exceptions aren’t checked at the compile time. 

与此相反,未检查的异常在编译时不被检查。

The most common unchecked exceptions are: ArrayIndexOutOfBoundsException, IllegalArgumentException, NullPointerException. 

最常见的未检查的异常是。ArrayIndexOutOfBoundsException, IllegalArgumentException, NullPointerException。

Unchecked exceptions are thrown during runtime. The following code will throw a NullPointerException. Probably it’s one of the most common exceptions in Java.

未经检查的异常在运行时被抛出。下面的代码将抛出一个NullPointerException。这可能是Java中最常见的异常之一。

Calling a method on a null reference will result in this exception:

在一个空引用上调用一个方法将导致这个异常。

public void runtimeNullPointerException() {
    String a = null;
    a.length();
}

Let’s verify this behavior in the test:

让我们在测试中验证这一行为。

@Test
void whenCalled_thenNullPointerExceptionIsThrown() {
    assertThrows(NullPointerException.class,
      () -> simpleService.runtimeNullPointerException());
}

Please remember that this code and test does not many any sense. It’s only for learning purposes to explain runtime exceptions.

请记住,这段代码和测试并没有什么意义。它只是为了学习解释运行时的异常。

In Java, every subclass of Error and RuntimeException is an unchecked exception. A checked exception is everything else under the Throwable class.

在Java中,ErrorRuntimeException的每个子类都是一个未检查的异常。被检查的异常是Throwable类下的其他一切。

5. Conclusion

5.总结

In this article, we’ve discussed the difference between two Java keywords: throw and throws. We’ve gone through the basic usage and talked a little about good practicesThen we’ve talked about checked and unchecked exceptions.

在这篇文章中,我们已经讨论了两个Java关键字的区别。throwthrows。我们浏览了基本的用法,并谈到了一些良好的做法然后我们谈到了有检查和无检查的异常。

As always, the source code can be found on our GitHub.

一如既往,源代码可以在我们的GitHub上找到

If you want to go deeper into Exception handling in Java, please take a look at our article about Java exceptions.

如果你想深入了解Java中的异常处理,请看看我们的文章关于Java异常