概念
在程序中,异常可能由程序员没有预料到的各种情况产生也可能由超出了程序员可控范围的环境因素产生,在Java中这些在程序运行时可能出现的一些错误称为异常
例题9.1:0可以作为除数吗?
public class Baulk {
public static void main(String[] args) {
int x=3/0;
System.out.println(x);
}
}
这个异常是算术异常,发生的错误是因为在算术表达式“3/0”中,0作为除数出现,系统不在执行下去,提前结束。这种情况就是所说的异常。
异常抛出
异常抛出后,如果不做任何处理,程序会终止
例题9.2:控制台输出“lili年龄是:20L”
public class Thundering {
public static void main(String[] args) {
//try {
String str="lili";
System.out.println(str+"年龄是:");
int age=Integer.parseInt("20L");
System.out.println(age);
}
}
从图片我们可以知道,“提示信息lili年龄是:”已经输出,可知该句代码之前没有异常而变量age没有输出,可知程序在执行类型转换代码时已经终止。
捕捉异常
java语言的异常捕捉结构由try、catch、finally3部分组成,语法如下:
try{//程序代码块
}
catch(Exceptiontype1 e){
//对Exceptiontype1的处理}
catch(Exceptiontype2 e){
//对Exceptiontype2的处理
}
........
finally{
//程序代码块
}
对9.1、9.2代码进行修改
9.1
//算数异常
public class Baulk {
public static void main(String[] args) {
try {
int x=3/0;
System.out.println(x);
}catch(ArithmeticException e) {
System.out.println("出现算术异常");
}
finally {
System.out.println("程序结束");
}
}
}
9.2
//数字格式异常
public class Thundering {
public static void main(String[] args) {
try {
String str="lili";
System.out.println(str+"年龄是:");
int age=Integer.parseInt("20L");
System.out.println(age);
}catch(NumberFormatException e) {
System.out.println();
System.out.println("不好意思,出现数字格式异常");
}finally {
System.out.println("程序结束");
}
}
}
只要系统捕捉到异常,那么就会进行异常处理然后在进行finally语句块。
如果系统捕捉到异常,没有进行异常处理,或者异常处理中没有这个异常,且有finally语句块,那么会输出无异常语句,然后输出finally语句块,最后才会表示异常没有得到处理。
由图片可知,异常处理的异常,并不是所发生的异常,所以发生的异常没有得到处理,仍会输出一大段英文异常。
如果try代码块中的代码有好几种异常,且异常处理完善了,那么,也只会执行第一个异常处理,其他的异常处理不会执行。这个叫及时止损,避免过多浪费。
finally语句块
完整的异常处理语句一定要包含finally语句,无论程序中有无异常发生,并且无论之前的try—catch语句块是否顺利执行完毕,都会执行finally语句,但在以下4种特殊情况下,不会执行:
在finally语句块中发生异常在前面的代码中使用了System.exit()退出程序
程序所在的线程死亡
关闭CPU
自定义异常
使用Java内置的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户只需继承Exception类即可自定义异常类。要使用自定义异常类,大体分为以下几个步骤:
- 创建自定义异常类
- 在方法中通过throw关键字抛出异常对象
- 如果在当前抛出异常的方法中处理异常,可以使用try-catch语句捕获并处理,否则在方法的声明处通过throws关键字指明要抛出给方法调用的异常,并继续进行下一步操作
- 在出现异常的方法调用者中捕获并处理异常
例题9.4:创建自定义异常
public class MyException extends Exception{//继承Exception类
public MyException(String message) {//构造方法
super(message);//继承父类构造方法
}
}
字符串String message是要输出的错误信息
例题9.5:自定义异常的抛出与捕捉
public class Tran {//创建类
static int avg(int num1,int num2) throws MyException {//定义方法,抛出异常
if(num1<0||num2<0) {//判断是否满足条件
throw new MyException("存在负数,无法计算");//错误信息
}if(num1>100||num2>100) {//判断是否满足条件
throw new MyException("大于100,无法计算");//错误信息
}
return (num1+num2)/2;//返回值
}
public static void main(String[] args) {
try {//try语句块处理可能出现异常的代码
int result=avg(132,56);//调用方法
System.out.println(result);
}catch(MyException e) {
System.out.println(e);//输出异常信息
}
}
}
在这段代码中如果if判断且不使用自定义异常,那么会输出值,但是这不是我们想要的结果。在这种情况下就要使用自定义异常达到我们想要的结果。
抛出异常
使用throws抛出异常
例题9.6:指明异常起源于何处
public class Shoot {//创建类
static void pop() throws NegativeArraySizeException{
int []arr=new int[-3];//创建数组
}
public static void main(String[] args) {
try {//try语句处理异常信息
pop();//调用方法
}catch(NegativeArraySizeException e){
System.out.println("pop()方法抛出的异常");//输出异常信息
}
}
}
使用throws关键字将异常抛给上一级后,如果不想处理该异常,可以继续向上抛出,但最终要有能够处理该异常的代码
使用throw抛出异常
throw关键字通常用于方法体中,并且抛出一个异常对象。程序在执行到throw语句时立即终止,它后面的语句都不执行。通过throw抛出异常后,如果想在上一级代码中捕获并处理异常则需要在抛出异常的方法中使用throws关键字在方法的声明中指明要抛出的异常;如果要捕捉throw抛出的异常则,必须使用try-catch语句块。
例题9.8:使用throw关键字捕捉自定义异常
public class Captor {//创建类
static int quotient(int x,int y)throws MyException{//定义方法,抛出异常
if(y<0) {//判断
throw new MyException("除数不能是负数");//异常信息
}
return x/y;//返回值
}
public static void main(String[]arg) {//主函数
try {//try语句块,可能发生异常的语句
int result=quotient(3,-1);//调用方法
}catch(MyException e){//处理自定义异常
System.out.println(e);//输出异常信息
}catch(ArithmeticException e){//处理ArithmeticException异常
System.out.println("除数不能为0");//输出异常信息
}catch(Exception e){//处理其他异常
System.out.println("程序发生了其他的异常");//输出异常信息
}
}
}
在方法体中发现异常,会抛给方法,方法抛出到调用它的地方,再进行异常处理
如果传入参数y小于0,为负数则执行自定义异常处理
如果传入参数y等于0,为0则执行ArithmeticException异常处理
根据异常抛出的情况执行相对应的异常处理
由于Exception是所有异常类的父类,如果将catch(Exception)语句块放在其他两个语句块前面,后面的语句块将永远得不到执行,也就无意义了,所以catch语句的顺序不可调换
Java中的异常类思维导图
异常的使用规则
java异常强制用户去考虑程序的强健性和安全性。
异常处理不应用来控制程序的正常流程其主要作用是捕获程序在运行时发生的异常并进行相应的处理。处理异常时可遵行以下几条原则:
在当前方法声明中使用try-catch语句捕获异常一个方法被覆盖时,覆盖它的方法必须抛出相同的异常或异常的子类
如果父类抛出多个异常,则覆盖方法必须抛出那些异常的子集,不能抛出新异常