Java基础笔记:Day_13 异常机制相关

一、异常机制

1.异常机制的引入

先入为主的一个概念:在以往的一些学习过程中,我们往往把一些错误用if-else语句进行处理。比如:如果一个字符串的输入并不是我们需要的,我们就用if-else对其进行控制(如果不是满足某种格式的话),以达到我们想要的结果。但是有些问题并不能解决的如此顺利,甚至它会影响到程序的运行。此时,异常机制就显得尤为重要了。

在程序当中,错误可能产生于程序员根本没有预料到的情况,或者是超出了程序员可控范围之内,比如:输入数据非法、试图访问根本不出存在的地址等。异常往往是程序在执行期间发生的事情,它中断了正在执行的程序正常指令流。

		String str = null;
		System.out.println(str.toString());
		//Exception in thread "main" java.lang.NullPointerException
		//空指针异常
		//上述代码异常时,下面的代码将不会执行
		System.out.println(30/0);
		//Exception in thread "main" java.lang.ArithmeticException: / by zero
		//算术异常

对于这些非正常情况(发生的时候回中断程序运行),一般都分两种情况:
①.Error一般是JVM相关的不可修复的错误,比如:内存溢出、系统崩溃等。此时由JVM抛出,我们无法处理。常见的有:StackOverflowError(程序递归过深发生内存溢出)
②.Exception表示异常。程序出现了不正常情况,并且可以修复。常见的有空指针异常和数组越界异常等。
当异常出现时,必须处理异常!!!

2.异常捕捉try/catch

如果异常出现了,我们通常会使用try/catch语句来处理异常。其语法如下:

try{
	//可能出现异常的代码块
}catch(异常类型  自定义的异常名e)
{
		//处理异常的代码
		//记录日志/打印异常信息/抛出异常
}

需要注意的是:try/catch都不能单独使用,必须连用!
那么,如何获取异常信息呢?我们往往使用Throwable类的方法,如getMessage()等。代码示例如下:

public class Demo{
	public static void main(String[] args) {	
		System.out.println("Begin!");
		try {
			int num = 10/0;
			System.out.println(num);//出现异常,此句不执行。转而执行Catch代码块
		}catch(ArithmeticException e) {
			System.out.println("About Exception:" + e.getMessage());
			//String getMessage()我们一般用来提示用户。输出错误性质
			System.out.println("Exception : " + e.toString());
			//String toString()一般我们不用。输出异常的类型和性质
			e.printStackTrace();
			//void printStackTrace() 我们在开发中常用,多用,方便调试和修改。
		}
		System.out.println("End!");
	}
}

但是这仅仅是发生了一个算术异常, 在实际开发中也有可能会遇到多个异常的情况。此时的代码就应该是这样的:

try{
	//可能出现异常的代码块
}catch(异常类型A  自定义的异常名e1)
{
		//处理异常的代码
		//记录日志/打印异常信息/抛出异常
}catch(异常类型B 自定义的异常名e2)
{
		//处理异常的代码
		//记录日志/打印异常信息/抛出异常
}

需要注意的是:①在一个catch语句中,只能捕获中类型的异常,如果需要捕获多种异常,就需要使用catch语句。
②代码在一瞬间只能出现一种类型的异常,只需要一个catch捕捉,但是绝对不会同时出现多个异常。

当然,一个完整的异常处理也是需要finally语句。它的作用是:无论程序有无异常发生,无论try/catch语句是否顺利执行,它总会执行finally语句。当然也会有4种特殊情况:
①.finally代码块中发生了异常。
②.前面的代码使用了System.exit()退出程序
③.程序所在的线程死亡
④.关闭CPU

public class Demo_01 {
	public static void main(String[] args) {	
		System.out.println("Begin!");
		try {
			int num = 10/0;
			System.out.println(num);//出现异常,此句以下不执行。转而执行Catch代码块
		}catch(ArithmeticException e) {
			System.out.println("About Exception:" + e.getMessage());
			//String getMessage()我们一般用来提示用户。
			System.out.println("Exception : " + e.toString());
			//String toString()一般我们不用。
			e.printStackTrace();
			//void printStackTrace() 我们在开发中常用,多用,方便调试和修改。	
			
			System.exit(0);
			//加入此句finally将无法执行
		}finally {
			System.out.println("Shut Down!");
			
		}
		System.out.println("End!");
	}
}

那么,此时又对于finally有个小小的问题:下列代码输出什么值呢?


**public class Demo_01 {
	public static void main(String[] args) {	
		int num = testMethod();
		System.out.print(num);
	}
	
	private static int testMethod()
	{
		try {
			return 1;
		}finally {
			return 1001;
		}
	}
}**

通过Debug后,可以看到的是,当进入testMethod()后,首先执行的try语句,但是并没有执行return 1而是直接跳入到了finally中。可以得到:finally是先于return执行的。

