异常处理
什么异常?
在生活中:平时上下班都平常,今天上班的时候,路上有一起车祸,导致你上班迟到。
所谓异常:就出现了出乎意料的事情。
在代码里:所谓异常也是程序在编写或者运行过程中出现了出乎意料的事情。
比如:你做3D彩票系统,你提示别人输入数字,结果别人输入的还是字符串,出现了crash。
再比如:你把元素放到ArrayList中,但是你给的下标超出了元素的个数,程序也会出现异常。
异常的分类
在Java中异常一共分为2大类:Error、Exception。
早整个异常体系了,有如下继承关系
Throwable 它是所有异常和错误的父类。
Error:它是Throwable的子类。 代表错误,一般指的是系统错误,或者虚拟机错误。错误例如:内存不足,JVM出现问题,也属于错误。错误往往不是程序员能解决的,所以无需解决。
Exception:也是Throwable的子类。 代表异常,异常分为2类,运行期异常,另外一类叫非运行期异常。
RunTimeException:运行期异常。编译期并不会报错,运行的时候才会报错。
其他:非运行期异常。编译期就报错。错误不处理,无法运行程序。
Error的示例
public static void main(String[] args) throws FileNotFoundException {
long[] arr = new long[1024*1024*100*4];//内存不足的错误
}
}
Exception的示例
RunTimeException示例
public class Test {
public static void main(String[] args) throws FileNotFoundException {
int[] arr1 = {1,2,3,4};
System.out.println(arr1[4]);//运行期异常, 数组下标越界
int m = 100;
int n = 0;
System.out.println(m / n );//运行期异常, 除数为0的异常
}
}
非运行期异常的示例
public class Test {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream fis = new FileInputStream("C:\\Users\\neal\\Desktop\\a.docx");//非运行期异常,文件没有找到异常
}
}
异常的处理
如果程序出现了异常,但是并没有对异常进行处理,虚拟机就会处理异常。虚拟机的处理方式就是结束出现异常的线程。在多线程的环境下,如果A线程出现了异常,只是结束A线程的运行,B,C等线程都能正常的执行。
异常的处理有2种方式:
方式一:try …catch…finally 直接处理异常
方式二:thows 抛出异常,交给这个方法的调用者处理。
如果有一个a方法,a方法内部可能出现异常,但是a方法内部通过thows抛出了异常,相当于告诉调用a方法的那段代码,a方法有可能有异常,但是我没有处理,交给调用a方法的代码去处理。
try…catch…finally
把有可能出现异常的代码,放到try代码块中。try的作用是试图找到和发现异常。一旦try代码块中有异常,会执行对应的catch代码块。
public class Test {
public static void main(String[] args) {
int m = 100;
int n = 0;
int result = 0;
int[] arr = {1,2,3};
try {
result = m / n;
int num = arr[3];
}catch(ArithmeticException e) {
// System.out.println(e.getMessage());
// System.out.println(e.getLocalizedMessage());
// System.out.println(e);
n = 1;
result = m / n;
}catch(ArrayIndexOutOfBoundsException e) {
int num = arr[0];
System.out.println(num);
}
System.out.println(result);
}
}
try中一旦发现异常,就会立即跳转到对应的catch块中进行异常处理。try中没有执行到的代码,不再执行。换句话说,try只能捕获第一个异常。
如果有多个异常,写多个try…catch…finally代码段。
多个catch是可以合并的。只需要把catch中的异常范围写的更大即可。
因为try一旦发现异常,就立即进入了catch块,导致try中的部分代码没法执行。异常处理提供了finally来解决这个问题。
finally也是一个代码块,无论try有没有异常,finally中的代码都会执行。
情形一:try中没有出现异常,catch不执行,finally执行。
情形二:try中出现异常,执行对应的catch,catch执行以后,执行finally。
所以可以把必须要执行的代码写在finally里面。
public class Test {
public static void main(String[] args) {
int m = 100;
int n = 10;
int result = 0;
int[] arr = {1,2,3};
try {
result = m / n;
int num = arr[2];
}catch(RuntimeException e) {
// System.out.println(e.getMessage());
// System.out.println(e.getLocalizedMessage());
// System.out.println(e);
n = 1;
result = m / n;
}finally{
System.out.println("我是finally");
}
System.out.println(result);
}
}
小节
Java中异常分为Error和Exception两类
Exception分为RuntimeException和非RuntimeException。
try…catch…finally可以处理异常。
try中写,可能出现的异常。
catch中写,如何处理异常。异常是可以分开捕获,异常还可以合起来捕获。
finally中写那些一定要执行的代码。
throws
throws是第二种异常处理的方式。这种处理方式,相当于自己没有做出处理,把处理的事情交给了它的上一级(调用者)来处理。
为什么要使用throws?
一般调用者更清楚异常出来以后应该做什么事情。
throws如何使用?
当有异常存在的时候,如果这个异常不适合你处理,适合你的上层(调用者)处理,你应该把这个异常处理的权利交给你的上层。上层就会得到这个异常,它有2种选择:第一,通过try…catch…finally处理异常,第二,继续上抛,抛给它的调用者。
public class TestException {
public static void main(String[] args) throws FileNotFoundException {
//readFile("C:\\Users\\neal\\Desktop\\微信小程序.md");
// try {
// readFile2("C:\\Users\\neal\\Desktop\\微信小程序.md");
// } catch (FileNotFoundException e) {
//
// }
readFile2("C:\\Users\\neal\\Desktop\\微信小程序.md");
}
public static void readFile2(String filePath) throws FileNotFoundException {
FileInputStream fis = new FileInputStream(filePath);
System.out.println(fis);
}
public static void readFile(String filePath) {
try {
FileInputStream fis = new FileInputStream(filePath);
} catch (FileNotFoundException e) {
System.out.println("打开文件失败,文件不存在");
e.printStackTrace();
}
}
}
throws的特点
throws本质上是在做一个声明,它在告诉调用者,我内部的代码可能有某种异常产生。用于警醒调用者。
throws出现在方法参数列表的后面,throws 后面跟着的是具体的异常的类名。
补充一句,throws 后面的异常可以是多个,多个异常之间用逗号隔开。
再补充一句,throws后面的异常可以合并为父异常。
throw
throw是个动词,它的作用是抛出一个具体的异常。
用法:throw 异常对象;
你平时代码里出错时控制台打印的这些异常信息,都是系统API里提前写了throw e的原因。
throw 和throws区别
- throw是用来抛出异常的。后面跟的是一个具体的异常对象。 throws是用来声明方法内可能会有异常。
- throw写在方法内,throws写在方法的参数列表后面。
自定义异常
虽然系统已经提供了很多写好的异常类,但是由于项目不同,需求不同等,所以可能出现的异常也不同(可能你出现的异常,系统之前没有定义过这样的类)。为了解决这个问题,系统提供了自定义异常的语法。
如何自定义异常
创建一个类,继承于某个异常类即可。
自定义一个运行期异常:
public class ChooseOutOfBoundsException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ChooseOutOfBoundsException(String message) {
super(message);
}
}
自定义异常的使用:
public class Test {
public static void main(String[] args) {
System.out.println("请输入您的选择(1-7):");
int n = getChoose();
System.out.println(n);
}
public static int getChoose() {
Scanner sc = new Scanner(System.in);
int choose = sc.nextInt();
if(choose > 7 || choose < 1) {
ChooseOutOfBoundsException e = new ChooseOutOfBoundsException("您选择的数字超出了范围[1,7]");
throw e;
}
return choose;
}
}
自定义一个非运行期异常
public class ChooseOutOfBoundsException2 extends Exception {
private static final long serialVersionUID = 1L;
public ChooseOutOfBoundsException2(String message) {
super(message);
}
}
使用非运行期异常
public class Test2 {
public static void main(String[] args) {
System.out.println("请输入1-7的数:");
int n = 0;
try {
n = getChoose();
} catch (ChooseOutOfBoundsException2 e) {
System.out.println("你真调皮");
//e.printStackTrace();
}
System.out.println(n);
}
public static int getChoose() throws ChooseOutOfBoundsException2 {
Scanner sc = new Scanner(System.in);
int choose = sc.nextInt();
if(choose > 7 || choose < 1) {
ChooseOutOfBoundsException2 e = new ChooseOutOfBoundsException2("选择超出范围[1,7]");
throw e;
}
return choose;
}
}