Java异常处理机制

Java异常:当我们编写程序出现错误的时候,系统就会报异常,异常也就是程序在运行时出现的不正常情况,出现的问题有很多种,比较常见的有空指针异常、数组越界异常、文件未找到、算术条件异常等。这些问题都有共性内容:每个问题都有对应的名称,都有问题描述的信息,问题的位置,所以可以不断地向上抽取,形成了异常体系。
Java 异常其实是对不正常情况的一种描述,并将其封装成对象; Java 在设计异常体系时,将容易出现的异常情况都封装成了对象。异常是要尽量避免的,如果避免不了,就需要提前给出处理方式。

我们的Java异常体系分为两大类(可抛出,都继承Throwable父类):
一、Error:错误,一般情况下,不编写针对性的代码进行处理,通常是JVM发生的,程序无法处理的,比如系统崩溃、虚拟机错误、动态链接失败等,这个我们是阻止不了的,这种错误无法恢复或不可能捕获,它将导致应用程序中断。
通常应用程序是无法处理这些错误的,因此应用程序不应该试图使用catch块来捕获Error对象。在定义该方法时,也无需在其throws子句中声明该方法可能抛出Error及其任何子类。
二、Exception:异常,我们可以进行针对性的处理。
处理方式有两种:捕捉异常(try…catch…)和抛出异常(throw、throws)

  • 捕捉异常(try…catch…):直接处理可能出现的异常;
    定义格式:可以同时捕捉多个异常
try{ 
	需要被检测的代码;
}catch(异常类 变量名){
	异常处理代码1;
}
...
catch(异常类 变量名){
	异常处理代码2;
}finally{
	一定会执行的代码(通常用来关闭资源)
}

数组索引越界异常:

public class ExceptionDemo {
		public static void main(String[] args) {
			//数组索引越界ArrayIndexOutOfBoundsException
			String[] strs={"1"};
			intDivide(strs);
		}
		public static void intDivide(String[] strs){//String数组
			try {//try块
				int a=Integer.parseInt(strs[0]);
				int b=Integer.parseInt(strs[1]);
			} catch(ArrayIndexOutOfBoundsException ae){
				System.out.println("数组索引越界:输入的参数个数不够");
				//将该异常的跟踪栈信息输出到标准错误输出
				ae.printStackTrace();//打印错误的追踪信息 
				try{//异常中嵌套异常,打印异常txt文件
					ae.printStackTrace(new PrintStream(new File("E:/z/ArrayIndexOutOfBoundsException.txt"))); 
				}catch(FileNotFoundException e1){
					e1.printStackTrace();
				}			
			} catch(Exception e){ //其它异常
				e.printStackTrace();
			}
		}
}

执行结果:
在这里插入图片描述
使用try…catch捕获异常处理原则:
功能抛出几个异常,功能调用如果进行 try 处理,需要与之对应的 catch 处理代码块,这样的处理有针对性,抛几个就处理几个。

  1. 不要过度使用异常,不能使用异常处理机制来代替正常的流程控制语句
  2. 异常捕获时,一定要先捕获小异常,再捕获大异常。否则小异常将无法被捕获 3. 避免出现庞大的try块
  3. 避免使用catch(Exception e){},要有针对性的处理,抛出什么异常就应该要有对应的catch处理
  4. 不要忽略异常

抛出异常(throw、throws):
throw:用于抛出异常对象,后面跟的是异常对象; throw用在函数内。抛出的是一个异常实例,而且每次只能抛出一个异常实例。

