异常要从下往上看,先看异常的类型,再看异常出现的位置。
异常的概述
- 异常:是指在我们的程序执行过程中,出现的非正常情况,如果不能正确处理会导致程序的非正常停止(崩溃)。
在我们进行程序设计的过程中,尽管我们已经很努力的让程序变得很完美了,但是还是会遇到一些问题。这些问题是我们不可避免的,比如读取文件是否存在、网络是否处于一直连接等。
我们要去想的就是在有可能发生异常的情况下怎么去避免异常、怎么去处理异常。
异常的体系
在Java中无论是什么异常都有一个根类:Java.lang.Throwable
,在这个根类之下又有两大个子类:java.lang.Error
与java.lang.Exception
,我们平常见到最多的就是java.lang.Exception
下的异常。
Throwable的两大子类
- Error:严重的错误,是指无法处理的错误,只能够去避免。比如:StackOverflow Error、OutOfMemoryError等。
- Exception:异常,是指可以通过相应预防或处理措施,让程序在发生异常后还可以继续运行。由编程错误或外在因素导致的问题。比如:空指针访问、读取文件不存在、数组的角标越界等。
Throwable中的常用方法
public void printStackTrace()
:打印出异常的详细信息。包含有异常的类型、异常的原因、包括异常出现的位置 。在开发和调试阶段都是使用printStackTrace。public String getMessage()
获取发生异常的原因。
遇到异常了,先不要慌
只要不是电脑被你搞没了,先看异常类型,再看位置,找找是不是哪里少写了什么东西。找不到了,也不要慌,有一个东西叫复制->打开百度->粘贴查异常。
异常的分类
由于Error是我们没法处理的。那得,只需要尽量去避免,修改代码。
关键是Exception异常。里面异常一大堆,如果你幸运碰见了,那好了,你进步的机会来了。找问题,改问题。Exception分为两大类:
- 运行期异常unchecked Exception(也叫非受检异常):发生的原因大多都是代码逻辑不严谨造成的(数组越界越界)等,可以处理也可以不处理,那必然是处理啦。通过修改和优化代码避免。
- 编译期异常checked Exception(也叫受检异常):一般是程序外的原因造成的(读取文件不存在,网络中断),不是代码逻辑编写有问题,但是出现的最多,而且我们还必须去处理。你真不去处理,程序还真就不让你编译通过。
你曾经犯过的错误与异常演示:
- VirtualMachineError:
最常见:
StackOverflowError
:虚拟机栈的内存空间不够,分配不了栈帧。
OutOfMemoryError
:内存空间不足。
@Test
public void test(){
//StackOverflowError
kanWo();
}
public void kanWo(){
kanWo();
}
@Test
public void test(){
//OutOfMemoryError
StringBuilder s = new StringBuilder();
while(true){
s.append("java");
}
}
- 运行时异常
@Test
public void nullPointer(){
//NullPointerException
int[] arr=null;
System.out.println(arr.length);
}
@Test
public void classCast(){
//ClassCastException
Animal c = new Cat();
Dog g = (Dog) c;
}
@Test
public void arrayIndexOut(){
//ArrayIndexOutOfBoundsException
int[] arr = new int[5];
System.out.println(arr[5]);
}
@Test
public void arithmetic(){
int a = 1;
int b = 0;
//ArithmeticException
System.out.println(a/b);
}
- 编译期异常
@Test
public void interruptedE() throws InterruptedException{
Thread.sleep(1000);//休眠1秒
}
@Test
public void fileNotFound() throws FileNotFoundException{
FileInputStream fis = new FileInputStream("Java学习秘籍.txt");
}
异常的抛出机制
Java程序运行过程中如果出现了异常,那么会生成一个异常对象,这个对象会被提交给Java运行时系统,这个过程就叫做抛出(throw)异常了。异常可能会由虚拟机自动生成
,也可以由我们开发人员手动去创建
。
- 虚拟机自动生成异常:程序发生问题了,虚拟机检测到了会自动在后台创建一个对应的实例对象并自动抛出。
异常对象被创建后,自动往上抛,抛给方法调用者,在抛给调用者的调用者…最后给虚拟机了,虚拟机打印异常信息之后就终止程序。
- 开发人员创建:我们可以自动在可能会发生异常的地方使用throw关键字抛出一个新建的异常,产生异常的方法使用throws进行声明一个异常。异常抛给方法调用处,再抛给方法调用出,…抛给虚拟机。如果中途没有处理,程序崩溃。
新建异常的格式:
throw new 异常类型(参数);
方法上声明异常的格式:
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }
异常的处理
一个异常产生了,那么如果没有在当时处理会先交给调用这个有异常的方法的方法进行处理,如果没有处理会在交给上级处理,直到异常被处理。这就是捕获异常的过程。
捕获异常之try…catch
- 对异常进行指定方式的处理。
语法格式:
try{
可能出现异常的代码
}catch(异常类型1 e1){
处理异常
}catch(异常类型2 e2){
处理异常
}...
try:
捕获异常第一步。
catch:
捕获异常,对异常进行捕获处理,可能会有多个catch语句,但是要按照顺序,异常子类在上,异常父类在下。也可以处理不同类型的异常。
-获取异常的信息
通过使用Throwable类中定义的一些方法获取捕获到异常的信息
- public String getMessage();`
- public void printStackTrace();
异常捕获语句之finally
finally
通常和try…catch语句一起使用,表示的一定会执行的代码,通常用于释放资源。
- 如果和return一起使用的话,try中有return,finally的使用不会改变try里面的返回值
- 但是如果finally里面也有return语句的话,会执行finally里面的return语句,try里面的return就不会执行了。
public static void main(String[] args) {
int sum = sum(3, 6);
System.out.println("sum = " + sum); //10
}
public static int sum(int a,int b){
int h = 0;
try{
int c =a+b;
h = c+1;
return c;
}catch (Exception e){
System.out.println("e = " + e.getMessage());
}finally {
return h;
}
}
注意了:finally不能单独使用
只有当在try或者catch语句中调用退出JVM的方法,例如System.exit(0);方法时finally才不会执行。
自定义异常
吾日二省:
- 为啥要自定义异常呢?
异常千千万,但是你写的类碰见的异常总会比异常种类还要多。有些时候我们无法通过Java定义好的异常去解决我们的问题怎么办呢(年龄负数的问题、考试成绩负数的问题)?那我们就去自己创建一个异常类。 - 异常类如何定义呢?
那当然是①新建异常类,②继承尽可能匹配的异常,通常继承RuntimeException,③直接调用父类异常类的构造方法
异常的五大关键字
- 异常处理注意事项
编译期异常
必须处理,要么直接try…catch,要么声明在方法上,让调用者处理- 运行时异常被抛出可以不处理,这里建议处理。
- try中的语句要尽量小的范围。
- catch语句中异常类型尽量小,尽量精准。
- finally语句中有return语句的话,会一直返回finally中的语句,要避免。
- 父类中抛出多个异常,子类中不能抛出更大的异常。
有感