【疯狂Java讲义】Java学习记录(异常处理)

异常机制,用于保障我们的程序更加健壮性(鲁棒性)。

程序员开发的程序,无论用户怎么操作,它都可以正常应对。

对于一个五子棋来说:

if(用户输入包含非数字、逗号之外的其他字符) {
    // 进行错误处理
} else if (用户输入逗号) {
    // 进行错误处理
} else if (用户输入坐标超出了范围) {
    // 进行错误处理
} ......
{
    // 最后才进行业务处理
}

这种机制有如下两个问题;

1、我们永远不可能“穷举”所有错误,并进行处理。

2、代码“臃肿”得难以忍受。

能不能把上面代码简化为:

if (一切正常) [
    // 进行业务处理
} else if (出现任何各种错误) {
    // 进行错误处理
}

 Java的异常处理:

//尝试让它执行业务处理,如果可以执行完成,就代表了一切正常
try {
    // 正常业务处理
} catch (异常1 e1) {
    // 进行异常1处理
} catch (异常2 e2) {
    // 进行异常2处理
} 

不同的catch块负责对不同的异常进行处理。

异常的执行流程(P414 图10.1)

异常类的继承体系(P414)

对于一个异常,最多只有一个catch块能捕捉到该异常。

多个catch块捕捉异常,应该是先捕捉子类异常,再捕捉父类异常。

多异常捕捉(P417)

JDK 7 提供了新功能:多异常捕捉

多异常捕捉的异常形参默认有final修饰。

catch ( 类1 | 类2 | 类3 ex)

访问异常信息(P417)

当catch块捕捉到异常时,异常对象会作为catch块的参数被传入。

因此在catch块可通过该参数来访问实际的异常对象。

getMessage

        返回异常的描述信息

public class AddTest {
	public static void main(String[] args) {
		try {
			// args[0] 代表用户运行该程序传入的第1个字符串
			Double d1 = Double.parseDouble(args[0]);
			
			// args[0] 代表用户运行该程序传入的第2个字符串
			Double d2 = Double.parseDouble(args[1]);
			
			System.out.println(d1 + d2);
		} catch (ArrayIndexOutOfBoundsException | NumberFormatException ex) {
			// 多异常捕捉的异常形参默认有final修饰。
			System.out.println("您必须输入2个数字来做加法运算!");
			System.out.println(ex.getMessage());
		}
	}
}

printStackTrace

        输出异常的跟踪栈信息

程序排错的方法

        排错时,应该从异常发生的“第一个你自己开发的程序”开始排错。

异常处理的完整语法格式

try {
    // 正常业务处理
} catch(异常 e1) {
    // 进行异常1处理    0~N个
} finally {
    
}

try

        不能“孤独”地存在——catch块和finally至少要有一个。

finally

        ——通常用于回收资源

                JVM会保证【finally块总会得到执行】,return语句也不能阻止。

                无论程序是否抛出异常,甚至程序遇到return语句——都不能阻止finally的执行。

                除非遇到退出虚拟机的代码,才能阻止finally的执行。

public class FinallyTest {
	public static void main(String[] args) {
		System.out.println(test());
	}

	public static int test() {
		int a = 20;
		try {
			System.out.println("执行try块");
			return ++a; // 该return语句会执行,该方法本来应该返回
						// 但finally总会获得执行的机会
						/*
							处理流程是:把return执行了,
							在将要返回方法、但又未返回的时候,程序本来打算返回21
							系统回去执行finally,当系统执行finally时,finally又遇到了return,
							于是该finally的return把方法结束。此时返回22.
						*/
		} finally {
			return ++a;
		}
	}
}

                 除非遇到退出虚拟机的代码,才能阻止finally的执行。

public class FinallyTest3 {
	public static void main(String[] args) {
		try {
			System.out.println("执行try块");
			
			// 退出虚拟机,就可以阻止finally的执行
//			System.exit(1);
			Runtime.getRuntime().exit(1);
		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			System.out.println("finally的执行");
		}
	}
}

异常处理可以嵌套

try {

} catch {
    try {
    
    }
} finally {
    try {

    }
}

JDK 7还提供一个自动关闭资源的try语句

try (
    // 此处声明的资源,系统可以自动关闭它。
)
{
    //
}

对于自动关闭资源的try语句,可以没有catch和finally——可以孤独地存在。

自动关闭资源的try语句,有两个注意点:

1、只有放在try后面的圆括号里的资源(不适合变量类型)才会被关闭。

2、能被自动关闭的资源必须实现Closeable或AutoCloseable接口

import java.io.FileInputStream;