public class ThrowDemo {
	public static void main(String[] args) throws Exception{
		String[] strs={"1"};//数组索引越界ArrayIndexOutOfBoundsException
		try {
			intDivide(strs);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void intDivide(String[] strs) throws Exception {//手动抛异常
		try {//try块
			int a=Integer.parseInt(strs[0]);
			int b=Integer.parseInt(strs[1]);
		} catch (ArrayIndexOutOfBoundsException ae) {
			throw new Exception("数组索引越界");
		}catch (Exception e) {
			System.out.println("其他异常");
			e.printStackTrace();
		}
	}
}

执行结果:
在这里插入图片描述
throws:用于抛出异常类,在方法上抛出可能出现异常的类型,后面跟的是异常类名,可以同时有多个异常类,用逗号隔开。 throws 用在函数上。当前方法不知道如何处理这种类型的异常,该异常应该由上级调用者处理;如果main方法也不知道如何处理这种类型的异常,也可以使用throws声明抛出异常,该异常将交给JVM处理
定义格式:
public 返回值类型 方法名(参数列表) throws 异常类1,异常类2,异常类3,…{ }

public class ThrowsDemo {
	public static void main(String[] args) throws Exception{//抛给Java虚拟机处理
		//算术异常(除零)ArithmeticException
		String[] strs={"21","0"};
		try {
			intDivide(strs);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	public static void intDivide(String[] strs) throws ArithmeticException{
		int c=a/b;
	}
}

执行结果:
在这里插入图片描述
定义异常处理时,什么时候使用try…catch,什么时候使用throws 呢?
功能内部如果出现异常,如果内部可以处理,就用try ;
如果功能内部处理不了,就必须声明出来,让调用者处理。使用 throws 抛出,交给调用者处理。谁调用了这个功能谁就是调用者;

Java的Exception异常分为两种:Checked异常和Runtime异常(运行时异常)。
Runtime异常:运行时异常,所有的RuntimeException类及其子类的实例,也就说这个异常是编译时不被检查的异常;
Checked异常:编译时被检查的异常,只要是 Exception 及其子类都是编译时被检测的异常,也就是除了RuntimeException类及其子类的异常实例
Runtime异常和Checked异常两者之间的区别:
1、Checked异常在函数内被抛出,函数必须要声明,否则编译失败。只有Java语言提供了Checked异常,Java认为Checked异常都是可以被处理(修复)的异常,所以Java程序必须显式处理Checked异常。如果程序没有处理Checked异常,该程序在编译时就会发生错误,无法通过编译。
声明的原因:是需要调用者对该异常进行处理。
2、Runtime异常则更加灵活,Runtime异常如果在函数内被抛出,在函数上无需显式声明抛出,如果程序需要捕获Runtime异常,也可以使用try…catch块来实现,但是一旦出现异常,程序将会中断,若采用异常处理,则会被相应的程序执行处理。
不声明的原因:不需要调用者处理,运行时异常发生,已经无法再让程序继续运行,所以不让调用处理的,直接让程序停止,由调用者对代码进行修正。

自定义异常:当开发时,项目中出现了java中没有定义过的问题时,这时就需要我们按照 java 异常建立思想,将项目的中的特有问题也进行对象的封装。这个异常,称为自定义异常。
用户自定义异常都应该继承Exception基类;如果希望自定义Runtime异常,则应该继承RuntimeException基类。定义异常类时通常需要提供两个构造器:一个是无参数的构造器;另一个是带字符串参数的构造器,这个字符串将作为该异常对象的描述信息(也就是异常对象的getMessage()方法的返回值)
自定义除零异常:

public class CustomException {
		public static void main(String[] args) {
			try {
				sink(3,0);
			} catch (DivideZeroException e) {//捕获自定义异常 
				e.printStackTrace();
			}
		}
public static int sink(int x,int y) throws DivideZeroException{
			if(y==0){ // 抛出自定义DivideZeroException异常
throw new DivideZeroException("您输入的除数是"+y+",除数不能为0!"); 
			}
			int m=x/y;
			return m;
		}
}
//自定义除零异常
class DivideZeroException extends Exception{
		private static final long serialVersionUID = 1L;
		//提供一个无参构造器
		public DivideZeroException(){
		}
		//一个字符串参数的构造器
		public DivideZeroException(String msg){
			super(msg);//把参数调用给super,把参数加入堆栈信息
		}	
}

Java7多异常捕获:一个catch块可以捕获多种类型的异常(不建议使用),多异常捕获之后不好区分异常的类型。
捕获多种类型的异常时需要注意:多种异常类型之间用竖线(|)隔开;异常变量有隐式的final修饰,因此程序不能对异常变量重新赋值。

finally:它是异常的统一出口,不管try…catch代码块出现何种异常,最后的finally块中的语句总是会执行。除非在try…catch块中调用了JVM退出的方法System.exit(0);否则finally块是一定会执行的, 它主要是用来关闭物理资源,无论是否发生异常,资源都必须进行关闭。

public class FinallyDemo {
	public static void main(String[] args){
		RandomAccessFile accessFile=null;
		try {//如果对象文件不存在,在打开流的情况下,IO流是不能被关闭的,因为根本就执行不到关闭流的代码,它已经出现了异常
				accessFile=new RandomAccessFile("E:/z/Array.java","r");//只读模式
			accessFile.writeChars("12345");//写入字符串
			System.out.println("-------");
			accessFile.close();//不会执行到这
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{//一定会执行的代码块
			if(accessFile!=null){
				System.out.println("--12345--");
				try {//关闭流
					accessFile.close();
				} catch (IOException e2) {
					e2.printStackTrace();
				}
			}
		}
	}
}

执行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值