什么是异常
异常就是Java的程序中出现的各种错误和异常。
因为java的核心思想是面向对象,所以程序中出现的各种错误和异常也可看做是一个个的对象,我们对异常的处理也可是看做是对 对象的处理。
既然是对象了,那么一定要有关于异常的类。
所有的异常都有一个父类,这个父类就是Throwable。所有的异常及错误都要在异常体系中才能被虚拟机识别到。
Throwable有两个子类,一个是Error(错误),另一个是Exception(异常)。
Error:所有的错误类后缀都是Error,错误的出现往往意味着虚拟机本身出现了问题,例:OutOfMemoryError(内存溢出错误),StackOverflowError(堆栈内存溢出)。我们不对虚拟机中的错误进行处理。
Exception:所有的异常类后缀都是Exception,分编译时期异常和运行时异常。例:NullPointerException(空指针异常),IndexOutOfBoubdsException(下标越界异常)。
如何寻找异常出现在哪里呢?
//这是一个异常测试类
public class ExceptionDemo {
public static void main(String[] args) throws Exception{
test();
}
//这是一个在运行中会出现异常的方法
public static void test(){
int a = 1/0;
}
}

1、先看错误类型,ArithmeticException:算数条件异常
2、第一行,错误在main方法里,错误信息是除数不能为0。
3、第二行,这里才是异常真正的出现的地方,这个代码我是在test方法中写了一个1/0的代码,所以这里会出现异常。
4、第三行,main方法出现异常。既然真正的异常是在test方法中出现的,为什么控制台会说main方法也出现异常了呢,这里我们就要开始下面的学习了
那要如何处理异常呢
异常的处理
异常有两种处理方式:
一种是捕获异常,由代码自己完成对异常的处理。
另一种是抛出异常,谁调用的这里的代码,谁来处理异常。
捕获异常
捕获异常里的关键词就是try、catch、finally
语法格式
try{
可能出现问题的代码
}catch(异常类型 变量){
处理方案
}finally{
最终必然会执行的代码
}
当可能出现多个异常时,可以使用多个catch进行处理
注意,其中的任意一部分都不能单独存在,必须配合另外的部分才能使用
try{}catch(){}
try{}finally{}
try{}catch(){}finally{}
捕获异常的演示
//异常测试
public class Test1 {
public static void main(String[] args) {
try{//把可能会出现异常的代码块放进这里
int a = 1/0;
System.out.println("这行是测试");
}catch(Exception e){//异常类型,先拿所有的异常类Exception来用
//try中的代码一旦出现问题,就不会执行剩下的代码了,程序会直接执行catch中的部分
System.out.println("这里出现异常了");
//这里使用Exception类型的e变量调用异常类中的获取信息方法
System.out.println(e.getMessage());
}finally{//不管是否异常都会执行的代码
System.out.println("这行一定会执行");
}
}
}
运行结果
这里出现异常了
/ by zero
这行一定会执行
当代码块中可能出现多个异常时,可以用多个catch来捕捉。
try catch为对代码块中异常的处理
对代码块中可能出现异常的整体用try括号包住,一旦里面的代码出现问题,就会在catch里寻找对应的异常类型,并执行catch代码块里的处理方法,之后再执行finally里的内容。try catch的执行并不会影响程序的执行,程序并不会中断。
抛出异常
抛出异常
而抛出异常就稍微有一点点绕了。
抛出异常是虚拟机默认帮你操作的,如果你写的代码,在运行时出现了异常,而你却又没有给代码写针对异常的处理方式,虚拟机就会自动将你的异常抛给上一级。这就是隐式的抛异常。上一级如果也没有针对异常的处理方法,也会继续默认的向上抛。直到抛给虚拟机,虚拟机对异常的操作就是打印异常信息到控制台,控制台终止程序的运行。
抛出异常有两个关键词,一个是throw,一个是throws。
throw就是手动的将异常抛出,也叫显式抛异常,这是执行的抛出的这个动作。简单理解为程序只要运行到这里就会执行抛异常的动作。哪怕代码中并没有真正的出现异常,程序也会执行这个抛出异常的动作。
throws是在方法头中的声明,他声明的是本方法中可能生成的异常的类型。这对应的就是方法中的throw抛异常动作了,只要代码中写了抛异常这个动作,那么这个方法就必须声明会出现的异常类型。可以用逗号隔开,写多个异常类型。
下面写一个简单的例子
//异常练习
public class ExceptionDemo {
//这里写一个抛出算数条件异常的代码,只要方法中抛出异常了,所以就需要声明异常类型
//这里声明Exception是因为这是所有异常的父类,所有的异常都继承了这个父类
public static void test() throws Exception{
// ↑ ↑
// throws声明异常关键词 异常类型
//本身这个方法是没有问题的,但只要写了throw这个手动抛异常
//哪怕代码本身并没有任何问题,程序都会抛一个异常给调用者,就像是人为制造了一个异常
//这里是抛给谁了呢,因为是main方法调用的这个test方法,所以就是把异常抛给它
throw new ArithmeticException();
}
//主方法,因为主方法调用了一个会抛出异常的方法,所以主方法也要声明异常类型。
public static void main(String[] args) throws Exception{
// ↑ ↑
// throws声明异常关键词 异常类型
//因为main方法调用了会出现异常的方法,所以就提前用try catch捕捉一下异常
try{
test();
}catch(Exception e){
//由于是人为制造的异常,所以异常信息并没有显示
System.out.println(e.getMessage());
//但既然执行了下面这行代码,就说明确实出现了异常,并被try catch捕获了
System.out.println("异常被我捕获了~~~");
}
}
}
null
异常被我捕获了~~~
上述代码说明,throw确实是执行的抛异常动作。只要写这个代码,那么它就会抛异常,不管代码中有没有真正的异常。那异常抛给谁了呢?通过main方法中的try catch可以看到确实是抛给了调用者。
再来看看开头介绍异常的代码
//这是一个异常测试类
public class ExceptionDemo {
public static void main(String[] args) throws Exception{
test();
}
//这是一个在运行中会出现异常的方法
public static void test(){
int a = 1/0;
}
}

