1.Exception异常的分类
- 编译时异常:继承自Exception的异常或者其子类,没有继承RuntimeException。编译阶段就会报错,必须程序员处理,否则代码编译就不能通过。
- 运行时异常:继承自RuntimeException的异常或者其子类。该异常可以处理也可以不处理,编译阶段是不会报错的,但在运行阶段可能会出错,建议处理。
2.运行时异常
2.1 ArrayIndexOutOfBoundsException
int[] a = {1,2};
System.out.println(a[2]);//ArrayIndexOutOfBoundsException
2.2 NullPointerException
String s = null;
System.out.println(s);//null
System.out.println(s.length());//NullPointerException
2.3 ClassCastException
Object s = "abc";
Integer i = (Integer)s;//ClassCastException
2.4 ArithmeticException
System.out.println(2/0);//ArithmeticException
2.5 NoSuchElementException
ArrayList<String> arr = new ArrayList<>();
arr.add("1");
Iterator<String> iterator = arr.iterator();
iterator.next();
System.out.println(iterator.next())//NoSuchElementException
2.6 NumberFormatException
String s = "123abc";
Integer i = Integer.valueOf(s);//NumberFormatException
3.编译时异常
public class ExceptionDemo {
public static void main(String[] args) {//没有在main函数处throws ParseException
String time = "2020-5-25 14:27";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy:MM-dd HH:mm");
Date date = sdf.parse(time);//此处会编译异常
System.out.println(date);
}
}
4.异常的处理
4.1 异常的默认处理机制
- 默认会在出现异常的代码那里自动的创建一个异常对象,例如:ArithmeticException
- 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虛拟机
- 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据。
- 直接从当前执行的异常点干掉当前程序。
- 后续代码没有机会执行了,因为程序已经死亡。
小结:异常一旦出现,会自动创建异常对象,最终抛出给虚拟机,虚拟机只要收到异常,就直接输出异常信息,干掉程序!!
默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡!
4.2 编译时异常的处理方式
4.2.1: Throws(抛出异常)
public class ExceptionDemo {
public static void main(String[] args) throws Exception {//抛出异常
String time = "2020-5-25 14:27";
parseTime(time);
}
public static void parseTime(String s) throws Exception {//抛出异常
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date date = sdf.parse(s);
System.out.println(date);
}
}
在岀现编译时异常的地方层层把异常抛出去给调用者,调用者最终抛出给JVM虚拟机。
JVM虚拟机输出异常信息,直接干掉程序,这种方式与默认方式是一样的。
虽然可以解决代码编译时的错误,但是一旦运行时真的出现异常,程序还是会立即死亡!
这种方式并不好!
4.2.2: try-catch(捕获处理)
监视捕获处理异常企业级写法:
try {
//可能出现异常的代码!
} catch (Exception e){
e, printStackTrace();//直接打印异常栈信息
}
public class ExceptionDemo {
public static void main(String[] args) {
String time = "2020-5-25 14:27";
parseTime(time);
}
public static void parseTime(String s){
try{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date date = sdf.parse(s);
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Exception可以捕获处理一切异常类型!
小结:
第二种方式,可以处理异常,并且出现异常后,代码也不会死亡。
这种方案还是可以的。
但是从理论上来说,这种方式不是最好的,上层调用者不能直接知道底层的执行情况!
4.2.3: try-catch,throws
方式三:在出现异常的地方把异常一层一层地抛出给最外层调用者,最外层调用者集中捕获处理!!(规范做法)
编译时异常的处理方式三:底层出现的异常抛出给最外层调用者集中捕获处理。
public class ExceptionDemo {
public static void main(String[] args) {
try{
String time = "2020-5-25 14:27";
parseTime(time);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void parseTime(String s) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
Date date = sdf.parse(s);
System.out.println(date);
}
}
总结:这种方案最外层调用者可以知道底层执行的情况,同时程序在出现异常后也不会立即死亡,这是理论上最好的方案。
虽然异常有三种处理方式,但是实际中只要能解决你的问题,每种方式都又可能用到!!
4.3 运行时异常的处理方式
运行时异常编译阶段不报错,可以处理也可以不处理,建议处理!
运行时异常可以自动抛出,不需要我们手工抛出。
运行时异常的处理规范:直接在最外层(try-catch)捕获处理即可,底层会自动抛出!
public class ExceptionDemo {
public static void main(String[] args) {
try{
devide(3,0);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void devide(int a,int b) {
System.out.println(a/b);
}
}
5.finally
用在捕获处理的异常格式中的,放在最后面。
try {
//可能出现异常的代码!
}catch(Exception e){
e.printstackTrace();
} finally {
//无论代码是出现异常还是正常执行,最终一定要执行这里的代码!!
}
try:1次
catch:0-N次(如果有finally那么 catch可以没有!!)
finally:0-1次
finally的作用:可以在代码执行完毕以后进行资源的释放操作。
资源:实现Closeable接口的,自带close()关闭方法。
public class ExceptionDemo {
public static void main(String[] args) {
func();
}
public static void func() {
try{
System.out.println(10/0);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("====finally被执行====");
}
}
}
/*
java.lang.ArithmeticException: / by zero
at zbr.ExceptionDemo.func(ExceptionDemo.java:10)
at zbr.ExceptionDemo.main(ExceptionDemo.java:5)
====finally被执行====
*/
6.异常的注意事项
- 运行时异常可以被自动抛出,被抛出时可以不处理;编译时异常需手动抛出,必须处理。按照规范两种异常都该被处理。
- 重写方法时,子类声明抛出的异常类型必须是其父类抛出的异常类型或是其子类。
- 在try-catch后可以加finally代码块,其中的内容一定会被执行,通常用于资源回收操作。
7.自定义异常
7.1 自定义编译时异常
(编译时异常是编译阶段就报错,提醒更加强烈,一定需要处理!!)
-
定义一个异常类继承 Exception 。
-
重写构造器。
-
在出现异常的地方用 throw new自定义对象抛出!
class AgeException extends Exception { public AgeException() { super(); } public AgeException(String message) { super(message); } public AgeException(String message, Throwable cause) { super(message, cause); } public AgeException(Throwable cause) { super(cause); } protected AgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } public class ExceptionDemo { public static void main(String[] args) { try {//因为是编译时异常,所以需要对抛出的异常做处理 isAge(-1); } catch (AgeException e) { e.printStackTrace(); } } public static void isAge(int age) throws AgeException {//需要抛出异常 if (age<0||age>200) { throw new AgeException("非法年龄");//在输入的年龄不符合时会抛出一个自定义的异常对象 }else { System.out.println("年龄是:"+age); } } }
7.2 自定义运行时异常
(提醒不强烈,编译阶段不报错!!运行时才可能出现)
- 定义一个异常类继承 RuntimeException
- 重写构造器。
- 在出现异常的地方用 throw new自定义对象抛出!
class MyArithmeticException extends ArithmeticException {
public MyArithmeticException() {
super();
}
public MyArithmeticException(String s) {
super(s);
}
}
public class ExceptionDemo {
public static void main(String[] args) {
devide(1,0);
}
public static void devide(int a, int b) {
if (b==0) {
throw new MyArithmeticException("除数不能为0");
}else {
System.out.println(a/b);
}
}
}
//Exception in thread "main" zbr.MyArithmeticException: 除数不能为0
8.总结
- 异常可以处理代码问题,防止程序出现异常后死亡。
- 提高程序的健壮性和安全性。