13.异常(throwable)

目录


Java专栏目录(点击进入…)



异常

异常分类

(1)Throwable类

所有的异常类型都是它的子类,它派生两个子类Error、Exception。

(2)Error类

表示仅靠程序本身无法恢复的严重错误(内存溢出动态链接失败、虚拟机错误),应用程序不应该抛出这种错误(一般由虚拟机抛出)

(3)Exception类

由Java应用程序抛出和处理的非严重错误

(4)运行时异常(RuntimeException及其子类)

(5)非运行异常(Checked异常)

除了运行时异常外的其它由Exception继承来的异常类

在这里插入图片描述

Exception异常类型

异常说明
Exception异常层次结构的根类(父类)
ArithmeticException算术错误
ArrayIndexOutOfBoundsException数组下标越界
NullPointerException尝试访问Null对象成员
ClassNotFoundException不能加载所需的类
InputMismatchException欲的到的数据类型与实际不匹配
IllegaArgumentException方法接收到非法参数
ClassCastException对象强制转换出错
NumberFormatException数字格式化异常

运行时异常(RuntimeException)

异常说明
ClassCastException类型转换出错
NullPointerException空指针异常
ArrayIndexOutOfBoundsException数组下标越界
ArithmeticException算术异常
ArrayStoreException数组中包含不兼容的值抛出的异常
NumberFormatException字符串转换为数字抛出的异常
IllegaArgumentException方法接收到非法参数
FileSystemNotFoundException文件系统未找到异常
SecurityException安全性异常
StringIndexOutOfBoundsException字符串索引超出范围
NegativeArraySizeException数组长度为负异常

非运行时异常(Checked)

RuntimeException类及其子类异常以外的异常,是必须进行处理的异常,如果不处理,程序就不能编译通过

异常说明
ClassNotFoundException未找到相应类异常
SQLException操作数据库异常类
IOException输入/输出流异常
TimeoutException操作超时异常
FileNotFoundException文件未找到异常

处理异常

try-catch

凡是可能抛出异常的语句,都可以用try-catch捕获。把可能发生异常的语句放在try { }中,然后使用catch捕获对应的Exception及其子类

try-catch-finally
关键字描述
try可能出现异常的代码块,出现异常,则跳转到catch(必须)
catch输出异常信息(可选)
finally必须执行的代码(可选)

catch块中,调用异常对象的方法输出异常信息
(1)void printStackTrace():输出异常的堆栈信息
(2)String getMessage():返回异常信息描述字符串

多重catch

try-catch-catch-finally

JVM在捕获到异常后,会从上到下匹配catch语句,匹配到某个catch后,执行catch代码块,然后不再继续匹配
(1)使用多重catch时,catch块的排列顺序必须从子类到父类,最后一个一般是Exception
(2)运行时,只执行一个catch块
(3)存在多个catch的时候,catch的顺序非常重要:子类必须写在前面

finally

finally语句保证了有无异常都会执行,它是可选的

(1)finally语句不是必须的,可写可不写
(2)finally总是最后执行

如果没有发生异常,就正常执行try {}语句块,然后执行finally
如果发生了异常,就中断执行try {}语句块,然后跳转执行匹配的catch语句块,最后执行finally


finally和return的执行顺序

(1)执行try中的return时
try {
	return 1;
} finally {
	System.out.println("finally模块被执行");
}

finally语句在return语句执行之后return返回结果之前执行

执行结果:先输出finally模块被执行,然后执行在return 1;

(2)执行catch中的return时
try {
	int a = 8 / 0;
	return 1;
} catch (Exception e) {
	return 2;
} finally {
	System.out.println("finally模块被执行");
}

程序是从try代码块或者catch代码块中返回时,finally中的代码总会执行
而且finally语句在return语句执行之后return返回之前执行的。可以使用编译器的Debug功能查看详细过程
执行结果:try语句报错,执行输出finally模块被执行,catch中的return 2

(3)finally也有return的时候
try {
	int a = 8 / 0;
	return 1;
} catch (Exception e) {
	return 2;
} finally {
	System.out.println("finally模块被执行");
	return 2;
}

当finally有返回值时,会直接返回。不会再去返回try或者catch中的返回值

(4)finally中对于返回变量做的改变会影响最终的返回结果吗
public int show() {
	int result = 0;
	try {
		return result;
	} finally {
		System.out.println("finally模块被执行");
		result = 1;
	}
}

并不会改变返回的内容

当返回的变量的类型是引用类型时,结果也是一样的

public Object show() {
	Object obj = new Object();
	try {
		return obj;
	} finally {
		System.out.println("finally模块被执行");
		obj = null;
	}
}

如果try和catch的return是一个变量时且函数的是从其中一个返回时,后面finally中语句即使有对返回的变量进行赋值的操作时,也不会影响返回的值。


声明异常、抛出异常

1.声明异常(throws)

(1)通过try-catch捕获并处理异常
(2)通过throws继续声明异常。如果调用者不打算处理异常,则可以继续通过throws声明异常,让上一级调用者处理异常,main()方法声明的异常将由Java虚拟机来处理

2.抛出异常(throw,手动抛出异常)

在代码中需要抛出异常时,尽量使用JDK已定义的异常类型。例如:参数检查不合法,应该抛出IllegalArgumentException

