Java之异常处理机制

一 什么是异常?

  • 异常模拟的是现实世界中“不正常”的事件。Java中采用类的方式去模拟现实世界的异常事件
  • 类是可以创建对象的,在Java中因为一类异常是用一个异常类表示的,所以异常类也可以创建对象。比如:NullPointerException npe=0x1234;其中e是一个引用类型的变量,保存的是NullPointerException异常对象的内存地址。
  • 异常处理机制的作用:Java为我们提供了套完善的异常处理机制,作用是【程序发生异常事件后,为我们输出详细的关于该异常的信息,程序员可以通过这些信息,对程序做一些处理,增强程序的健壮性。】
  • 子类中处理的异常范围不能大于父类

二 异常的分类和层次结构

1. 异常的分类

  • 异常可以分为编译时异常和运行时异常
  • 常见的运行时异常:ClassCastException、ArithmeticException、EmptyStackException、IndexOutOfBoundsException、NullPointerException
  • 常见的编译时异常:IOException、InterruptedException

2. 编译时异常和运行时异常怎么区分?

  • 如果一个异常是RuntimeException或者是RuntimeException的子类,那么这个异常是运行时异常。
  • 如果一个异常是Exception的直接子类,且不是RuntimeException,那么这个异常是编译时异常。

3. Sun是怎么定义一个异常是编译时异常还是运行时异常的?

  • 编译时就会出现的异常是编译时异常,运行时才会出现的异常是运行时异常。
  • 编译时异常是在程序中出现的概率较大的异常,而运行时异常是程序中出现的概率较小的异常。
  • 编译时异常程序员必须处理(未雨绸缪),程序员不处理的话,程序无法通过编译,更不可能运行;在编写程序时,程序员不需要处理运行时异常。
  • Sun是怎么强制让开发人员处理编译时异常的呢?比如说,FileInputStream fis=new FileInputStream("文件路径");是我们很常见的一段代码,这段代码有可能会出现FileNotFoundException,并且Sun把FileNotFoundException定义成了编译时异常,如果我们看FileInputStrea的构造函数就会发现,它的构造函数抛出了FileNotFoundException,也就是说只要我们调用FileInputStream的构造函数,这个构造函数就会抛出一个FileNotFoundException,既然抛出了异常,我们就必须处理(向上抛或者try...catch),如果不处理,就无法通过编译,这样就保证了让程序员去处理编译时异常。

三 案例说明Java中的异常处理机制

public class ExceptionDemo {
	public static void main(String[] args) {
		int num1=3;
		int num2=0;
		/*
		 Exception in thread "main" java.lang.ArithmeticException: / by zero
	     at com.donglijiedian.ExceptionDemo.ExceptionDemo.main(ExceptionDemo.java:7)
		 */
		int res=num1/num2;
		System.out.println(res);
	}
}

以上程序编译通过了,但是运行时出现了java.lang.ArithmeticException异常,JVM创建了一个ArithmeticException类型的对象,这个对象中包含了关于这个异常的详细信息,并且JVM将这个对象中的信息输出到控制台

四 异常处理方式之throws

1. throws关键字

  • 异常处理的第一种方式是:声明抛出!在方法声明的位置上使用throws关键字,将异常向上抛出。

2. 案例说明怎么使用throws

下面这段代码是无法编译通过的,因为当我们创建FileInputStream对象时,FileInputStream的构造方法抛出了一个FileNotFoundException异常,我们的程序中并没有对该异常做任何处理,因此无法通过编译。

public class ExceptionDemo {
	public static void main(String[] args) { m1(); }
	public static void m1() { m2(); }
	public static void m2() { m3();}
	public static void m3() {
		FileInputStream fis=new FileInputStream("文件路径");
	}
}

上述问题的一个解决方案就是将异常向上抛出。比如在ExceptionDemo2中,我们将异常一层层向上抛出,直到最后抛给了JVM。这种解决方案是能通过编译了,但是是一种"不负责任"的表现,并没有真正地处理异常,违反了Sun设计异常处理机制的初衷。在这个案例中,最初是FileInputStream的构造函数抛出了一个FileNotFoundException异常,m3()去调用FileInputStream的构造函数,但是m3()没有给出解决方案,抛给了m2();m2()抛给了m1();m1()抛给了main();main()也没有给出解决方案,抛给了JVM。通过编译后,如果在运行过程中,真的出现了FileNotFoundException,那么JVM会创建一个FileNotFoundException对象,并将该对象保存的信息输出到控制台,该程序退出JVM。

