异常类、异常机制与异常处理

java 专栏收录该内容
15 篇文章 0 订阅

java是采用面向对象的方式来处理异常的,过程如下所示:

  1. 抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径 ,并把异常对象交给JRE
  2. 捕获异常:JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止

如果没有异常机制,我们需要对程序可能出现的所有异常情况进行考虑(很复杂,并会导致出现大量的条件判断语句),这对程序员的本身要求较高。java给我们提供了一种方便的机制来应对和解决异常情况,这就是java的异常机制

异常的层次结构图如下图所示:

由图可知,所有异常对象都是派生于Throwable类的一个实例,Throwable下面又分了两个子类:Error类和Exception类

Error和Exception的区别:

  1. error是程序无法处理的错误,表示运行程序中有较严重的问题,大多数错误都与代码编写者执行的操作无关,是运行代码时jvm自身出现的问题,error表示系统处于无法修复的崩溃状态中,这个不需要我们去管
  2. exception是程序本身能够处理的异常,Exception类是所有异常类型的父类,其中子类包括了各种各样可能出现的异常事件

throw关键字的作用:手动抛出异常

throws关键字的作用:一个方法在声明时可以使用throws关键字声明要产生的若干个异常

注意:throw和throws是两个不同的关键字

RuntimeException异常:通常是由于编程错误引起的,例如除0、数组越界、空指针等等,由于产生频繁,如果显示声明或者捕获将会对程序的可读性和性能产生较大的影响。因此系统自动检测它们,并将它们交给默认的异常处理程序,代码编写者不需要对其进行处理,但经常需要增加逻辑处理来避免这些异常

//试图进行除0操作
public class Test01 {
	public static void main(String [] args) {
		int i=0;
		System.out.println(1/i);
	}
}

//运行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at day_6_1.Test01.main(Test01.java:6)

 我们可以通过增加逻辑判断语句来避免这些异常:

//通过逻辑处理来避免除0和空指针异常
public class Test01 {
	public static void main(String [] args) {
		int i=0;
		if(i!=0) {                  //避免除0异常
		System.out.println(1/i);
		}
		String string=null;
		if(string!=null) {
		System.out.println(string.length());        //避免空指针异常
		}
	}
}

CheckException异常:所有不是RuntimeException的异常统称为Checked Exception,又被称为“已检查异常”,这类异常在编译时就必须做出处理,否则无法通过编译;异常处理方式有两种:使用“try/catch”捕获异常,使用“throws”声明异常

try-catch-finally处理异常

  1. 用try执行一段代码,如果这段代码抛出了异常,可以通过它的类型来捕获(catch)并处理这个异常,finally语句是为异常处理提供的统一的出口,finally中的代码都要被执行(catch语句可以有多个,finally语句最多只有一个,可以通过自己的需求可有可无)
  2. try语句部分中存放的代码是可能发生异常的代码块,代码块的范围就是异常捕获和处理的范围。当try语句部分中的任意一条代码发生异常时,就会跳过这条语句后面的、处于try语句范围内的代码块。代码中可能会抛出一种或多种类型的异常,catch语句要分别对这些异常作出处理。一个try语句至少要有一个catch语句或者finally语句
  3. catch语句的常用方法(均继承于Throwable类):
    1. public String toString():显示异常的类名和产生异常的原因
    2. public String getMessage():只显示产生异常的原因,不显示类名
    3. public void printStackTrace():用来跟踪异常事件发生时堆栈中的内容
    4. 注意catch的顺序:如果想要捕获的异常有继承关系,父类异常的catch语句需要写在子类异常的catch语句下面,或者直接不写子类的catch语句。因为子类异常是可以被父类异常的catch语句捕获的
  4. 如果一些语句不管异常是否发生都必须执行,那么就可以把它放在finally语句块中。如果在try-catch语句中执行了return语句,那么finally子语句仍然会被执行,如果在try-catch语句中执行了程序退出代码,即执行System.exit(0);,则不会执行finally子语句
  5. 不要在finally语句块中放return语句,因为它会覆盖try中的return、throw语句和catch中的return、throw语句

