前言
对于编译性异常我们必须捕获或者抛出 ,否则程序无法编译成功,对于非编译性异常(运行时异常),这类异常可以编译成功但在运行的时候可能就会发生异常,常见的运行时异常主要有:ClassCastException、IndexOutOfBoundsException、NullPointerException、ArrayStoreException
等,这类异常一般是程序逻辑错误引起的,在编写代码的时候应该规避这类异常的发生。
相关问题
1:如果当前代码块存在异常,应该采用抛出的方式or捕获方式?
2:针对潜在的运行时异常是否应该对其做处理?
判读采用抛出异常or捕获处理异常?
- 1:抛出异常VS捕获异常
抛出异常:抛出异常后当前方法无法继续下去,会从当前方法跳出来,然后把问题交给上一级处理,通过throws关键字抛出异常
捕获异常:可以对异常进行处理或者继续抛出,通过try-catch关键字进行异常捕获,异常在catch中被处理后,可以继续向下执行。也可以在catch中通过throw方法继续向外抛出异常,此时不能继续向下执行。
- 2:异常处理的三个原则:具体明确、及早抛出、延迟处理
具体明确:抛出与捕获异常的时候尽量明确异常的类型,这样才能根据不同的异常,提供不同的解决方案。
@SuppressWarnings("resource")
public void readFile(String fileName){
if(fileName==null){
throw new IllegalArgumentException("fileName must not be null");
}
try {
InputStream is= new FileInputStream(fileName);
is.read();
is.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
//处理一
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
//处理二
}
}
提早抛出:有些比较明确的异常需要提前抛出,这样我们可以通过控制台打印的异常信息快速定位到出错的代码块,同时也可以避免不必要的对象构造或者资源调用。如下分别在调用两个方法的时候fileName参数都传递为null,显然采用代码二的方式更有利排查错误。
代码一:
@SuppressWarnings("resource")
public void readFile(String fileName) throws IOException {
InputStream is=new FileInputStream(fileName);
is.read();
is.close();
}
Exception in thread "main" java.lang.NullPointerException
at java.io.FileInputStream.<init>(FileInputStream.java:130)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at com.javaweb.test.ExceptionTest.readFile(ExceptionTest.java:27)
at com.javaweb.test.ExceptionTest.main(ExceptionTest.java:17)
代码二:
@SuppressWarnings("resource")
public void readFile(String fileName) throws IOException {
if(fileName==null){
throw new IllegalArgumentException("fileName must not be null");
}
InputStream is=new FileInputStream(fileName);
is.read();
is.close();
}
Exception in thread "main" java.lang.IllegalArgumentException: fileName must not be null
at com.javaweb.test.ExceptionTest.readFile(ExceptionTest.java:27)
at com.javaweb.test.ExceptionTest.main(ExceptionTest.java:17)
延迟捕获:让异常捕获处理放在最外层进行处理,这样才能根据不同的情况进行不同的处理。如下面代码:
@SuppressWarnings("resource")
public void readFile(String fileName) {
if(fileName==null){
throw new IllegalArgumentException("fileName must not be null");
}
InputStream is;
try {
is = new FileInputStream(fileName);
is.read();
is.close();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
在readFile这个方法中处理异常的方法并没有直接抛出而是捕获处理,看起来可能没有什么错误。但在上层引用中,不同的业务逻辑对待处理异常的方式可能不同,所以对于底层方法出现的异常需要进行抛出。
针对潜在的运行时异常是否应该对其做处理?
代码一:
@SuppressWarnings("resource")
public void readFile(String fileName) throws IOException {
if(fileName==null){
throw new IllegalArgumentException("fileName must not be null");
}
InputStream is=new FileInputStream(fileName);
is.read();
is.close();
}
代码二:
public int divide(int a ,int b){
int result=0;
if(b!=0){
result=a/b;
}
return result;
}
通过对比代码一与代码二,可以总结一些规律。针对存在潜在运行时异常的代码块,如果能通过逻辑处理从而避免抛出异常,那么就对代码进行逻辑处理;如果无法进行逻辑处理从而避免抛出异常,那么就及时抛出异常。
catch中通过throw继续抛出异常,此时throw抛出的异常如何被接收?
@Override
public void add(Category bean) {
// TODO Auto-generated method stub
try {
QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());
String sql="INSERT INTO category (id,name,description) VALUES(?,?,?)";
Object[] params={bean.getId(),bean.getName(),bean.getDescription()};
qr.update(sql, params);
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new RuntimeException(e);
}
}
如果上层方法直接或者间接调用这个add(),那么我们可以通过在上层方法中使用try-catch捕获这个异常