Java异常
异常概述 体系
异常是指程序在"编译"或者"执行"过程中可能出现的问题
注意:语法错误不算在异常体系中
异常体系结构
Java中的异常主要是继承自 Throwable类,分为 Error 和 Exception.
Error : 是系统级别错误,JVM退出等,代码无法控制
Exception:
编译时异常 ---- 除了RuntimeException及其子类以外的异常
运行时异常 ---- RuntimeException
常见运行时异常
注意:运行时异常一般是程序员业务没有考虑好或者逻辑不严谨引起的程序错误
数组索引越界异常 ArrayIndexOutOfBoundsException
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
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at d1_exception.exceptionDemo.main(exceptionDemo.java:10)
空指针异常 NullPointerException
String str = null;
System.out.println(str); // 可以输出 但是不能调用方法
//System.out.println(str.length());
Exception in thread "main" java.lang.NullPointerException
at d1_exception.exceptionDemo.main(exceptionDemo.java:15)
数学操作异常 ArithmeticException
int a = 10;
int b = 0;
System.out.println(a / b);
Exception in thread "main" java.lang.ArithmeticException: / by zero
at d1_exception.exceptionDemo.main(exceptionDemo.java:20)
类型转换异常 ClassCastException
Object o = 123; // Object一切类的祖宗类 什么样的类型都可以接收
String str1 = (String) o; // 123整数类型无法转换成String
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
at d1_exception.exceptionDemo.main(exceptionDemo.java:24)
数字转换异常 NumberFormatException
//数字转换异常 NumberFormatException
String str2 = "124aaa";
Integer in = Integer.valueOf(str2);
System.out.println(in + 1);
Exception in thread "main" java.lang.NumberFormatException: For input string: "124aaa"
at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68)
at java.base/java.lang.Integer.parseInt(Integer.java:658)
at java.base/java.lang.Integer.valueOf(Integer.java:989)
at d1_exception.exceptionDemo.main(exceptionDemo.java:28)
常见编译时异常
不是RuntimeException或者其子类的异常, 编译时就报错,必须处理,否则代码不通过
//编译时异常案例
String date = "2020-11-11 11:11:11";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss"); //故意把格式写错 看异常提示信息
Date d = sdf.parse(date);
System.out.println(d);
其中
Date d = sdf.parse(date)会有日期解析异常,ParseException
Exception in thread "main" java.text.ParseException: Unparseable date: "2020-11-11 11:11:11"
at java.base/java.text.DateFormat.parse(DateFormat.java:395)
at d1_exception1.exceptionDemo2.main(exceptionDemo2.java:14)
异常的默认处理流程
1.默认会在出现异常的代码那里自动的创建一个异常对象
2.异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给虚拟机
3.虚拟机接收异常对象后,先在控制台直接输出异常栈信息数据
4.直接从当前执行的异常点结束当前程序
5.后续代码没有机会执行,因为程序已经死亡
案例
System.out.println("程序开始");
int a = 10;
int b = 0;
chu(a,b);
System.out.println("程序结束");
}
public static void chu(int a,int b){
System.out.println(a);
System.out.println(b);
System.out.println(a / b);
}
程序开始
10
0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at d1_exception1.exceptionDemo3.chu(exceptionDemo3.java:16)
at d1_exception1.exceptionDemo3.main(exceptionDemo3.java:9)
编译时异常的处理机制
编译时异常是编译阶段就出错的,所以必须处理,否则代码无法通过
编译时异常的处理形式:3种
方式1
出现异常直接抛出去给调用者,调用者也继续抛出去
方式2
出现异常自己捕获处理,不麻烦别
方式3
前两者结合,出现异常直接抛出去给调用者,调用者捕获处理
异常处理方式1 throws
- throws:用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理’
- 这种方式并不好,发生异常的方法自己不处理,如果异常最终抛出去给虚拟机将引起程序死亡
抛出异常格式:
方法 throws 异常1,异常2,异常3...{
}
规范做法:
方法 throws Exception{
}
/*
演示异常处理方式一 : throws
*/
public static void main(String[] args) throws ParseException {
String date = "2011-11-11 11:11:11";
System.out.println("程序开始~~");
ParseTime(date);
System.out.println("程序结束~~");
}
public static void ParseTime(String date) throws ParseException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
}
正常执行:
程序开始~~
Fri Nov 11 11:11:11 CST 2011
程序结束~~
发生异常:(这里将异常对象抛给了JVM虚拟机 虚拟机会打印错误信息 然后结束掉当前程序 后面的程序就不再执行了)
程序开始~~
Exception in thread "main" java.text.ParseException: Unparseable date: "2011-11-11 11:11:11"
at java.base/java.text.DateFormat.parse(DateFormat.java:395)
at d1_exception1.exceptionThrows.ParseTime(exceptionThrows.java:21)
at d1_exception1.exceptionThrows.main(exceptionThrows.java:15)
如果程序下面还有异常抛出,可以在方法后面继续抛出更多的错误
public static void ParseTime(String date) throws ParseException, FileNotFoundException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM-dd HH:mm:ss");
Date d = sdf.parse(date);
System.out.println(d);
InputStream in = new FileInputStream("E:/apple.jpg");
}
后面的读取文件也可能出现错误,所以在方法的后面也抛出了FileNotFoundException异常
注意:
因为前面的日期解析已经抛出异常了,所以后面的异常就没有机会抛出,因为前面异常抛出被JVM虚拟机接收以后,程序就已经结束了,后面的程序没有机会再执行.
这里有个问题,如果程序里面需要抛出的异常对象很多,那么就会在方法后面写一大堆异常对象,这样显得累赘臃肿
推荐格式
public 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:/apple.jpg");
}
直接在方法后面加上 throws Exception即可
异常处理方式2 try…catch…
- 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理
- 发生异常的方法自己独立完成异常的处理,程序可以继续往下执行
格式:
try{
//监视可能出现异常的代码
}catch(异常类型1 变量){
//处理异常
}catch(异常类型2 变量){
//处理异常
}...
建议格式:
try{
//可能出现问题的代码
}catch(Exception e){
e.printStackTrace(); //直接打印异常栈信息
}
Exception可以捕获一切异常类型
public 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:/apple.jpg");
} catch (ParseException | FileNotFoundException e) {
e.printStackTrace();
}
//直接 catch (Exception e)
/* catch (Exception e) {
e.printStackTrace();
} */
}
异常处理方式3 throws + try … catch …(二者结合)
- 方法直接将异常通过throws抛出去给调用者
- 调用者收到异常后直接捕获处理
String date = "2011-11-11 11:11:11";
System.out.println("程序开始~~");
try {
ParseTime(date);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("程序结束~~");
}
public 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:/apple.jpg");
}
在ParseTime方法中采用抛出异常的方式,调用者在主函数中接收抛出的异常捕获并处理.采用try...catch...语句
运行时异常的处理机制
- 运行时异常编译阶段不会出错,是运行时才可能出错的,所以编译阶段不处理也是可以的
- 建议在最外层调用处集中捕获处理
public static void main(String[] args) {
System.out.println("程序开始");
int a = 10;
int b = 0;
try {
chu(a,b);
System.out.println("程序结束");
} catch (Exception e) {
e.printStackTrace();
System.out.println("操作失败");
}
}
public static void chu(int a,int b){
System.out.println(a);
System.out.println(b);
System.out.println(a / b);
}
程序开始
10
0
操作失败
java.lang.ArithmeticException: / by zero
at d1_exception1.exception_hadle_runtime.chu(exception_hadle_runtime.java:21)
at d1_exception1.exception_hadle_runtime.main(exception_hadle_runtime.java:10)
异常处理案例
案例:
需求:键盘录入一个合理的价格为止(必须是数值,值必须大于0)
Scanner sc = new Scanner(System.in);
while (true) {
try {
System.out.println("请输入价格:");
String priceStr = sc.nextLine();
//转换成double类型
double d = Double.valueOf(priceStr);
if (d > 0) {
System.out.println("定价是:" + d);
break;
} else {
System.out.println("价格不能为负数 请重新输入~");
}
} catch (NumberFormatException e) {
System.out.println("输入有问题 不能乱搞啊 ~~~~");
}
}
在没有添加try catch语句时候,如果键盘录入了非法字符,程序就会异常终止掉.
当添加了try catch语句,当键盘再次录入不合法数据时,就可以在异常语句中给到提醒并再次录入,直到录入成功为止
如此一来,保证了程序的稳健性.
自定义异常
- 可以使用异常的机制管理业务问题
- 一旦出现bug,可以用异常的形式清晰的指出出错的地方
自定义编译时异常
第一步 : 定义一个异常类继承Exception
第二步 : 重写构造器
第三步 : 在出现异常的地方用 throw new 自定义对象抛出
public class AgeIllegalException extends Exception{
/*
自定义 编译时异常 AgeIllegalException
1.继承Exception
2.重写构造器
年龄超过200 或者 小于0 就是异常
*/
public AgeIllegalException() {
}
public AgeIllegalException(String message) {
super(message);
}
}
public class DIYDemo {
public static void main(String[] args) throws AgeIllegalException {
checkAge(900);
}
public static void checkAge(int age) throws AgeIllegalException {
if (age < 0 || age > 200){
//throws: 用在方法申明上的,抛出方法内部的异常
//throw:在方法内部直接创建一个异常对象,并从此点抛出
// 抛出去一个异常对象给调用者
throw new AgeIllegalException(age+" is illegal");
}else{
System.out.println("年龄合法 推荐相应商品");
}
}
}
Exception in thread "main" d1_exceptiom_DIY.AgeIllegalException: 900 is illegal
at d1_exceptiom_DIY.DIYDemo.checkAge(DIYDemo.java:17)
at d1_exceptiom_DIY.DIYDemo.main(DIYDemo.java:6)
自定义运行时异常
第一步 : 自定义一个异常类继承RuntimeException
第二步 : 重写构造器
第三步 : 在出现异常的地方用 throw new 自定义对象抛出
作用 : 提醒不强烈,编译阶段不报错!!! 运行时才可能出现!
public class AgeIlleagalRuntimeException extends RuntimeException {
public AgeIlleagalRuntimeException() {
}
public AgeIlleagalRuntimeException(String message) {
super(message);
}
}
public class DIYDemo {
public static void main(String[] args) {
try {
checkAge(900);
} catch (Exception e) {
e.printStackTrace();
}
checkAge(23);
}
public static void checkAge(int age) {
if (age < 0 || age > 200){
//throws: 用在方法申明上的,抛出方法内部的异常
//throw:在方法内部直接创建一个异常对象,并从此点抛出
// 抛出去一个异常对象给调用者
throw new AgeIlleagalRuntimeException(age+" is illegal");
}else{
System.out.println("年龄合法 推荐相应商品");
}
}
}
d1_exceptiom_DIY.AgeIlleagalRuntimeException: 900 is illegal
at d1_exceptiom_DIY.DIYDemo.checkAge(DIYDemo.java:24)
at d1_exceptiom_DIY.DIYDemo.main(DIYDemo.java:7)
年龄合法 推荐相应商品