概述
异常是在编译或者运行过程中可能出现的问题。比如:数组越界、空指针、日期格式化、类型转换等。
注意:语法错误不算在异常体系中
为什么要学习异常?
- 异常一旦出现,如果没有提前处理,程序就会退出JVM虚拟机而终止
- 研究异常并且避免异常,然后提前处理异常,增强程序的安全,健壮性
异常体系
Error:系统级别问题、JVM退出等,代码无法控制
Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题
RuntimeException及其子类:运行时异常,编译阶段不会报错。(空指针异常、数组越界等)
处RuntimeException外的所有异常:编译时异常,编译器必须处理,否则程序不能通过编译。(日期格式化异常等)
运行时异常
运行时异常:直接继承自RuntimeException或者其子类,编译阶段不会报错,运行时可能出现的错误。
运行时异常示例
- 数组越界异常:ArrayIndexOutOfBoundsException
- 空指针异常:NullPointException,直接输出没问题,但是调用空指针的变量就会报错
- 数学操作异常:ArithmeticException
- 类型转换异常:ClassCastException
- 数字转换异常:NumberFormatException
运行时异常一般是程序员业务没有考虑好或者逻辑不严谨引起的程序错误
package com.spark.study;
/**
* Test class
* description: 常见的运行时异常
*
* @author Administrator
* @date 2023/4/16
*/
public class Test {
public static void main(String[] args) {
// 数组索引越界异常
int [] arr = {1,2,3};
System.out.println(arr[2]);
//System.out.println(arr[3]); // 抛出异常
// 空指针异常
String name = null;
System.out.println(name); // 直接输出不会抛出异常
//System.out.println(name.length()); // 引用空指针变量抛出异常
// 数学操作异常
//int result = 10/0;
// 类型转换异常
Object number = 13;
//String str = (String) number; // 抛出异常
// 数字转换异常
String str = "123";
int number1 = Integer.valueOf(str);
System.out.println(number1);
str = "123aabb";
//int number2 = Integer.valueOf(str); // 抛出异常
//System.out.println(number2);
}
}
编译时异常
不是RuntimeException或者其子类的异常,编译阶段就报错,必须进行处理,否则代码不通过。
package com.spark.study;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Test2 class
* description: 编译时异常
*
* @author Administrator
* @date 2023/4/16
*/
public class Test2 {
public static void main(String[] args) {
String str = "2023-04-15 12:00:00";
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 日期格式转换为编译时异常,必须进行处理
// 向上抛出异常或者使用try-catch捕获异常
try {
Date date = simpleDateFormat.parse(str);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
异常的处理机制
默认处理机制
① 默认会在出现异常的代码处自动创建一个异常对象
② 异常会从方法中出现的位置抛出给调用者,调用者最终抛出给JVM虚拟机
③ 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据
④ 直接从当前执行的异常点干掉当前程序
⑤ 后续代码没有机会执行,因为程序已经死亡
编译时异常处理机制
编译时异常处理形式有三种
- 出现异常直接抛出给调用者,调用者也继续抛出去
- 出现异常自己捕获处理
- 前两者结合,出现异常直接抛出给调用者,调用者捕获处理
方式一:throws
throws用在方法上,可以将方法内部出现的异常抛出去给本方法的调用者处理
这种方式并不好,发生异常自己不处理,如果异常最终抛出给虚拟机将引起程序死亡
抛出格式:
方法 throws 异常1,异常2,异常3...{
}
规范做法:
方法 throws Exception{
}
方式二:try-catch
监视捕获异常,用在方法内部,出现异常可以对其进行捕获处理
捕获处理格式:
try{
}catch(异常类型1 变量){
// 处理异常
}catch(异常类型2 变量){
// 处理异常
}...
建议格式:
try{
}catch(Exception e){
e.printStackTrace(); // 直接打印异常栈信息
}
方式三:方式一和二结合
方法直接通过throws抛出去给调用者
调用者收到异常后直接捕获处理
void test01() throws Exception{
// 测试代码
}
void test02(){
try{
// 调用test01时捕获处理其抛出的异常
test01();
}catch(Exception e){
e.printStackTrace();
}
}
运行时异常处理机制
运行时异常编译阶段不会报错,是运行时才可能出现的错误,所以编译阶段不处理也可以
按照规范建议:在最外层调用集中捕获处理即可
自定义异常
针对特定业务进行问题提醒,Java提供的异常无法满足时可以自定义异常
自定义异常的分类
- 自定义编译时异常
- 继承Exception
- 重写构造器
- 在抛出异常时throw new 自定义异常对象
- 自定义运行时异常
- 继承RuntimeException
- 重写构造器
- 在抛出异常时throw new 自定义异常对象
package com.spark.myexception;
/**
* AgeCheckException class
* description: 自定义异常校验年龄
*
* @author Administrator
* @date 2023/4/17
*/
public class AgeCheckException extends Exception{
public AgeCheckException() {
}
public AgeCheckException(String message) {
super(message);
}
}
package com.spark.myexception;
/**
* AgeCheckRuntimeException class
* description: 自定义运行时异常
*
* @author Administrator
* @date 2023/4/17
*/
public class AgeCheckRuntimeException extends RuntimeException{
public AgeCheckRuntimeException() {
}
public AgeCheckRuntimeException(String message) {
super(message);
}
}
package com.spark.myexception;
/**
* Test class
* description: 自定义异常
*
* @author Administrator
* @date 2023/4/17
*/
public class Test {
public static void main(String[] args) {
try {
// 调用者捕获自定义编译时异常
checkAge(-1);
} catch (AgeCheckException e) {
e.printStackTrace();
}
// 调用者不需要处理运行时异常
checkAge2(-1);
}
// 自定义编译时异常
public static void checkAge(int age) throws AgeCheckException {
if(age<0 || age>150){
// 抛出异常对象给调用者
throw new AgeCheckException(age+" is illegal");
}
}
// 自定义运行时异常
public static void checkAge2(int age){
if(age<0 || age>150){
// 抛出异常
throw new AgeCheckRuntimeException(age+" is illegal");
}
}
}