Java | 异常
异常
异常,就是不正常的意,在程序中指的是程序在执行过程中,出现的非正常的情况,最终导致程序停止。
异常的体系
在java中,异常的要类是java.lang.Throwable
,他有两个直接子类,java.lang.Error
和java.lang.Exception
,两个类,这三个都是类,不是接口。并且,所有的和异常相关的都是类。
在java.lang.Throwable
里面我们常用的有三个方法:
public void printStackTrace()
,打印异常的详细信息,打印出异常的类型,异常的信息,异常的出现的位置,我们在开发和调试的时候,用的最多的就是这个方法。public String getMessage()
,只打印异常的描述信息。public String toString()
,打样印异常的类型,还有异常的描述信息。
异常的分类
异常的分类,我们通常说的异常的分类,一般指的都是Exception类或者Exception下面的子类,并且Exception分为两种类型:
运行时异常:(
RuntimeException
) 在运行的时候,会暴露出来,但是在编译的时候,不会提示。可以不用我们处理。
编译时异常:(Exception
)在编译的时候出现的异常,说明必须要处理,如果不处理的话,编译就会不通过,会编译失内败。
异常的处理
处理异常有的五个关键字:try
、catch
、finally
、throw
、throws
。
我们处理异常的方式有两种:
1、
try ... catch
,在try
里面捕获异常,在catch
里面处理异常。
2、throws
,声明异常。
声明异常throws
声明异常,就是将问题的标识出来,然后报告给方法的调用者,如果方法里面出现了编译时导演,没有进行捕获,而是选择了抛出异常,也就是声明异常,那么,这个异常就会交给调用者出理。
使用的格式:
修饰符 返回值类型 方法名(参数)throws 异常类型1,...异常类型n { }
例:
public static void test() throws InterruptedException {
//这是一个编译时异常,但是没有进行捕获,直接抛出
Thread.sleep(1000);
}
异常抛出以后,可以在调用者那里直接也进行抛出,也可以进行捕获。
public static void main(String[] args) throws InterruptedException {
test();
}
public static void main(String[] args){
try {
test();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
异常可以一直向上抛出,一直到JVM里面。
可以自己声明异常:
public static void main(String[] args) throws IOException, InterruptedException {
test(0);
}
public static void test(Integer num) throws InterruptedException, IOException {
//这是一个编译时异常,但是没有进行捕获,直接抛出
Thread.sleep(1000);
if (num == 0) {
throw new IOException("输入的是一个0");
}
}
运行的结果:
上面的一段代码中,我们抛出了两个异常,一个是线程类必须要处理的异常,一个是自己声明的异常,所以如果我们要抛出多个异常的时候,我们要用 ,
隔开。
其实在抛出异常的时候,我们不用抛出那么细致的异常,我们可以直接抛出一个大的异常类,比如Exception
类。并且运行的结果还是一样的。
捕获异常 try...catch
如果异常出现,我们必须得处理,不会程序会立刻终止。用try...catch
方式就是捕获异常,我们可以对可以出现异常的语句进行异常的捕获。
语法如下:
try{
//可能出现异常的语句
}catch{
//出现异常之后的处理的方法
}
- try:这个代码块当中编写可能出现的异常。
- catch:这个代码块当中对异常进行捕获,并且进行处理。
他们必须要一起使用,不能单个使用!
例子:
//对异常进行捕获,然后打印出详细的信息
try {
Thread.sleep(1000); //编译时异常
} catch (InterruptedException e) { //但是如果没有异常,就会继续执行下面的代码。
e.printStackTrace();
}
//也可以对运行时异常进行处理
try {
int i = 1 / 0; //运行时异常
} catch (Exception e) {
e.printStackTrace();
}
执行的结果:
上面图中的异常信息,就是Throwable
里面的printStackTrace()
打印出来的,包含了异常的信息,异常的类型,并且包含了异常出现的行数。
finally 代码块
finally:我们在处理一些特定的事情的时候时,不管我们的代码是否出现异常,我们都必须要执行。并且有的时候,我们在执行代码的时候,如果在try
里面出现了异常,那么从出现异常的地方开始,后台的代码都不会执行,直接会跳过,执行catch
里面的内容,这是我们不能容忍的,所以出现了finally
代码块。把我们必须要处理的代码放进去,不管会不会出现异常,都必须要执行这一段代码。
语法:
try{
//可能出现异常的语句
}catch{
//如果出现异常执行的逻辑
}finally{
//不管出不出现异常都要执行的代码
}
例:
没有使用finally的时候:
try {
int i = 1 / 0;
System.out.println("我想输出这一句话");
} catch (Exception e) {
e.printStackTrace();
}
结果:
在使用finally的情况:
try {
int i = 1 / 0;
} catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("我想输出这一句话");
}
结果:
自定义异常
在我们实际工作中,java自带的异常总是不够我们用,所以我们这个时候,就必须要自定义异常了。
//可以继承Exception (编译时异常),RuntimeException(运行时异常)
public class ZeroException extends Exception {
//自定义的异常的时候,我们一般都是这个格式
//来一个无参构造方法,一个有参构造方尘,然后都是调用父类的构造方法
public ZeroException() {
super();
}
public ZeroException(String message) {
super(message);
}
}
上面是我们自己定义的异常,下面让我们看看java
定义的异常类,NullPointerException
。(空指针异常)
public class NullPointerException extends RuntimeException {
private static final long serialVersionUID = 5162710183389028792L;
/**
* Constructs a {@code NullPointerException} with no detail message.
*/
public NullPointerException() {
super();
}
/**
* Constructs a {@code NullPointerException} with the specified
* detail message.
*
* @param s the detail message.
*/
public NullPointerException(String s) {
super(s);
}
}
使用我们自己定义的异常:
int i = 0;
if (i == 0) {
try {
throw new ZeroException("不可以为0");
} catch (ZeroException e) {
e.printStackTrace();
}
}
运行的结果:
使用异常时候需要注意
运行时异常被抛出可以不处理。即不捕获也不声明抛出。
如果finally
有return
语句,永远返回finally
中的结果,避免该情况。
如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出。
多个catch
中的异常不能相同,并且若catch
中的多个异常之间有子父类异常的关系,那么子类异常要求在上面的catch
处理,父类异常在下面的catch处理。
关注公众号,随时获取最新资讯
细节决定成败!
个人愚见,如有不对,恳请斧正!