try-catch-finally处理异常代码示例:

//try-catch-finally处理异常
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class Test01 {
	public static void main(String [] args) {
		FileReader reader=null;
		try {
			reader=new FileReader("d:/b.txt");     
			System.out.println("step1");
			char c1=(char) reader.read();
			System.out.println(c1);
		}catch(FileNotFoundException e) {
			System.out.println("step2");
//			e.printStackTrace();
			System.out.println("我是FileNotFoundException异常");
		}catch(IOException e) {
//			e.printStackTrace();
			System.out.println("我是IOException异常");
		}finally {
			System.out.println("step3");
			try {
				if(reader!=null) {
					reader.close();
				}
			}catch(IOException e) {
				e.printStackTrace();
				System.out.println("IOException异常");
			}
		}
	}
}
//运行结果
step2
我是FileNotFoundException异常
step3

解释:在上述代码中,在d盘下并没有b.txt这个文件,所以将会报FileNotFoundException异常,
同时try语句块中后面三句代码都会被跳过,然后catch语句捕获了这个异常,
并对其进行处理(即执行catch(FileNotFoundException e)语句块中的内容),
打印出了”step2“和”我是FileNotFoundException异常“。
然后执行finally语句块中的代码,打印出了“step3”

在finally中加入return语句导致catch中的throw语句失效示例代码:

import java.io.FileNotFoundException;
import java.io.FileReader;

public class Test4 {
	public static void main(String [] args) {
		fun2();
	}
	public static String fun1() throws FileNotFoundException {
		try {
			fun();
		}catch(FileNotFoundException e) {
			throw e;
		}
		finally {
			return "nihao";
		}
	}
	public static void fun() throws FileNotFoundException {
		FileReader reader=new FileReader("d:/b.txt");
	}
	public static void fun2() {
		String c="hello";
		try {
			c=fun1();
		}catch(FileNotFoundException e) {
			System.out.println(e);
		}
		finally {
			System.out.println(c);
		}
	}
}

//运行结果:
nihao

//解释:fun1方法中的return语句覆盖了catch中的throw语句,导致e没有被抛出来,
所以fun2中的catch语句没有执行;

通过throws声明异常:

  1. 当当前方法产生Checked Exception异常时,不一定要立刻处理它,可以将它throws给上层(即调用当前方法的方法),让上层来处理它。
  2. 如果一个方法中可能产生某种异常,而不知道怎么去处理它时,则应根据异常规范在方法的首部声明该方法可能抛出的异常
  3. 如果一个方法抛出多个Checked Exception异常,就必须在方法的首部列出所有的异常,之间用逗号隔开
  4. 如果这个方法发生了异常,异常代码后面的代码都不会被执行
import java.io.FileReader;
import java.io.IOException;
public class FileClass1 {
	public static void main(String[] args)throws IOException{
		read();
	}
	public static void read() throws IOException{
		FileReader reader=new FileReader("d:/b.txt");//产生FileNotFoundException异常
                System.out.println("hello");
		char c1=(char)reader.read();//产生IOException异常
	}
}
//运行结果:
Exception in thread "main" java.io.FileNotFoundException: d:\b.txt (系统找不到指定的文件。)
	at java.base/java.io.FileInputStream.open0(Native Method)
	at java.base/java.io.FileInputStream.open(FileInputStream.java:211)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:153)
	at java.base/java.io.FileInputStream.<init>(FileInputStream.java:108)
	at java.base/java.io.FileReader.<init>(FileReader.java:60)
	at day_6_1.FileClass1.read(FileClass1.java:11)
	at day_6_1.FileClass1.main(FileClass1.java:8)

