异常(我们想让程序遇到问题时还能继续执行/)
程序在运行过程中,由于意外情况导致程序发生异常事件,默认情况下发生的异常会中断程序运行。
在Java中,把常见的异常情况,都抽象成了对应的异常类型,那么每种异常类型都代表了一种特定的异常情况。
当程序中出现一种异常情况时,也会创建并抛出一个异常类型对象,这个对象就表示当前程序所出现的问题。
异常体系中的根类是: java.lang.Throwable ,该类下面有俩个子类型, java.lang.Error 和 java.lang.Exception
Error ,表示错误情况,一般是程序中出现了比较严重的问题,并且程序自身并无法进行处理。
Exception ,表示异常情况,程序中出了这种异常,大多是可以通过特定的方式进行处理和纠正的,并且处理完了之后,程序还可以继续往下正常运行。
异常种类
我们平时使用的异常类型,都是 Exception 类的子类型,它们把异常划分成了俩种:
编译时异常
运行时异常
编译时异常,继承自 Exception 类,也称为checked exception,编译器在编译期间,会主动检查这种异常,发现后会报错,并提示我们要对这种异常进行处理。
运行时异常,继承自 RuntimeException 类,也称为unchecked exception,编译器在编译期间,不会检查这种异常,也不要求我们去处理,但是在运行期间,代码中可能会抛出这种类型的异常。
手动抛异常(用throw 并且new一个对象)
public static double div(double i,double j) {
if(j>0) {
throw new NullPointerException("空指针");
}
return i/j;
}
自动抛异常(jvm)
异常传播
如果一个方法中抛出了异常,并且一直没有进行处理,那么这个异常将会抛给当前方法的调用者,并一 直向上抛出,直到抛给JVM,最后JVM将这个异常信息打印输出,同时程序运行的停止。
public class Test {
public static void main(String[] args) {
System.out.println("hello");
test1();
System.out.println("world");
}
public static void test1(){
test2();
}
public static void test2(){
test3();
}
public static void test3(){
int a = 1/0;
}
}
//运行结果:
hello
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.demo.Test.test3(Test.java:16)
at com.demo.Test.test2(Test.java:13)
at com.demo.Test.test1(Test.java:10)
at com.demo.Test.main(Test.java:5)
层层上抛给jvm
自动抛出异常
当前java代码中,出现了提前指定好的异常情况的时候,代码会自动创建异常对象,并且将该异常对象抛出。
例如,当代码中执行 int a = 1/0; 的时候,代码会自动创建并抛出 ArithmeticException 类型的异常对象,来表示当前的这种异常情况。(算术异常)
例如,当前代码中执行 String str = null; str.toString(); 的时候,代码会自动创建并抛出NullPointerException 类型的异常对象,来表示当前这种异常情况。(空指针异常)
手动抛出异常
以上描述的异常情况,都是JVM中提前规定好的,我们不需要干预,JVM内部自己就会创建并抛出异常 对象。 但是在其他的一些情况下,我们也可以手动的创建并抛出异常对象,其效果也是一样的。
异常捕获
当一个方法内,抛出了编译异常的时候,编译器在编译期间检查到,就会报错,提示我们有俩种修改方案:
把这个异常在方法上进行声明抛出(throws Exception)直接向上抛出/jvm/调用者
把这个异常再方法内进行捕获处理(try-catch)抛出之后处理
try-catch
try-catch 语句块,就是用来对指定代码,进行异常捕获处理,并且处理完成后,JVM不会停止运行,代码还可以正常的往下运行!
public class Test {
public static void main(String[] args) {
System.out.println("hello");
Test t = new Test();
try {
t.test("zs");
} catch (Exception e) {
//打印输出发生异常的时候,栈里面的方法调用情况,方便我们定位和修改代码
e.printStackTrace();
}
System.out.println("world");
}
public void test(String name)throws Exception{
if(!"tom".equals(name)){
throw new Exception("用户名不正确");
}
}
}
//运行结果:
hello
world
java.lang.Exception: 用户名不正确
at com.briup.demo.Test.test(Test.java:20)
at com.briup.demo.Test.main(Test.java:9)
3 捕获多种异常
如果try语句块中的多句代码,都会抛出异常,并且是不同类型的异常,那么catch语句块就有不同的写法,来处理这几个不同类型的异常。
public static void main(String[] args) {
String className = "com.briup.demo.Student";
String methodName = "sayHello";
try {
//forName声明抛出ClassNotFoundException
Class c = Class.forName(className);
//getMethod方法声明抛出NoSuchMethodException
Method m = c.getMethod(methodName);
//invoke方法声明抛出IllegalAccessException和InvocationTargetException
m.invoke(null);
} catch (ClassNotFoundException | NoSuchMethodException |
IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
或者
try {
//forName声明抛出ClassNotFoundException
Class c = Class.forName(className);
//getMethod方法声明抛出NoSuchMethodException
Method m = c.getMethod(methodName);
//invoke方法声明抛出IllegalAccessException和InvocationTargetException
m.invoke(null);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
//建议把大的异常写后面
finally语句
try-catch语句块,虽然可以捕获并处理异常情况,但是它也会改变代码的执行流程
只要使用 finally 关键,就可以保证指定代码一定会执行,无论是否发生异常
finally最后才会执行且一定会执行
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码,且无论上面是否抛出异常还是继续执行,这段代码都会执行且最后执行
}
自定义异常
异常都是Throwable的子类/Error/Exception(runtime/运行时异常,uncheck异常 exception,编译时异常,check异常)
比如自定义ModifyUserInfoExceptin异常
public class ModifyUserInfoExceptin extends RuntimeException{
public ModifyUserInfoExceptin(){
}
public ModifyUserInfoExceptin(String message) {
super(message);
}
}
final finally 和 finalize()区别
final 用于修饰类、方法和变量。是一种非访问修饰符
finally 关键字用来创建在 try 代码块后面执行的代码块。
无论是否发生异常,finally 代码块中的代码总会被执行。
finalize()是Object的protected方法,GC在回收对象之前会调用该方法进行垃圾回收。