异常机制
异常概述
-
在Java语言中,将程序执行中发生的不正常情况称为“异常”。异常是程序中的一些错误,但不是所以的错误都是异常,并且错误有时候是不可避免的。
异常分类![](https://img-blog.csdnimg.cn/fd2fa0ce2ca641f797d41852ae3c85cd.png)
-
从继承结构中可以看到Object下有一个Throwable,在Throwable下有两个子类。Error表示错误,在java中发生错误是无法处理的,例如:内存溢出错误OutOfMemoryError等。Exception表示异常,异常是可以处理的,异常发生之后有两个结果,1:程序终止执行,2:处理完异常继续执行。
-
在Exception下有两大类,Exception子类(受控异常/编译时异常),RuntimeException(非受控异常/运行时异常)。所有的编译时异常要求在程序编写阶段预先处理,如果不处理编译器会报错。
常见的异常
-
NullPointerException空指针异常类
-
ClassNotFoundException指定的类不存在
-
NumberFormatException字符串转换为数字异常
-
ArrayIndexOutOfBoundsException数组下标越界异常
-
IllegalArgumentException方法的参数错误
-
IllegalAccessException没有访问权限
-
ArithmeticException数学运算异常
-
ClassCastException数据类型转换异常
-
FileNotFoundException文件未找到异常
-
ArrayStoreException数组存储异常
-
NoSuchMethodException方法不存在异常
-
InstantiationException实例化异常
-
OutOfMemoryException 内存不足错误
-
.NoClassDefFoundException 未找到类定义错误
-
......
try-catch捕捉
-
以下程序没有使用异常机制:
public class Test { public static void main(String[] args) { int[] arr = new int[]{10,20,30,40,50,0,60,70,80,90}; for (int i=0;i<arr.length;i++){ int val = 100 / arr[i]; System.out.println("100/"+arr[i]+"=" + val); } } } 运行结果: 100/10=10 100/20=5 100/30=3 100/40=2 100/50=2 Exception in thread "main" java.lang.ArithmeticException: / by zero at Test.main(Test.java:7)
-
通过以上程序发现当一个地方出现了错误,程序就停止执行。接下来使用异常机制try-catch捕捉执行以上代码:
public class Test { public static void main(String[] args) { int[] arr = new int[]{10,20,30,40,50,0,60,70,80,90}; for (int i=0;i<arr.length;i++){ try{ int val = 100 / arr[i]; System.out.println("100/"+arr[i]+"=" + val); }catch(Exception e){ System.out.println("100/"+arr[i]+",出现异常"); } } } } 运行结果: 100/10=10 100/20=5 100/30=3 100/40=2 100/50=2 100/0,出现异常 100/60=1 100/70=1 100/80=1 100/90=1
-
如果想要知道捕捉的异常是什么,可以使用以下方式:
public class Test { public static void main(String[] args) { int[] arr = new int[]{10,20,30,40,50,0,60,70,80,90}; for (int i=0;i<arr.length;i++){ try{ int val = 100 / arr[i]; System.out.println("100/"+arr[i]+"=" + val); }catch(Exception e){ // 使用这一句可以知道捕捉了什么异常 e.printStackTrace(); } } } } 运行结果: 100/10=10 100/20=5 100/30=3 100/40=2 100/50=2 100/60=1 100/70=1 100/80=1 100/90=1 java.lang.ArithmeticException: / by zero at Test.main(Test.java:8)
-
以上程序捕捉了ArithmeticException异常,这是一个算术运算异,例如:一个整数除以零。
-
如果异常代码块发生异常,异常代码块外的程序会不会执行:
public class Test { public static void main(String[] args) { int[] arr = new int[10]; try{ System.out.println("arr数组第十一个元素 = " + arr[11]); }catch(Exception e){ e.printStackTrace(); } // 异常代码块发生异常之后,这里的程序会不会执行! System.out.println("hello world!"); } } 运行结果: java.lang.ArrayIndexOutOfBoundsException: Index 11 out of bounds for length 10 at Test.main(Test.java:7) hello world!
-
从以上程序可以发现,异常代码块中发生了异常,之外的程序可以执行。
finally语句块
-
finally语句块必须和try语句块联合使用,不能独立使用,放在finally语句块中的程序是必须执行的。
public class Test { public static void main(String[] args) { int[] arr = new int[10]; try{ System.out.println("arr数组第十一个元素 = " + arr[11]); }catch(Exception e){ e.printStackTrace(); }finally { System.out.println("finally语句块执行了!"); } } } 运行结果: java.lang.ArrayIndexOutOfBoundsException: Index 11 out of bounds for length 10 at Test.main(Test.java:7) finally语句块执行了!
-
无论try语句块中有没有发生异常,finally语句块都必须执行。一般finally语句块经常用来关闭流\资源,流\资源是非常宝贵的,用完之后必须要关闭,如果不关闭会导致内存泄漏。
public class Test { public static void main(String[] args) { FileWriter out = null; try { // 创建一个标准输出流 out = new FileWriter(""); } catch (IOException e) { throw new RuntimeException(e); }finally { try { // 关闭流的方法,需要处理异常 out.close(); } catch (IOException e) { throw new RuntimeException(e); } } } }
如何自定义异常
-
异常类要么继承RuntimeException(非受控异常)或者继承Exception(受控异常),然后提供一个无参构造方法和一个带有String参数的构造方法。
-
接下来自定义一个登录失败的异常(自定义异常时需要判断继承哪个异常):
public class LoginException extends RuntimeException{ public LoginException(){ } public LoginException(String s){ super(s); } }
手动抛出异常
-
异常发生需要new异常对象,使用throw关键字将异常抛出。结合上面自定义登录失败异常,写一个简单案例:
public class TestLogin { /** * 模拟登录程序 * @param args */ public static void main(String[] args) { // 准备一个用户名和密码 String user = "123456"; String pwd = "abc"; Scanner scanner = new Scanner(System.in); // 输入用户名和密码 String uuser = scanner.next().trim(); String ppwd = scanner.next().trim(); if (!(user.equals(uuser) && pwd.equals(ppwd))){ // 手动抛出异常 throw new LoginException("登录失败!"); } System.out.println("登录成功!"); } } 运行结果1: 123456 abc 登录成功! 运行结果2: 123456 aaa Exception in thread "main" com.bj.servlet.LoginException: 登录失败! at com.bj.servlet.TestLogin.main(TestLogin.java:22)
声明异常
-
使用throws用来声明以便抛出,throws只能出现在方法声明的位置上,意思是将异常继续上抛给调用程序来处理。
public class Test { public static void main(String[] args) throws Exception{ // 声明异常 // 创建流 FileWriter out = new FileWriter(""); // 关闭流 out.close(); } }
-
假设某个公司有3个员工,普通员工犯了错误处理不了,上抛给经理,如果经理处理不了,继续上抛给董事长,如果董事长也处理不了,公司倒闭\相当于程序结束。
-
try catch捕捉异常,被捕捉的异常在当前程序被处理掉,throws上抛异常,自己不处理,如果一直不处理一直上抛,最终抛到JVM(java虚拟机),如果JVM也处理不了,程序结束。
final , finalize , finally的区别
-
final是一个关键字,用来修饰类,方法,变量等,表示最终的,不可变的。
-
finalize是Object类中的一个方法名,属于标识符。
-
finally是一个关键字,属于异常处理机制,finally必须联合try一起使用。