一、异常
1.1 什么是异常
异常(exception),就是程序中出现的错误
1.2 之前见过的异常
算术异常 ArithmeticException
数组下标越界异常 ArrayIndexOutOfBoundsException
空指针异常 NullPointerException
类转换异常 ClassCastException
解析异常 ParseException
堆栈内存溢出错误 StackOverflowError
1.3 异常的分类
在java中,所有的错误(error)或者异常(exception)都是默认可以被抛出到控制台被看见的,这样才能方便解决
所以,可抛出就是所有异常/错误的共性,所以java就设计了一个父类: Throwable;
java的异常(Exception)和错误(Error)就是Throwable的子类
Error: 只能抛出,不能捕获的异常就是错误!!
ps: Error一般都是严重问题,遇到必须里面立马解决,所以不应该捕获....
Exception: 可以抛出,也可以捕获的异常
Exception异常又可再细分
运行时异常
凡是RuntimeException及其子类的都是运行时异常
运行时异常,默认无需处理
编译期异常
除了Runtime以前子类都是编译期
编译期,默认是要强制处理(怎么处理都行,但是要处理)
二、异常的处理
异常的处理只有两种方式
抛出异常
捕获异常
2.1 抛出异常
什么是抛出异常?
目前为止任何异常,默认的处理方式都是抛出
所谓抛出异常就是直接将错误信息打印到控制台
怎么声明抛出异常?
如果是运行时异常,不用处理,默认就会自动抛出
如果是编译期异常,想要抛出异常,语法是
位置: 在方法参数列表后,{}前
写: throws 异常类名,类名2,...
public static void main(String[] args)
throws ArithmeticException,ArrayIndexOutOfBoundsException {
}
声明抛出异常后,什么效果?
如果代码一切正常,虽然声明的有抛出异常,也不会在控制台打印异常信息
如果代码真的有异常,声明了抛出异常,
1错误信息就会在控制台打印
2抛出异常后,后续代码不再执行
// 以下代码中19行报错,4,5,6,7.7都不再执行
public static void main(String[] args) {
System.out.println(0.0 );
m1();
System.out.println(7.7 );
}
public static void m1() {
System.out.println(1);
System.out.println(2);
m2();
System.out.println(5);
System.out.println(6);
}
public static void m2(){
System.out.println(3);
System.out.println(4 / 0);
System.out.println(4 );
}
运行时异常,可以主动抛出,也可以不用抛出
但是如果方法抛出的是编译期异常,谁调用这个抛出编译期异常的方法,那么哪个方法就得处理!!
特殊的 ,关于抛出异常在重写方法时也有要求.....
子类的重写后,方法抛出的异常范围要 <= 父类的异常范围
特殊的,如果父类方法没有抛出异常,那么子类方法内有异常也不能抛出,只能捕获
总结抛出异常
哪里抛出的?
方法签名上只是声明可能抛出的异常类型
方法体内,执行到某一行代码,如果有错才会抛出
抛给谁?
如果有异常抛出,谁调用抛给哪个方法
最终抛出到main
main方法抛出到jvm,jvm打印到控制台
抛到哪?
抛出到控制台
抛出了什么?
异常的线程信息
异常类型
异常原因
异常位置
2.2 试图捕获异常
试图捕获(try-catch)
语法
try{
// 可能会有报错的代码
} catch (异常类名 对象名){
// 如果抓到异常,执行这里
}
执行流程:
-
代码正常运行,执行try后
-
进入try执行,如果try内一切正常,catch就跳过不执行
-
如果try内有异常
-
try内异常处后续不执行
-
catch捕获到异常后,就会执行
-
-
无论try内有无异常,try-catch后的代码都能运行
public static void main(String[] args) {
System.out.println(1);
System.out.println(2);
try {
System.out.println(3);
System.out.println(4.1);
System.out.println(4/0);
System.out.println(4.2);
}catch (ArithmeticException ae){
System.out.println("捕获到了异常:"+ae );
}
System.out.println(5);
}
特殊情况1: 出现的异常和捕获的异常不一致,try-catch失效
特殊情况2: 允许同时声明catch多个异常
特殊情况3: 如果真需要catch写多个异常,用于去捕获多种异常的话,建议这么写,写一个Exception,直接捕获最大类异常,这样所有异常子类都可以捕获
捕获异常与抛出异常最大的区别就是,抛出异常后,代码还能继续执行...
如下,代码能运行 1234789...
public class Demo3 {
public static void main(String[] args) {
System.out.println(1);
m1( );
System.out.println(9 );//运行结果:1 2 3 4 (异常信息) 7 8 9
}
public static void m1() {
System.out.println(2);
m2( );
System.out.println(8 );
}
public static void m2() {
System.out.println(3);
try {
System.out.println(4);
System.out.println(5 / 0);
System.out.println(6);
} catch (Exception e) {
System.out.println(e);
}
System.out.println(7 );
}
}
2.3 finally
finally配合try-catch使用
单词释义: 最终的
作用: 最终一定会执行,即放在finally里面的代码,之前无论有无报错,无论是抛出还是捕获,fianlly里面的代码最终一定能执行
try{
// ...
}catch(Exception e){
}finally{
}
或者
try{
// ...
}finally{
}
finally一般用在 关闭流/通道的场景中,用于及时释放资源
例如: IO流,jdbc连接
三、异常的产生(创建)
之前学习的抛出异常,捕获异常都是异常出现了之后的解决方案.
现在,要学习如何产生异常...
产生异常,两个步骤:
创建异常对象
通过throw 抛出异常对象
需要注意,throw抛出的是编译期异常,需要强制处理
throw抛出的是运行时异常,可以默认不用处理
public class Demo5 {
public static void main(String[] args) throws ParseException {
String s = parseBirthday2("41011120240101123");
System.out.println(s );
}
/**
需求: 解析身份证号中的生日,要求身份证号长度18位,
否则抛出异常
*/
public static String parseBirthday(String idNumber) throws ParseException{
if (idNumber.length() == 18) {
return idNumber.substring(6,14);
}
// 抛出异常(编译期异常,需要处理)
throw new ParseException("生日解析出错,格式不正确",1);
}
public static String parseBirthday2(String idNumber) {
if (idNumber.length() == 18) {
return idNumber.substring(6,14);
}
// 抛出异常(运行时异常,不用管)
throw new RuntimeException("2222生日解析出错,格式不正确");
}
}
总结throw和throws...
throw | throws | |
---|---|---|
位置 | 方法体内 | 方法签名上 |
后面写什么 | 跟一个异常对象 | 跟异常类名,且允许多个 |
作用 | 一定会抛出异常的 | 声明抛出的异常类型,但是不一定有异常 |
四、异常的API
查阅API,发现所有异常,方法基本上都直接继承自Throwable类,常用的方法
构造方法 Throwable(String message)
所有的异常对象,都通过有参构造将异常信息传递给Throwable父类
且这个message就是异常信息
String getMessage()
void printStackTrace()
String toString()
public class Demo6 {
public static void main(String[] args) {
try {
System.out.println(1/0 );
}catch (ArithmeticException ae) {
ae.printStackTrace();
}
}
}
五、自定义异常
自定义异常实现步骤
创建一个异常类,命名规范XxxxException
继承一个合适的父类
继承编译期异常
继承运行时异常
设计一个有参构造,将异常信息参数通过super传递给父类异常
如何使用自定义异常类:
在需要使用的方法内部,通过throw+该异常对象,将其抛出
// 异常类
public class AgeOutOfBoundsException extends Exception{
public AgeOutOfBoundsException(String msg){
super(msg);
}
}
// 使用异常
public class Dog {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws Exception{
if (age <= 0 || age >= 100) {
throw new AgeOutOfBoundsException("年龄"+age+"越界!");
}
this.age = age;
}
}