异常处理
异常处理
异常概述、体系
什么是异常?
- 异常是程序在"编译"或者 “执行” 的过程中可能出现的问题,注意:语法错误不算在异常体系中。
- 比如:数组索引越界、空指针异常、日期格式化异常,等...
为什么要学习异常
- 异常一旦出现了,如果没有提前处理,程序就会退出JVM虚拟机而终止。
- 研究异常并且避免异常,然后提前处理异常,体现的是程序的安全,健壮性
public class ExceptionDemo {
public static void main(String[] args) {
int[] arr = {1,2,3};
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[3]);//ArrayIndexOutOfBoundsException
System.out.println("-------程序截止-----");
}
}
output:
1
2
Exception in thread "main" 3
java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at com.csl.d3_exception.ExceptionDemo.main(ExceptionDemo.java:9)
异常体系
Error:
- 系统级别问题、JVM退出等,代码无法控制。
Exception:java.lang包下,称为异常类,他表示程序本身可以处理的问题。
- RuntimeException及其子类:运行时异常,编译阶段不会报错。(空指针异常,数组索引越界异常)
- 除RuntimeException之外的所有异常:编译时异常,编译期必须处理的,否则程序不能通过编译。(日期格式化异常)。
编译时异常和运行时异常
常见运行时异常
运行时异常
- 直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。
运行时异常示例
- 数组索引越界异常:ArrayIndexOutOfBoundsException
- 空指针异常:NullPointerException,直接输出没有问题,但是调用空指针的变量的功能就会报错。
- 数学操作异常:ArithmeticException
- 类型转换异常: ClassCastException
- 数字转换异常:NumberFormatException
**运行时异常:一般是程序员业务没有考虑好或者是编程逻辑不严谨引起的程序错误,
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("程序开始。。。。");
/**1.数组索引越界异常:ArrayIndexOutOfBoundsException*/
int[] arr = {1,2,3};
System.out.println(arr[2]);
//System.out.println(arr[3]);ArrayIndexOutOfBoundsException
/**2.空指针异常:NullPointerException,直接输出没有问题,但是调用空指针的变量功能就会报错*/
String name = null;
System.out.println(name);//null
// System.out.println(name.length());//NullPointerException
/**3.类型转换异常:ClassCastException。*/
Object a = 23;
//String s = (String)a;
//System.out.println(s);//ClassCastException
/**4.数学操作异常:ArithmeticException*/
// int c = 1/0;
// System.out.println(c);//ArithmeticException: / by zero
/**5.数字转换异常:NumberFormatException*/
// String number = "23a";//NumberFormatException
// Integer in = Integer.valueOf(number);
// System.out.println(in);
System.out.println("程序结束。。。");
}
}
常见编译时异常
编译时异常
- 不是RuntimeException或者其子类的异常,编译阶段就报错,必须处理,否则代码不通过。
编译时异常示例
String date = "2022-02-17 10:45:40";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
编译时异常的作用是什么:
- 是担心程序员的技术不行,在编译阶段就爆出一个错误,目的在于提醒不要出错!
- 编译时异常是可遇不可求。
编译时异常的特点
- 编译时异常:继承自Exception的异常或者其子类
- 编译阶段报错,必须处理,否则代码不通过。
异常的默认处理流程
- 默认会在出现异常的代码那里自动的创建一个异常对象:ArithmeticException。
- 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机
- 虚拟机接收到异常对象后,现在控制台直接输出异常栈信息数据
- 直接从当前执行的异常点干掉当前程序
- 后续代码没有机会执行了,因为程序已经死亡
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("程序开始。。。");
chu(10, 0);
System.out.println("程序结束、、、");
}
public static void chu(int a, int b) {
System.out.println(a);
System.out.println(b);
int c = a / b;
System.out.println("结果是:" + c);
}
}
output:
程序开始。。。
10
0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at com.csl.d4_exception_javac.ExceptionDemo.chu(ExceptionDemo.java:13)
at com.csl.d4_exception_javac.ExceptionDemo.main(ExceptionDemo.java:6)
默认的异常处理机制并不好,一旦真的出现异常,程序立即死亡。
编译时异常的处理机制
编译时异常是编译阶段就报错的,所以必须处理,否则代码根本无法通过
编译时异常的处理形式有三种:
- 出现异常直接抛出去给调用者,调用者也继续抛出去
- 出现异常自己捕获处理,不麻烦别人
- 前两者结合,出现异常直接抛出去给调用者,调用着捕获处理。
异常处理方式1——throws
- throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理。
- 这种方式并不好,发生异常的方法自己不处理异常,如果异常最终抛出去给虚拟机将引起出现死亡。
抛出异常格式:
方法 throws 异常1,异常2,异常3 ..{}
规范做法:
方法 throws Exception{}
public class ExceptionDemo1 {
public static void main(String[] args) throws Exception {
System.out.println("程序开始。。。。");
parseTime("2022-01-17 11:07:02");
System.out.println("程序结束。。。。");
}
private static void parseTime(String date) throws Exception{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
InputStream in = new FileInputStream("E:/meinv.jpg");
}
}
output:
程序开始。。。。
Mon Jan 17 11:07:02 CST 2022
Exception in thread "main" java.io.FileNotFoundException: E:\meinv.jpg (系统找不到指定的文件。)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at com.csl.d4_exception_javac.ExceptionDemo1.parseTime(ExceptionDemo1.java:20)
at com.csl.d4_exception_javac.ExceptionDemo1.main(ExceptionDemo1.java:11)
异常处理方式2——try...catch...
- 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理。
- 这种方式还可以,发生异常的方法自己独立完成异常的处理,出现可以继续往下执行。
public class ExceptionDemo1 {
public static void main(String[] args) {
System.out.println("程序开始。。。。");
parseTime("2022-01-17 11:07:02");
System.out.println("程序结束。。。。");
}
private static void parseTime(String date) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
} catch (ParseException e) {
//解析出现了问题
System.out.println("出现了解析时间异常!");
}
try {
InputStream in = new FileInputStream("E:/meinv.jpg");
} catch (FileNotFoundException e) {
System.out.println("你的文件找不到");
}
}
}
output:
程序开始。。。。
Mon Jan 17 11:07:02 CST 2022
你的文件找不到
程序结束。。。。
public class ExceptionDemo1 {
public static void main(String[] args) {
System.out.println("程序开始。。。。");
parseTime("2022-01-17 11:07:02");
System.out.println("程序结束。。。。");
}
private static void parseTime(String date) {
try {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
InputStream in = new FileInputStream("E:/meinv.jpg");
} catch (Exception e) {
e.printStackTrace();//打印异常栈信息
}
}
}
output:
程序开始。。。。
Mon Jan 17 11:07:02 CST 2022
java.io.FileNotFoundException: E:\meinv.jpg (系统找不到指定的文件。)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at com.csl.d4_exception_javac.ExceptionDemo1.parseTime(ExceptionDemo1.java:23)
at com.csl.d4_exception_javac.ExceptionDemo1.main(ExceptionDemo1.java:13)
程序结束。。。。
异常处理方式3——前两者结合
- 方法直接将异常通过throws抛出去给调用者
- 调用者收到异常后直接捕获处理
public class ExceptionDemo1 {
public static void main(String[] args) {
System.out.println("程序开始。。。。");
try {
parseTime("2022-01-17 11:07:02");
System.out.println("功能操作成功!!");
} catch (Exception e) {
e.printStackTrace();
System.out.println("功能操作失败。。。");
}
System.out.println("程序结束。。。。");
}
private static void parseTime(String date) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
//InputStream in = new FileInputStream("E:/meinv.jpg");
}
}
output:
程序开始。。。。
Mon Jan 17 11:07:02 CST 2022
功能操作成功!!
程序结束。。。。
异常处理的总结
- 在开发中按照规范来说第三种方式是最好的:底层的异常抛出去给最外层,最外层几种捕获处理
- 实际应用中,只要代码能够编译通过,并且功能能完成,那么每一种异常处理方式似乎也都是可以的
运行时异常的处理机制
运行时异常的处理方式
- 运行时异常编译阶段不会出错,是运行时才可能出错的,所以编译阶段不处理也是可以的
- 按照规范建议还是处理:建议在最外层调用处集中捕获处理即可
public class RuntimeException {
public static void main(String[] args) {
System.out.println("程序开始。。。。");
try {
chu(12,0);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("程序结束。。。。");
}
public static void chu(int a, int b){
int c = a / b;
System.out.println(c);
}
}
output:
程序开始。。。。
java.lang.ArithmeticException: / by zero
at com.csl.d4_exception_javac.RuntimeException.chu(RuntimeException.java:14)
at com.csl.d4_exception_javac.RuntimeException.main(RuntimeException.java:7)
程序结束。。。。
异常处理使代码更稳健的案例
需求:
- 键盘录入一个合理的价格为止(必须是数值,值必须大于0)
分析:
- 定义一个死循环,让用户不断输入价格
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (true){
try {
System.out.println("请输入合法的价格:");
String s = sc.nextLine();
//转换成double类型的价格
Double price = Double.valueOf(s);
//判断价格是否大于0
if (price>0){
System.out.println("定价:"+price);
break;
}else {
System.out.println("价格必须是整数。。。");
}
} catch (NumberFormatException e) {
System.out.println("用户输入的数据有误,请输入合法的数据!");
}
}
}
}
output:
请输入合法的价格:
aa
用户输入的数据有误,请输入合法的数据!
请输入合法的价格:
12
定价:12.0
自定义异常
自定义异常的必要?
- Java无法为这个世界上全部的问题提供异常类
- 如果企业想通过异常的方式来管理自己的某个问题,就需要自定义异常类了。
自定义异常的好处:
- 可以使用异常机制管理业务问题,如提醒程序员注意。
- 同事一旦出现bug,可以用异常的形式清晰的指出出错的地方。
自定义异常的分类
1.自定义编译时异常
- 定义一个异常类继承Exception
- 重写构造器。
- 在出现异常的地方用throw new 自定义对象抛出
- 作用:编译时异常是编译阶段就报错,提醒更加强烈,一定要处理!
public class CustomizeIllegalException extends Exception {
public CustomizeIllegalException() {
}
public CustomizeIllegalException(String message) {
super(message);
}
}
public class ExceptionDemo {
public static void main(String[] args) {
try {
checkAge(-16);
} catch (CustomizeIllegalException e) {
e.printStackTrace();
}
}
public static void checkAge(int a) throws CustomizeIllegalException {
if (a<0 || a>150){
//抛出去一个异常对象给调用者
//throw在方法内部直接定义异常对象,并从此点抛出
//throws用在方法申明上面,抛出方法内部的异常
throw new CustomizeIllegalException(a+"is Illeagal!");
}else {
System.out.println("年龄合法!");
}
}
}
output:
com.csl.d5_exception_custom.CustomizeIllegalException: -16is Illeagal!
at com.csl.d5_exception_custom.ExceptionDemo.checkAge(ExceptionDemo.java:17)
at com.csl.d5_exception_custom.ExceptionDemo.main(ExceptionDemo.java:7)
2.自定义运行时异常
- 定义一个异常类继承RuntimeException
- 重写构造器
- 在出现异常的地方用throw new 自定义对象抛出
- 作用:提醒不强烈,编译阶段不报错!!!运行时才可能出现!