//解释:
read静态方法产生了FileNotFoundException异常和IOException异常,但在方法里并没有去处理它,
而是将它throws到上层,即抛给main方法。因为FileNotFoundException异常是IOException异常的子类,
所以只要抛出IOException异常到上层就可以了。同理,当main方法接收了来自read方法的异常后,
它并没有立刻对异常进行处理,而是继续向上抛,抛给jre去处理(当然在main方法中异常不往上抛,
立刻处理也是可以的)。当read方法中异常代码产生异常后,异常代码后面的代码都不会被执行,
直接被跳过,所以"hello"并没有被打印出来

自定义异常类:当jdk中提供的所有异常类都不能充分描述清楚我们的问题时,我们可以自定义异常类

  1. 只需继承Exception类或者它的任一子类即可
  2. 如果继承Exception类,则自定义异常类为Checked Exception异常类,必须在产生该异常时对其进行处理,如果不想处理,可以继承RuntimeException类,这个时候自定义异常类就是Unchecked Exception异常类,可以通过编译。
  3. 习惯上,自定义异常类应该有两个构造器:一个无参构造器,一个带详细信息的构造器

继承RuntimeException异常类示例代码:

//定义异常类AbnormalAgeException(Unchecked Exception)

public class AbnormalAgeException extends RuntimeException{
	
	public AbnormalAgeException() {
		
	}
	public AbnormalAgeException(String message) {
		super(message);
	}
}

//使用异常类

public class AdolescentInformation {
	int age=18;
	String name;
	void setAge(int age) {
		if(age>0 && age<18) {
			this.age=age;
		}
		else {              
			throw new AbnormalAgeException("年龄超过最大限制");//使用throw手动抛出异常
		}	
	}
	public static void main(String[] args) {
		AdolescentInformation i=new AdolescentInformation();
			i.setAge(20);
}
	
}

//运行结果:
Exception in thread "main" day_6_1.AbnormalAgeException: 年龄超过最大限制
	at day_6_1.AdolescentInformation.setAge(AdolescentInformation.java:11)
	at day_6_1.AdolescentInformation.main(AdolescentInformation.java:16)

//解释:
上面代码中,自定义异常类AbnormalAgeException继承于RuntimeException,所以可以通过编译,不需要去处理它

继承Exception类示例代码:

//自定义异常类AbnormalAgeException(Checked Exception)

public class AbnormalAgeException extends Exception{
	
	public AbnormalAgeException() {
		
	}
	public AbnormalAgeException(String message) {
		super(message);
	}
}

//使用自定义异常类

public class AdolescentInformation {
	int age=18;
	String name;
	void setAge(int age) throws AbnormalAgeException {
		if(age>0 && age<18) {
			this.age=age;
		}
		else {              
			throw new AbnormalAgeException("年龄超过最大限制");//使用throw手动抛出异常
		}	
	}
	public static void main(String[] args) throws AbnormalAgeException {
		AdolescentInformation i=new AdolescentInformation();
		try {
			i.setAge(20);
		}catch(AbnormalAgeException e) {
			e.printStackTrace();
		}
}
	
}

//运行结果:
Exception in thread "main" day_6_1.AbnormalAgeException: 年龄超过最大限制
	at day_6_1.AdolescentInformation.setAge(AdolescentInformation.java:11)
	at day_6_1.AdolescentInformation.main(AdolescentInformation.java:16)

//解释:
因为我们定义的是Checked Exception,当抛出异常时我们必须对异常进行处理,
这里使用的处理异常方式是throws异常给上层处理(即抛给main方法处理)

异常机制的注意事项:

  1. 只在异常情况使用异常机制:逻辑错误不要用异常机制来处理,这样会降低程序的清晰性,处理方式应是增加逻辑处理来避免逻辑错误,也不要用异常机制去代替简单测试
  2. 不要进行小粒度的异常机制,应该将整个任务包装在一个try语句块中
  3. 异常往往在高处处理
  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值