两种语法介绍
我们以前使用的try-catch格式是这样的
格式如下:
// 申请资源对象需要外置定义
try{
//1. 申请资源和要执行的代码逻辑
}catch(IOException e){
//2. 捕获到的异常
e.printStackTrace();
}finally{
//3. 关闭资源,无论如何在最后都会执行到的代码
}
例子:
InputStream in=null;
OutputStream out=null;
try{
in=new FileInputStream("D:\\demo1.txt");
out=new FileOutputStream("D:\\demo2.txt");
byte[] buf=new byte[1024];
int len=0;
while((len=fis.read(buf))!=-1){
System.out.println(new String(buf,0,len));
out.write(buf,0,len);
}
}catch(Exception e){
e.printStackTrace();
}finally{
try{
if(in!=null){
in.close();
}
if(out!=null){
out.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
现在有jdk1.7新特性的书写方式:try-with-resource,可以实现自动关流操作
格式如下:
try(
//1.对资源的申请
InputStream in=new FileInputStream("D:\\demo1.txt");
OutputStream out=new FileOutputStream("D:\\demo2.txt");
){
//2.执行的代码
}catch(Exception e){
//3.捕获到的异常
e.printStackTrace();
}
注:
1. 无论在 2 处执行时是否出现异常(如果出现异常被 3 捕获)1 处都会自动关流。
2. 关流的顺序是按照创建资源的逆顺序自动关闭。
3. 这样就不用再写一堆finally中的代码了,这在InputStream和OutputStream中很好用。
4. 只要实现了AutoCloseable接口的流都可以实现这种操作。
public abstract class InputStream implements Closeable {
public interface Closeable extends AutoCloseable {
//@since 1.7
public interface AutoCloseable {
例子:
try(
InputStream in=new FileInputStream("D:\\demo1.txt");
OutputStream out=new FileOutputStream("D:\\demo2.txt");
){
byte[] buf=new byte[1024];
int len=0;
while((len=in.read(buf))!=-1){
System.out.println(new String(buf,0,len));
out.write(buf,0,len);
}
}catch(Exception e){
e.printStackTrace();
}
执行顺序:
// 申请资源对象需要外置定义
int a=0;
try{
//1. 申请资源和要执行的代码逻辑
//a=1/0;
a=6;
return a;
}catch(Exception e){
//2. 捕获到的异常
e.printStackTrace();
a=7;
}finally{
//3. 关闭资源,无论如何在最后都会执行到的代码
return a=8;
}
注:
- 没有出现异常时:1->3
其实是在1中return时会将a设置一个标记值6,然后向后检查是否有finally,然后执行finally中的逻辑,将a设置为8,最后在3中返回a=8;
详解:
在不抛出异常的情况下,程序执行完 try 里面的代码块之后,该方法并不会立即结束,而是继续试图去寻找该方法有没有 finally 的代码块,
如果没有 finally 代码块,整个方法在执行完 try 代码块后返回相应的值来结束整个方法;
如果有 finally 代码块,此时程序执行到 try 代码块里的 return 语句之时并不会立即执行 return,而是先去执行 finally 代码块里的代码,
若 finally 代码块里没有 return 或没有能够终止程序的代码,程序将在执行完 finally 代码块代码之后再返回 try 代码块执行 return 语句来结束整个方法;
若 finally 代码块里有 return 或含有能够终止程序的代码,方法将在执行完 finally 之后被结束,不再跳回 try 代码块执行 return。 - 出现异常时:1->2->3。
try-catch中的异常处理
异常发生的原因很多,通常有以下几类
- 用户输入了非法数据。
- 网络通信中断,JVM内存溢出。
- 要打开的文件不存在。
在try-catch代码块放在异常可能发生的地方,在里面的代码称为保护代码
一个try代码块后跟多个catch代码块,叫多重捕获。
如果一个方法捕获一个检查性异常,那么该方法必须使用throws关键字在方法签名的尾部来声明。
也可以使用throw关键字抛出一个异常,无论是新实例化还是刚捕获到的。
例如:
public static void main(String[] args) throws Exception{
try{
int s=test01();
System.out.println(s);
}catch(Exception e){
e.printStackTrace();
}
}
public static int test01() throws Exception{
int s= 0;
try{
s=1/0;
}catch(Exception e){
//新异常实例被捕获
throw new Exception(e.getMessage());
}
return s;
}
如何处理异常
java提供了抛出异常捕获异常的逻辑用于处理异常,当程序抛出异常时,如果不存在捕获异常逻辑,正在执行的方法将在异常处停止执行,并将该异常向外抛出,调用该方法的程序进行相同的处理方式,然后一层一层的向外抛出,直到到达当前程序处时终止线程的执行。
使用场景
java中对异常的处理主要有两种:
一种是使用throws 向上抛出,交给调用者处理,
另一种是使用try-catch进行捕获,自己处理
选择哪种取决于异常应该怎么处理。
使用throws的场景:
异常必须由容器来处理
例如:有事物处理的方法中,事务相关的逻辑必须抛出异常,不能捕获,否则会导致事务不回滚。
本地方法不知道该怎么处理,只有调用方才可能知道如何处理
例如:一些底层的方法,可能出现多种异常,只要调用方可能根据不同异常做出不同处理,这样只能抛出,而且必须是具体的异常类型,不能是Exception类型。
使用try-catch的场景:
程序中的语句可能异常不能引起其他逻辑中断;
例如:缓存逻辑不能引起其他逻辑中断,所以缓存逻辑必须放在try-catch中
必须对异常进行处理,否则会影响用户体验;
例如:异常在Controller层,若不处理会报404或500错误页面,有时抛出过多异常会导致程序崩溃,因此,必须使用try-catch处理。
扩展:自定义异常
编写自定义异常,处理输入字符串abc抛出异常,其他不抛出
定义异常MyException,当输入字符串为abc时,抛出异常
public class MyException extends Exception{
private String errorMsg;
public MyException(String errorMsg){
this.errorMsg=errorMsg;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
测试类,从控制台输入abc,抛出异常。
public class TestException{
public static void main(String[] args){
Scanner scan=new Scanner(System.in);
String str=scan.nextline();
if(str.equals("abc")){
throw new MyException("错了");
}
}
}