文章目录
1 异常的概述、体系
1.1 什么是异常?
- 异常是程序在编译或运行中可能出现的问题。
- 比如:数组索引越界、空指针异常、日期格式化异常等
- 异常一旦出现,如果没有及时处理,程序就会退出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]);
System.out.println("___________上面错误,这里不会被显示________");
}
}
会出现如下结果(程序终止并且抛出异常)
1.2 异常体系
Error
系统级别问题,JVM退出等,代码无法控制
Exception:java.lang包下,称为异常类,它表示程序本身可以处理的问题。
- RuntimeException及其子类:运行时异常,编译阶段不会报错。
- 除RuntimeException之外的所有异常:编译时异常,这种在编译期必须处理,否则程序不能通过编译。(写代码阶段就会报错)
1.3 运行时异常
运行时异常示例:
- 数组索引越界异常:ArrayIndexOutofBoundsException
- 空指针异常:NullPointerException,直接输出没有问题,但是调用空指针的变量功能就会报错
- 数学操作异常:ArithmeticException
- 类型转换异常:ClassCastException
- 数字转换异常:NumberFormatException
运行时异常:一般是程序员技术不行导致的
public class ExceptionDemo {
public static void main(String[] args){
String name = null;
System.out.println(name);
// 这里会出现空指针异常
int len = name.length();
System.out.println(len);
}
}
2 异常的默认处理流程
- 默认会在出现异常的代码那里自动创建一个异常对象
- 异常会从方法中出现的点这里抛出给调用者,调用者最终抛出给JVM虚拟机
- 虚拟机接收到异常对象后,先在控制台直接输出异常栈信息数据
- 直接从当前执行的异常点干掉当前程序
- 后续代码没有机会执行了,因为程序已经死亡。
2.1 编译时异常的处理形式
- 1,出现异常直接抛出给调用者,调用者也抛出去
- 2,出现异常自己捕获处理,不麻烦别人
- 3,前两者结合,出现异常直接抛出去给调用者,调用者捕获处理
方法1
throws:用在方法上,可以将方法内部出现的异常抛出去给方法的调用者
如果有多个异常可以选择throws 异常1,异常2,异常3(出问题时其实只抛出一个)
也可直接throws Exception(Exception是其他的父类,类型更高)
这个方式不好,如果异常最终抛给JVM虚拟机会导致程序死亡
如下图所示,出现编译时异常
抛出异常给调用者,那么方法就不会再报错
调用者继续抛出异常给虚拟机。虚拟机会把程序干掉。
方法2
try…catch…
- 监视捕获异常,用在方法内部,可以将方法内部出现的异常直接捕获处理
- 这个方法还行,发生异常的方法直接独立处理异常,程序继续往下执行
try{
//可能会出现异常的代码
}catch(异常类型1 变量){
//处理异常
}catch(异常类型1 变量){
//处理异常
}...
public static void parseTime(String date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-dd");
Date d = null;
//对异常1的处理
try {
d = (Date) sdf.parse(date);
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(d);
//对异常2的处理
try {
InputStream is = new FileInputStream("E:/bucunzai.jpg");
}catch (Exception e){
e.printStackTrace();
}
}
当然也可以两个放在一起抛出,这时程序会抛出第一个异常然后程序结束
这个方法更好,如果多个异常时更省力
public class ExceptionDemo {
public static void main(String[] args) {
parseTime("2022-03-05");
}
public static void parseTime(String date) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-dd");
try {
Date d = null;
d = (Date) sdf.parse(date);
System.out.println(d);
InputStream is = new FileInputStream("E:/bucunzai.jpg");
}catch (Exception e){
e.printStackTrace();//打印异常栈信息
}
}
方法3(推荐!!!)
前两者结合
- 方法直接将异常throws给调用者
- 调用者捕获并处理异常
(这个方法更好,因为调用者更希望知道下级的情况,而不是下级自己直接处理但是并不汇报)
public class ExceptionDemo {
public static void main(String[] args) {
//调用者捕获并且处理异常
try {
parseTime("2022-03-05");
}catch (Exception e){
e.printStackTrace();
}
}
//方法抛出异常
public static void parseTime(String date) throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-M-dd");
Date d = null;
d = (Date) sdf.parse(date);
System.out.println(d);
InputStream is = new FileInputStream("E:/bucunzai.jpg");
}
}
2.2 运行时异常的处理机制
- 运行时异常在编译阶段不会报错,编译阶段不处理也可以
- 按照规范建议还是处理:建议在最外层调用处集中捕获处理即可
注:运行时异常不用自己加throws 自己就会抛出(运行中自动抛出)
public class ExceptionDemo {
public static void main(String[] args) {
System.out.println("开始,,,,,,,");
//调用者处理异常 不影响后续程序的执行
try {
chu(10,0);
}catch (Exception e){
e.printStackTrace();
}
System.out.println("结束,,,,,,");
}
public static void chu(int a, int b){ //throws Exception
System.out.println(a);
System.out.println(b);
//这里分母不应该为零,可能会出现RuntimeException
int c = a / b;
System.out.println(c);
}
3 异常案例
键盘录入一个合理的价格为止(必须是数值,值必须大于0)
定义一个死循环,让用户不断输入价格
public class Test {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
while (true){
System.out.println("请输入合法的价格:");
String priseStr = sc.nextLine();
//如果用户输入的不是数而是乱七八糟的东西,那么会导致运行异常程序终止
double price = Double.valueOf(priseStr);
if(price > 0 ){
System.out.println("定价"+price);
break;
}else {
System.out.println("价格必须不小于0");
}
}
}
}
用户如果输入的是不合理的数还行,但是如果输入其他会导致异常
进行异常处理
try {
while (true) {
System.out.println("请输入合法的价格:");
String priseStr = sc.nextLine();
//如果用户输入的不是数而是乱七八糟的东西,那么会导致运行异常程序终止
double price = Double.valueOf(priseStr);
if (price > 0) {
System.out.println("定价" + price);
break;
} else {
System.out.println("价格必须不小于0");
}
}
}catch (Exception e){
System.out.println("请您输入合法的价格");
}
这时就不会终止程序而是继续提示重新输入
(这里其实还有点问题,抛出异常后只能保证程序不报错结束,但是循环不在继续,实际上是无法继续判断的!!!)
4 自定义异常
- java无法为所有异常提供异常类
- 企业想通过异常的方式来管理自己的某个业务问题,就需要自定义异常类
- 使用异常类机制管理业务问题,如提醒程序员注意
- 一旦出现bug,可以用异常的方式清晰的指出出错的地方
自定义异常的方法
1 自定义编译时异常
- 定义一个异常类继承Exception
- 重写构造器
- 在异常出现的地方用 throw new自定义对象抛出
下面是自定义的年龄非法输入的类
/**
* 自定义年龄异常 0-200 之外的都算作异常
*/
public class AgeIlleagalException extends Exception{
public AgeIlleagalException() {
}
public AgeIlleagalException(String message){
super(message);
}
}
使用这个异常类来检查异常
public class AgeCheck {
public static void main(String[] args){
//这时这里就会直接报错,提醒程序员出问题了
CheckAge(300);//Error!!!
}
public static void CheckAge(int age) throws AgeIlleagalException {
if(age<0 || age>200){
//抛出异常 如果这里只是提醒,则不知道问题出在哪里
//throws:用在方法申明上的,抛出方法内部的异常
//throw:在方法内部查找一个异常对象,并且抛出
throw new AgeIlleagalException("年龄有问题");
}else {
System.out.println("年龄是"+ age);
}
}
}
然后就可以捕获异常,程序不会终止
public static void main(String[] args) throws AgeIlleagalException{
//这时这里就会直接报错,提醒程序员出问题了
try {
CheckAge(300);
}catch (AgeIlleagalException e){
e.printStackTrace();
}
}
2 自定义运行时异常
- 定义一个异常类继承RuntimeException
- 重写构造器
- 在出现异常的地方用throw new自定义对象抛出
作用:这个的提醒并不强烈,编译阶段不报错!!运行时才报错 编译时异常经常打扰程序员,非必要不定义编译异常