public class AutoClose {
	public static void main(String[] args) throws Exception {
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("D:\\Program Files\\eclipse-java-2023-06-R-win32-x86_64\\workplace\\day\\src\\day12\\AutoClose.java");
			
			System.out.println((char)fis.read());
			System.out.println((char)fis.read());
			System.out.println((char)fis.read());
			System.out.println((char)fis.read());
		}
		finally {
			try {
				fis.close();
			}
			catch (Exception ex) {
				ex.printStackTrace();
			}
		}
	}
}

上面的代码等价于下面的: 

import java.io.FileInputStream;

public class AutoClose {
	public static void main(String[] args) throws Exception {
		try (
			FileInputStream fis = new FileInputStream("D:\\Program Files\\eclipse-java-2023-06-R-win32-x86_64\\workplace\\day\\src\\day12\\AutoClose.java");
		)
		{
			System.out.println((char)fis.read());
			System.out.println((char)fis.read());
			System.out.println((char)fis.read());
			System.out.println((char)fis.read());
		}
        // 自动关闭资源的try,相当于在最后有一个隐藏的finally
	}
}

异常分类

checked异常:普通的、不是RuntimeExcepiton子类的异常,都是checked异常。

runtime异常:RuntimeException的子类都是runtime异常。

【除非你直接,或间接继承了RuntimeException】

checked异常

Java设计初衷:没有完善的错误处理的代码,不会得到执行的机会。

Java设计了一种“checked”异常——编译器会强制检查。

                编译器要求程序员:1、要么处理该异常。

                                                2、要么声明抛出该异常。

runtime异常

程序员你想捕捉,就捕捉。

如果你现在不想处理,编译器不会进行强制检查

checkded异常可以更好地保证【程序员一定会对异常进行处理】,

但checked也会给编程带来繁琐——要么用throws声明抛出,要么显式用catch来捕捉。

有名的Runtime异常

        - NullPointerExcepiton

        - ArrayIndexOutOfBoundsException

        - ClassCastExcepiton

        - ArithemethicExcepiton 比如整数除以0

        - NumberFormatExcepiton

        - IllegalArgumentException

抛出异常可能的两种情况

        1、程序不能成功地执行完成,由系统自动生成异常对象,并抛出。

        2、程序出现了与业务规则不符的情况,由程序员来创建异常对象,并通过throw关键字抛出。

        即使是程序员通过throw抛出的异常,如果是checked异常,JDK同样会强制检查,要求程序员要么用try catch显式捕捉,要么用throws声明抛出

Exception不属于Runtime异常,属于Checked异常。

 Throwable也可以捕获Excepiton异常,因为Throwable包括了异常和错误,是最大的。

 

用throws声明抛出异常

该方法已经注意到了这几个异常,但目前该方法无法、不想处理该异常。

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExcepitonType {

	public static void main(String[] args) {
		int b = 40 / 0; //这条代码肯定会出异常,但编译时可以通过
	
		try {
			FileInputStream fis = new FileInputStream("ExceptionType.java");
		} catch (FileNotFoundException ex) {
			System.out.println("修复、处理异常");
			ex.printStackTrace();
		}
	}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ExcepitonType {

	// main方法声明已经注意到可能发生FileNotFoundException异常
	// 但main方法目前不想、不知道怎么处理该异常
	public static void main(String[] args) throws FileNotFoundException {
		int b = 40 / 0; //这条代码肯定会出异常,但编译时可以通过
		FileInputStream fis = new FileInputStream("ExceptionType.java");
		
	}
}

方法重写

                两同(方法名相同、形参列表相同)

                两小(⼦类⽅法的返回类型⼩于等于⽗类⽅法的返回类型、⼦类抛出的异常⼩于等于⽗类抛出的异常)

                一大(⼦类⽅法的访问权限要⼤于等于⽗类⽅法的访问权限)

                其中一小,就包括用throws声明抛出的异常必须更小

              【注意】子类方法不声明抛出异常,肯定是可以的。

throws与throw的区别

        1、throws只能在方法签名中用。

              throws后可以紧跟多个异常类的类名,多个类名之间用逗号(,)隔开。

        2、throws就是一条普通的语句。

              throws后紧跟一个异常实例。

catch与throw结合使用

有一种场景:A方法调用了B方法,当B方法出现了异常,而且B方法自己捕捉了异常。

                    此时A方法就不知道被调的B方法曾经发生过异常。

                    但有些时候,虽然B自己捕捉了异常,但A依然需要知道被调用方法曾经发生的异常。

                   此时的做法2,可以是先用catch来捕捉原有的异常,再用throw抛出 一个自定义异常。

                   专业说法叫做“异常转移”。

自定义异常

如果你想自定义runtime异常,继承RuntimeExcepiton。

如果你想自定义checkde异常,继承Excepiton。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值