3.异常与Throwable

异常的分类:
①.编译时期异常:checked异常。在编译时期就是检查,如果没有处理异常将会编译失败。比如上面提到的算术异常。处理方案有二:1.使用throw抛出;2.使用try/catch。
②.运行时期异常:在运行时期检查的异常,不会影响编译器检测(不报错)。运行异常:在编译时期可处理可不处理。
在这里插入图片描述
在这里插入图片描述
但是我们多数还是使用Runtime异常,受检异常真的很难处理。

4.抛出异常
有两种方法抛出异常:throw和throws。
①.用throw抛出异常的时候,是运用于方法内部的,抛出一个具体的异常对象(是对象)。语法格式为:

throw new 异常类("异常信息");//后终止方法。

具体应用:

public class Demo_01 {
	public static void main(String[] args) {	
		
		try {
			int ret = divide(12,0);
			System.out.println(ret);
		}catch(ArithmeticException e) {
			System.out.println(e.getMessage());
		}
	}
	
	private static int divide(int num1,int num2)
	{
		System.out.println("Begin!");
		if(num2 == 0){
			throw new ArithmeticException("除数不能为0");
			//执行完此句后,后面的句子均不能执行了
			//return是返回一个值,它就相当于返回一个错误给调用者
		}
		try {
			int ret = num1/num2;
			System.out.println("结果 = " + ret);
			return ret;
		}catch(ArithmeticException e) {
			e.printStackTrace();
		}
		System.out.println("End!");
		return 0;	
	}
	

同时,对于Java的源代码中,也有类似。比如:String类中的charAt()方法,如果参数为-1的话,会有StringIndexOutOfBoundsException异常。源代码为:
在这里插入图片描述
②.throws用于方法声明之上,表示该方法不处理异常而是提醒方法的调用者来处理异常(抛出异常)。语法格式:

	修饰符 返回类型 方法名 (参数列表) throws Exception{}

代码示例如下:

public class Demo_01 {
	public static void main(String[] args) throws Exception{	
		divide(10,0);
	}
	
	//不处理这类型的异常,提醒调用者处理
	private static int divide (int num1,int num2) throws Exception 
	{
		System.out.println("Begin!");
		if(num2 == 0){
			new Exception("除数不能为0");
		}
		try {
			int ret = num1/num2;
			System.out.println("结果 = " + ret);
			return ret;
		}catch(ArithmeticException e) {
			e.printStackTrace();
		}
		System.out.println("End!");
		return 0;	
	}	
}

如果都不处理异常,连主方法也不处理异常,那么将继续抛出给JVM,底层的处理机制就是打印异常的跟踪栈信息。runtime异常默认的就是这种处理方式。
以上代码的异常结果:
在这里插入图片描述
5.自定义异常结果

在开发中,根据需要自定义异常。异常类如何定义?
方法一:自定义一个受检查的异常类:自定义类 并继承于java.lang.Exception
方法二:自定义一个运行时期的异常类:自定义类 并继承于java.lang.RuntimeException
(此刻再次提倡我们使用RuntimeException)。

例如已经定义好的了ArithmeticException:

在这里插入图片描述
代码示例(自定义一个LogicException异常,模拟一个注册用户名判断机制):

//自定义的异常
public class LogicException extends RuntimeException {

	private static final long serialVersionUID = 1L;
	public LogicException() {
		super();	
	}
	public LogicException(String message, Throwable cause) {
		super(message, cause);
		/*
		 * @param message 表示当前异常的原因/信息
		 * @param cause   表示异常的根本原因
		 * */
	}
	public LogicException(String message) {
		super(message);
	
	}
}
//用户名判重机制代码
public class RegisterDemo {
	
	private static String[] names = {"Lucy","Jack"};//模拟数据库提供的信息
	public static void main(String[] args) {
		try {
			checkUsername("Lucy");
			System.out.println("Congratulations!");
		}catch(LogicException e) {
			//处理异常
			String errorMsg = e.getMessage();
			System.out.println(errorMsg);
		}
	}
	
	public static boolean checkUsername(String username)
	{
		for(String name : names) {
			if(name.equals(username)) {
				throw new LogicException("Registered!");
			}
		}
		return true;
	}
}

最后,总结一下异常机制的使用原则:
①.异常只能用于非正常情况,毕竟try-catch会影响性能。
②.需要为异常提供说明文档,比如Java doc,如果自定义了一个异常或者某一个方法抛出了异常。我们应该记录在文档注释中。
③.尽可能避免一些没有必要的异常,比如算术异常和空指针异常。
④.异常的粒度很重要。应该为一个基本操作提供try-catch代码块,而不是对几百行代码使用。
⑤.不建议在循环中使用异常处理。
⑥.自定义异常尽量使用RuntimeException,它真的很好用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值