现在test方法异常是不是很好理解了。只不过这种抛异常时隐式的抛异常,代码只要出现了异常,系统就会自动的往上抛异常。所以我们不给test方法声明异常类型它也会向上抛,这不过这一切都隐藏着去执行的罢了。
控制台输出异常的时候也会提示main方法异常。因为main方法是程序的入口嘛,程序是从这里开始执行的,所以异常在往上层抛的时候总会经过main方法的。
而这个代码的主方法并没有写对异常的处理,这时主方法也将异常抛出,抛给再上一层的调用者,这就抛到了虚拟机这里。虚拟机会将程序终止,并将异常信息打印在控制台。
总结
我们在运行代码时,代码的异常终止其实就是代码再做隐式的抛异常,最终异常抛到了虚拟机这里。虚拟机会终止我们的程序,并告诉我们究竟是哪里出现了问题。
自定义异常
在Java中可以自定义异常。如果需要要自定义异常类,就需要将异常类继承Exception类即可。因为虚拟机只能够识别到加入异常体系的异常类,所以继承所有异常的父类就可以加入异常体系类。那为什么不选择继承Throwable呢,Throwable类明明更是所有错误的父类啊。因为Throwable类还包含了错误,自定义的是异常,不能有错的相关信息。
一般我们只在自定义遗产中定义一个无参的构造器,和一个带有String类型参数的构造器。
public class MyCustomException extends Exception{
//无参构造器
public MyCustomException () {}
//带有String类型参数的构造器
public MyCustomException (String s) {
super(s);
}
}

本文围绕Java异常展开,介绍了异常概念,指出异常和错误是对象,有父类Throwable,其两个子类为Error和Exception。还阐述了异常处理方式,包括捕获(用try、catch、finally)和抛出(隐式与显式),最后说明了可通过继承Exception类自定义异常。

2089

被折叠的 条评论
为什么被折叠?