static void process1(int age) {
	if (age <= 0) {
		throw new IllegalArgumentException();
	}
}
// 创建自定义异常类
class MyException extends Exception {
	// 父类方法
	public MyException(String ErrorMessagr) {
		super(ErrorMessagr); 
	}
}

// 使用自定义异常
throw new MyException("发生了自定义的异常");  

throw和throws不同

throwthrows
作用不同用于程序中抛出异常用于声明在该方法内可能抛出的异常
使用位置不同位于方法体内部,可以作为单独语句使用必须跟在方法参数列表的后面,不能单独使用
内容不同抛出一个异常对象,而且只能是一个后面跟异常类,而且可以跟多个异常类

自定义异常

自定义新的异常类型,但是,保持一个合理的异常继承体系是非常重要的

一个常见的做法是自定义一个BaseException作为“根异常”,然后,派生出各种业务类型的异常自定义新的异常类型,但是,保持一个合理的异常继承体系是非常重要的

BaseException需要从一个适合的Exception派生,通常建议从RuntimeException派生,自定义的BaseException应该提供多个构造方法

public class BaseException extends RuntimeException {
    public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

    public BaseException(String message) {
        super(message);
    }

    public BaseException(Throwable cause) {
        super(cause);
    }
}

上述构造方法实际上都是原样照抄RuntimeException。这样,抛出异常的时候,就可以选择合适的构造方法。通过IDE可以根据父类快速生成子类的构造方法

其他业务类型的异常就可以从BaseException派生:

public class UserNotFoundException extends BaseException {}
public class LoginFailedException extends BaseException {}

(1)抛出异常时,尽量复用JDK已定义的异常类型
(2)自定义异常体系时,推荐从RuntimeException派生“根异常”,再派生出业务异常
(3)自定义异常时,应该提供多种构造方法


1.继承Exception

自定义异常类继承自Exception类;需要检查编译期异常和运行期异常

// 或者继承RuntimeException(运行时异常) 
public class MyException extends Exception {
	private static final long serialVersionUID = 1L;
	
	public MyException() { // 提供无参数的构造方法
	}

	// 提供参数构造,把参数传递给Throwable的带String参数的构造方法
	public MyException(String message) {
		super(message);
	}
}

serialVersionUID作用:序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性

2.抛出自定义异常方法

写一个测试分数的方法类:这里面是抛出一个自己写的异常类

public class CheckScore { 
	// 检查分数合法性的方法check() 如果定义的是运行时异常就不用抛异常了 
	public void check(int score) throws MyException {
		if (score > 120 || score < 0) {
			// 分数不合法时抛出异常,new一个自定义异常类 
			throw new MyException("分数不合法,分数应该是0--120之间");
		} else { 
			System.out.println("分数合法,你的分数是" + score); 
		} 
	} 
} 

3.测试自定义异常

写一个测试分数,如果有异常,要捕获,不要抛出了

public class Student { 
	public static void main(String[] args) { 
		Scanner sc = new Scanner(System.in); 
		int score = sc.nextInt(); 
		CheckScore check = new CheckScore(); 
		try { 
			check.check(score); 
		} catch (MyException e) {
			// 用自定义异常类来捕获异常 
			e.printStackTrace();
		}
	}
}

自定义异常类继承自RuntimeException类;只需要检查运行期异常

仅仅为了提示,又不想自定义一个Exception,可以用RuntimeException。这个可以抛出异常,并准确定位,缺点是不能处理这个异常,自定义异常的话可以捕获并且处理。


使用断言(了解)

断言(Assertion)一种调试程序的方式。在Java中,使用assert关键字来实现断言
(1)断言是一种调试方式,断言失败会抛出AssertionError,只能在开发和测试阶段启用断言
(2)对可恢复的错误不能使用断言,而应该抛出异常
(3)断言很少被使用,更好的方法是编写单元测试

语法

语句assert x >= 0;即为断言,断言条件x >= 0预期为true。如果计算结果为false,则断言失败,抛出AssertionError

使用assert语句时,还可以添加一个可选的断言消息

assert x >= 0 : "x must >= 0";

这样,断言失败的时候,AssertionError会带上消息x must >= 0,更加便于调试

Java断言的特点:断言失败时会抛出AssertionError,导致程序结束退出。因此,断言不能用于可恢复的程序错误,只应该用于开发和测试阶段

对于可恢复的程序错误,不应该使用断言

void sort(int[] arr) {
    assert arr != null;
}

应该抛出异常并在上层捕获

void sort(int[] arr) {
	if (arr == null) {
	        throw new IllegalArgumentException("array cannot be null");
	}
}

一个简单的断言

public static void main(String[] args) {
    int x = -1;
    assert x > 0;
    System.out.println(x);
}

开启断言

JVM默认关闭断言指令,即遇到assert语句就自动忽略,不执行;也就是上方断言不会抛出异常AssertionError

选择地对特定地类启用断言,命令行参数是:-ea:com.itranswarp.sample.Main,表示只对com.itranswarp.sample.Main这个类启用断言。
或者对特定地包启用断言,命令行参数是:-ea:com.itranswarp.sample…(注意结尾有3个.),表示对com.itranswarp.sample这个包启动断言。实际开发中,很少使用断言;更好的方法是编写单元测试

要执行assert语句,必须给Java虚拟机传递-enableassertions(可简写为-ea)参数启用断言

java -ea Main.java
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未禾

您的支持是我最宝贵的财富!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值