目录
1.关于异常
什么是异常:
- 异常字面意思就是‘意外’,‘例外’的意思,也就是非正常的意思。
- 异常本质上是程序上的错误。
为什么要处理异常:
如果异常不处理的话,程序就会强行终止,不会继续运行下去。假若程序在运行期间出现
了错误,如果置之不理,程序便会终止或直接导致系统崩溃,显然这不是我们希望看到的结果。
程序中的异常:
-错误在我们编程的过程中会经常发生的,包括编译期间和运行期间的错误。
- 在编译期间出现的错误有编译器帮我们一起修正,然而运行期间的错误便不是编译器力
所能及的了,并且运行期间的错误往往是难以入料的。
- 事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误。比如使用空的引
用、数组下标越界、内存溢出错误等,这些都是意外的情况。
例如:(运行期间的错误,即编译器不能识别的)
空指针异常(NullPointerException):
public class Demo12 {
public static void main(String[] args) {
//创建一个空数组
String str = null;
//输出数组的长度
System.out.println(str.length());
}
}
数组访问时下标越界(ArrayIndexOutOfBoundsException):
public class Demo12 {
public static void main(String[] args) {
int[] sry = {1, 2, 3};
for (int i = 0; i <=3; i++) {
System.out.println(sry[i]);
}
}
}
上述程序中,定义了一个长度为3的整型数组,在遍历的过程中有出现了sry[3],但是数组并没有这个元素。
算数运算时除数为0(ArithmeticException):
public class Demo12 {
public static void main(String[] args) {
int one = 12;
int two = 0;
System.out.println(one/two);
}
}
当除数为0时,编译器并不能发现错误,只在运行的时候会报错。
类型转换时无法正常转型(ClassCastException):
class Animal{
}
class Dog extends Animal{
}
class Cat extends Animal{
}
public class Demo12 {
public static void main(String[] args) {
Animal a1 = new Dog();
Animal a2 = new Cat();
Dog d1 = (Dog)a1;
Dog d2 = (Dog)a2;
}
}
上述程序中,a2是Cat类的实例,与Dog类无关。
异常的分类
在Java中,通过Throwable及其子类描述各种不同的异常类型(Throwable类是所有异常的父类)。
Throwable有两个重要的子类:Exception和Error。注意:Error异常一般处理不了(次异常与硬件有关)。我们一般处理的都是Exception。
unchecked exception(非检查异常),也称运行时异常(RuntimeException),比如常见的NullPointerException、IndexOutOfBoundsException。对于运行时异常,java编译器不要求必须进行异常捕获处理或者抛出声明,由程序员自行决定。
checked exception(检查异常),也称非运行时异常(运行时异常以外的异常就是非运行时异常),java编译器强制程序员必须进行捕获处理,比如常见的IOExeption和SQLException。对于非运行时异常如果不进行捕获或者抛出声明处理,编译都不会通过。
1) Error
Error是程序无法处理的错误,表示运行应用程序中比较严重的问题。大多数错误与代码编写者执行的操作无关,而表示代码运行时JVM虚拟机出现了问题。
这些错误是不可查的,因此我们在编写的过程中不需要关心这类的异常。
2)Exception
Exception是程序本身可以处理的异常。Exception异常通常包括checked exception 和 unchecked exception。
2.异常处理
·Java中应用程序中,异常处理机制为:抛出异常、捕捉异常。
- 抛出异常:
- 当一个方法出现错误引发异常时,方法创建异常对象并交付运行时的系统
- 异常对象中包含了异常类型和异常出现时的程序状态等异常信息。
- 运行时系统负责寻找处置异常的代码并执行
- 捕获异常
- 在方法抛出异常之后,运行时系统间转换为寻找合适的异常处理器。
- 运行时系统从异常的方法开始,依次回查调用栈中的方法,当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。
- 当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止
- 对于运行时异常,Java技术所要求的异常处理方式有所不同
- 总体来说,Java规定:对于可查异常必须捕捉、或声明抛出。允许忽略不可查的RuntimeException和Error。
- 总的来说,异常总是先被抛出,后被捕获的。
异常时通过5个关键字来实现的:try、catch、finaly、throw、throws.
try -catch-finally的使用
代码格式如下:
public void method(){
try{//代码段1
//产生异常的代码段2
}catch(异常类型 ex){//对异常进行处理的代码段3
}finally{//代码段4
}
}//注意try块后面必须跟0个或多个catch块,或者直接跟finally块。
被try包围的代码说明这一段代码可能会发生异常,一旦发生异常,异常就会被catch捕捉到,然后需要在catch块中进行异常的处理。
public class Demo12 {
public static void main(String[] args) {
System.out.println(123);
try {
int sum = 12/0;
System.out.println(sum);
}catch(ArithmeticException e) {
System.out.println("除数不能为0");
//打印异常的堆栈信息,并不会影响程序的运行
// e.printStackTrace();
}finally {
System.out.println(456);
}
System.out.println(789);
}
}
上述代码中,显而易见int sum = 12/0;肯定会报错的,当运行到这句话的时候,异常就会被catch捕获到,而后就会运行catch块中的语句对异常进行处理。处理过后并不会影响之后的程序运行。若不处理JVM虚拟机就会崩溃,从而程序强行终止。finally语句,除非是遇到Error异常或者JVM虚拟机被强行停止(System.exit(0)),否则finally块中的语句都会被执行(return也不会影响finally块的执行)。
输出结果:
- 实际应用中的经验与总结
- 处理运行时异常时,采用逻辑去合理规避同时辅助try-catch处理
- 在多重catch块后面,可以加一个catch(Exception)来处理可能会被遗落的异常
- 对于不确定的代码,也可以加上try-catch,处理潜在的异常
- 尽量去处理异常,切记只是简单的调用printStackTrace()去打印输出
- 具体如何处理异常,要根据不同的业务需求和异常类型去决定
- 尽量添加finally语句去释放占用资源
import java.util.InputMismatchException;
import java.util.Scanner;
public class TryDemo4 {
public static void main(String[] args) {
testOne();
}
public static void testOne(){
try {
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int a = sc.nextInt();
System.out.println("请输入第二个数字:");
int b = sc.nextInt();
int result = a / b;
System.out.println("result:" + result);
}catch(ArithmeticException e) {
System.out.println("请不要将除数至为0");
}catch (InputMismatchException e) {
System.out.println("只能输入数字");
}catch (Exception e) {
e.printStackTrace();
}finally {
System.out.println("===程序结束===");
}
System.out.println(123);
}
}
运行结果1(当b为0时):
运行结果2(正常输入):
运行结果3(当输入字母时):
throw和throws关键字
在Java中还提供了另一种异常处理方式即抛出异常,顾名思义,也就是一旦发生异常,我们就把这个异常抛出去,让调用者去进行处理,自己不进行具体处理,此时需要用到throw和throws关键字。
throws语句在格式:
public void method() throws 异常1,异常2,异常3,...,异常N{
可能产生异常的代码
}
代码如下:
import java.util.InputMismatchException;
import java.util.Scanner;
public class TryDemo5 {
public static void main(String[] args) {
try {
testOne();
} catch(ArithmeticException e) {
System.out.println("请不要将除数至为0");
}catch (InputMismatchException e) {
System.out.println("只能输入数字");
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(123);
}
/**
* 如果抛出的是非检查异常(运行时异常RuntimeException),可以用文档注释标注异常信息
* @return double result
* @throws InputMismatchException
* @throws ArithmeticException
*/
public static double testOne() throws InputMismatchException, ArithmeticException,Exception{
Scanner sc = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int a = sc.nextInt();
System.out.println("请输入第二个数字:");
int b = sc.nextInt();
return a / b;
}
}
当然还可以使用throw关键字来手动抛出异常。例如:
创建一个hotelRule类,只允许18岁以上或者80岁以下的人来住。代码如下:
public class Demo12 {
public static void main(String[] args) {
try {
hotelRule();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static void hotelRule() throws Exception {
Scanner sc = new Scanner(System.in);
System.out.println("请输入客户年龄:");
int age = sc.nextInt();
if(age < 18 || age > 80) {
throw new HotelException();
}else {
System.out.println("入住成功");
}
}
}
e.getMessage()方法获取的时Exception类构造方法的参数。
自定义一个异常类,并且调用父类的构造方法
public class HotelException extends Exception{
public HotelException() {
super("由于你年龄问题,无法入住");
}
}
1)对代码块用try..catch进行异常捕获处理;
2)在 该代码的方法体外用throws进行抛出声明,告知此方法的调用者这段代码可能会出现这些
异常,你需要谨慎处理。此时有两种情况:
如果声明抛出的异常是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获
或者继续向上层抛出异常。
如果声明抛出的异常是运行时异常,此方法的调用者可以选择地进行异常捕获处理。
3)在代码块用throw手动抛出一个异常对象,此时也有两种情况,跟2)中的类似:
如果抛出的异常对象是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或
者继续向上层抛出异常。
如果抛出的异常对象是运行时异常,此方法的调用者可以选择地进行异常捕获处理。
(如果最终将异常抛给main方法,则相当于交给jvm自动处理,此时jvm会简单地打印异常信息)
代码执行:
自定义异常:
使用Java内置的异常类可以描述在编程时主线的大部分异常情况
也可以通过自定义异常描述待定业务产生的异常类型;
所谓自定义异常就是定义一个类,去继承Exception类或者它的子类
在继承当中使用异常的一些情况
- 父类的函数如果没有声明任何异常,那么子类重写的函数也不能声明
- 就算子类当中出现了编译时异常也只能进行捕获不能声明
- 子类的函数如果声明了编译时异常,那么子类重写的函数只能声明父类异常或父类异常的子类