public class ExceptionDemo2 {
	public static void main(String[] args) throws FileNotFoundException { m1(); }
	public static void m1() throws FileNotFoundException { m2(); }
	public static void m2() throws FileNotFoundException{ m3();}
	public static void m3() throws FileNotFoundException{
		FileInputStream fis=new FileInputStream("文件路径");
	}
}

如果想对异常给出解决方案,那么可以使用try...catch的方式

五 异常处理方式之try...catch

1. 捕捉异常

  • 处理异常的第二种方式是使用try...catch捕捉异常并处理。
  • catch语句块可以写多个,但是捕捉异常时,必须从小类型到大类型进行捕捉。
  • try...catch中最多执行一个catch分支

语法如下:

try {
       //可能出现异常的代码块
    } catch (异常类型1 e) {
       //处理异常的代码
    }catch (异常类型2 e) {
       // 处理异常的代码
    }....

2. 异常类型的对象中常用的方法

  • getMassage():输出关于异常的一些信息,但是信息较少
  • printStackTrace:输出异常的一些信息,信息较多,便于程序员处理异常

3. 案例说明try...catch处理异常

【方式一】可以处理FileNotFoundException异常,即使在运行的过程中出现了异常,那么【System.out.println("异常处理机制");】也能被执行。

//方式一
public class ExceptionDemo {
	public static void main(String[] args){
		try {
			FileInputStream fis=new FileInputStream("C:/abc.txt");
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		System.out.println("异常处理机制");
	}
}

【方式二】也可以处理FileInputStream抛出的FileNotFoundException异常。FileNotFoundException继承自IOException,运用了多态机制

//方式二
public class ExceptionDemo {
	public static void main(String[] args){
		try {
			FileInputStream fis=new FileInputStream("C:/abc.txt");
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println("异常处理机制");
	}
}

【方式三】编译不能通过,因为catch语句块虽然可以写多个,但是必须从上到下,从小到大,而这段代码中IOException比FileNotFoundException的处理范围要大

//方式三
public class ExceptionDemo {
	public static void main(String[] args){
		try {
			FileInputStream fis=new FileInputStream("C:/abc.txt");
		} catch (IOException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		System.out.println("异常处理机制");
	}
}

4. finally语句块

  • finally语句块可以直接跟在try后面,也可以跟在catch后面
  • 除非编译不通过,或者在try中执行 System.exit(...);finally都会执行
public class ExceptionDemo {
	public static void main(String[] args){
		try {
			int res=2/0;//该语句会抛出ArithmeticException异常
			System.out.println("try中输出");//不会执行
		} catch (ArithmeticException e) {
			//捕捉异常并处理
			e.printStackTrace();
		}finally {
			System.out.println("finally中输出");//会执行
		}
		System.out.println("异常处理机制");//会执行
	}
}

下面这段代码说明如果在try中执行System.exit(0),那么在finally中语句不会再执行

public class ExceptionDemo {
	public static void main(String[] args){
		try {
			System.out.println("try中语句");//执行
			System.exit(0);
		} finally {
			System.out.println("finally中语句块");//不执行
		}
	}
}

下面这段代码我不是很理解

public class ExceptionDemo {
	public static void main(String[] args){
		int res=m();
		System.out.println("res="+res);//输出10
	}
	public static int m() {
		int i=10;
		try {
			return i;
		} finally {
			i++;
			System.out.println(i);//输出11
		}
	}
}

六 自定义异常和手动抛出异常

自定义编译时异常继承Exception,自定义运行时异常继承RunTimeException

下面这个案例是判断用户输入的用户名是否符合规范(长度大于6),如果不符合规范就抛出一个自定义的IlleagleNameException.

class IlleagleNameException  extends Exception{
	public IlleagleNameException() {}
	public IlleagleNameException(String message) {
		super(message);
	}
}
class CustomerService {
	public void register(String name) throws IlleagleNameException {
		if (name.length()<6) {
			//手动抛出异常
			throw new IlleagleNameException("用户名长度不能少于6位");
		}
	}
}
class Test {
	public static void main(String[] args) {
		String name="jack";
		CustomerService cs=new CustomerService();
		try {
			cs.register(name);
		} catch (IlleagleNameException e) {
			System.out.println(e.getMessage());
		}
		//控制台会输出“用户名不能少于6位”
	}
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值