- 一级目录
- 二级目录
- 异常的定义
异常的定义:在Java语言中, 将程序执行中发生的不正常情况称为异常。而错误是系统错误,一般不需要开发人员处理(也无法处理),比如栈溢出或者内存不足。(开发过程中的语法错误和逻辑错误不是异常,也不是我们分类中的错误,是你的 错觉 哈哈)
异常的认知:在Java中,异常被定义为类,异常相关的类被称作异常类。当程序发生异常时,会抛出对应异常类的对象,该对象承载了异常信息,我们就能在显示框中看到诸如 NullPointerException, NoSuchElementException, IndexOutOfBoundsException 这些异常。
异常的体系:异常类,是由继承构成的,最上层是java.lang.Throwable。
下属两大子类,
1️⃣ java.lang.Error: Java虚拟机无法解决的严重问题。 如: JVM系统内部错误、 资源耗尽等严重情况。 例如:StackOverflowError和OutOfMemoryError。 这类错误一般不编写针对性的代码进行处理。
2️⃣ java.lang.Exception: 它出现的原因就是诸如 空指针访问,试图读取不存在的文件,数组索引越界等。 这类问题可以通过使用针对性的代码进行处理。
上面提到的 java.lang.Error ,这里不做过多说明。我们主要讲 写代码常遇到的 异常,也就是Exception ,Exception 又分为两类:
编译时异常(checked),编译时可能发生的异常,除了下面RuntimeException的子类,其他的异常都属于编译时异常。
运行时异常(unchecked/RuntimeException),编译时无异常,运行时发生的异常。
这里用图来展示它们之间的关系
里面涉及到一个概念,叫受检异常和非受检异常。其实受检异常就是编译异常,非受检异常就是运行时异常,这两个词应该很好理解。
常见运行时异常举例
1,NullPointerException
int[] arr = null; System.out.println(arr[0]); // 数组空指针异常 String str = null; System.out.println(str.charAt(0)); // 字符串空指针异常
2,NullPointerException
int[] arr = new int[10]; System.out.println(arr[10]); // 数组角标越界 ArrayIndexOutOfBoundsException String str = "abc"; System.out.println(str.charAt(-1)); // 字符串角标越界 StringIndexOutOfBoundsException
3,ClassCastException
Father sonA = new SonA(); SonB sonB = (Sonb)sonA; // 类型转换异常 Object obj = new Date(); String str = (String)obj; // 类型转换异常
4,NumberFormatException
public void test2(){
String str = "abc";
Integer.parseInt(str); // 数字格式异常
}
5,InputMismatchExcepton
Scanner scan = new Scanner(System.in); int num = Scanner.nextInt(); // 输入非数字时,则会出现输入不匹配异常
6,ArithmeticException
int a = 10; int b = 0; // 不能除0 System.out.println(a / b); // 算数异常
异常的处理
有小伙伴可能会想,异常不都是程序发现并抛出的吗,那我们能干啥。嘿嘿,对于一般人,这个确实无解,但咱们程序猿可是无所不能的啊,当然有办法处理。
我们知道程序执行过程中,一旦出现异常,执行就会停止,后面的代码就不会再执行,这是因为我们没有处理,程序遇到异常就会交给JVM,它就会终止程序,那么如果我们告诉了它遇到这个异常,该干什么,它就不会中断执行。
我们要主动处理异常,就要提前预设会出现什么异常,以及出现异常后显示什么。
异常处理的方式:try-catch-finally 和 throws
使用方式如下:
try {
// 可能出现异常的代码段
} catch(异常类型1 变量名1) { // 也可以同时定义多个,但不推荐
// 处理异常的方式1
} catch(异常类型2 变量名2) {
// 处理异常的方式2
} catch(异常类型3 变量名3) {
// 处理异常的方式3
}
...
finally {
// 一定会执行的代码
}
在上述语法中, try catch是必须要有的,try负责运行可能出现异常的代码段,catch负责捕获相应的异常,如果没有捕获到,程序就交给JVM。 finally 是一个可选的部分,但是如果加上它,它里面的内容是无条件执行的。
关于catch使用的易错点,如果用catch写了多个异常,并且这些异常之间构成父子类,那么最上面一定要是子类。 道理很简单,父类包含的异常种类最多,如果把父类放上面,那么发现的所有异常都被父类通吃了,那么捕获异常所提示的信息就没有价值,不利于快速处理异常。
这个时候,有小伙伴又有疑问了,你说的try,catch,finally,我大概懂了,可是你不是还提了个 throws嘛,它怎么用啊,真是爱动脑筋的聪明鬼,好,那我们就讲一下它们的实战用法。
public static void func2 throws NullPointerException() {
int[] a = new int[]{};
for (int i = 0; i < 5; i++) {
a[i] = i;
if (i == 4) {
a[11] == 10;//由于没有11下标,所以一定会报空指针异常
}
}
}
public static void main(String[] args) {
try{func2();
}catch (NulException e){
System.out.println("空指针异常");//这里输入信息,程序执行到这里,就会给出有关这个异常的信息,并且不会中断
}
}
结合上面这个例子,小伙伴们应该就懂了,throws 像一个岗哨,它在可能出现异常相关异常的方法前面声明这个异常,那么后续在调用这个方法执行时,就必须用 try catch来捕获,并给出处理方式,这样程序即使遇到异常,也能执行下去而不会中断。
除此而外,catch中还有两个方法可以使用,方便我们在后续代码开发中,及时发现问题,减轻工作量。
getMessage() : 返回值是String,主要作用是打印错误代码
printStackTrace(): 返回值类型是void ,主要作用是打印异常类型
还有说明的一点是,在实际生活中,遇到异常的场景不一样,同样的异常处理方式也不同,例如 和钱相关的场景,出现异常就属于比较严重的异常这个时候,就应该让程序直接崩溃掉。 而对于产生后果不严重的异常,就记日志或者监控报警并通知程序猿后期处理。
异常种类见了这么多个,但是有些伙计觉得有点不高兴,因为束缚了他的自由,不能让他大展才华,哈哈,那能不能自己定义异常呢,当然可以,那我们说一下怎么做到。
自定义异常
要注意一下几点,
1,自定义的类必须继承于现有的异常结构:通常继承于RuntimeException 和Exception (继承这个默认受查异常)
2,提供无参和一参的构造方法
如下
//自定义异常
class MyException extends RuntimeException{
public MyException(){
super();
}
public MyException(String message){
super(message);
}
}
//使用举例
//异常
public static void func() throws RuntimeException,NullPointerException{
int a= 10;
if (a == 10){
throw new RuntimeException("a==10");
}
}
public static void main10(String[] args) {
try{
func();
}catch (RuntimeException e){
System.out.println("抛出了一个异常!");
}
}



