Java异常处理机制

1.什么是异常

异常由来:问题也是显示生活中一个具体的事物,也可以通过java类的形式进行描述,并封装成对象。 java分为两种问题,一种是严重的问题,一种是非严重的问题。 对于严重的,java通过Error类进行描述。 对于Error一般不编写针对性的代码对其进行处理。 对于非严重的,java通过Exception类进行描述。也就是异常。   无论Error或者Exception都具有一些共性内容。 比如:不正常情况的信息,引发原因等。
异常:就是程序在运行时出现的不正常情况。
异常的体系:
Throwable
	|----Error
	|----Exception
		|----RuntimeException

2.异常的处理

java提供了特有的语句进行处理
try {
需要被检测的代码
} catch {
处理异常的代码
} finnally {
一定会执行的语句
}

例子1

class Demo {
	int div(int a, int b) {
		return a/b;
	}
}
public class ExceptionDemo {
	public static void main (String[] args) {
		Demo d = new Demo();
		int x = d.div(4, 0);
		System.out.println("x=" + x);
		
		System.out.println("over");
	}
}
上面代码的运行结果是:

首先我们要知道java虚拟机自带了异常处理机制,当我们除0之后,java虚拟机可以识别到这个异常,但是虚拟机处理的方式简单粗暴,直接输出异常信息,并且程序停止运行,出现异常的语句后面的代码都不会被执行。

下面我们给给程序加上自己的异常处理

class Demo {
	int div(int a, int b) {
		return a/b;
	}
}
public class ExceptionDemo {
	public static void main (String[] args) {
		Demo d = new Demo();
		try {
			int x = d.div(4, 0);
			System.out.println("x=" + x);
		} catch (Exception e) {
			System.out.println("不能除0");
			
			System.out.println(e.getMessage());
			System.out.println(e.toString());
			
			e.printStackTrace();
		}
		
		System.out.println("over");
	}
}

运行结果:


可以看到over输出了,说明程序运行到了最后输出语句,那么异常处理的过程到底是怎样的,下面来分析一下。

1.首先程序运行到d.div(4,0)那里,调用Demo类的div函数,在函数中执行4/0,这时程序发生了不能除0异常,这个异常已经被java事先定义好了,就是ArithmeticException,在这里会把这个ArithmeticException封装成一个对象,相当与new ArithmeticException()。

2.发生这个异常的Demo类的div并没有处理异常的能力,它就把这个异常抛给了调用它的函数,也就是主函数,在主函数的try代码块了就有了这个算术异常,而try就是用来检测异常的,当这个异常被检测到之后,就会把它丢给了catch。

3.那么这个catch怎么接呢,可以看到catch后面定义了(Exception e)这个参数,因此catch接受到异常后其实相当于这样,Exception e = new ArithmeticException(), ArithmeticException是Exception的子类。然后就开始执行catch里面的代码。并且后面的程序也可以正常执行了。

P.S:1.如果在第2步当中,div函数把异常抛给了主函数之后,主函数也没有try-catch来处理这个异常,那么这个异常就会被主函数继续往上抛,抛给我们的虚拟机,虚拟机会停止程序,默认输出异常信息,也就是第一个例子。

   2.在try代码块里,出现异常后面的语句是不会执行了,比如上面的System.out.println("x=" + x);

   3.在这个例子中的catch打印那么多信息是为了让大家知道e.printStackTrace();到底是怎么构成的,可以看到e.getMessage只输出了异常信息,etoString输出了异常名称和异常信息,而e.printStackTrace最完整,包括了异常名称,异常信息,还有异常发生的位置,java虚拟机默认处理其实就是输出它。


例子2

上面那个例子存在一个问题,如果Demo是别人写的代码,我们调用里面的函数其实并不知道它是否会报异常,只有运行之后出错了才知道有异常,这是非常不科学的,所以我们必须在写代码的时候就的预判是否会发生异常,如果可能发生异常就声明异常,告诉调用者这代码有可能会发生异常,我们看下面的代码

class Demo {
	int div(int a, int b) throws Exception{
		return a/b;
	}
}
public class ExceptionDemo {
	public static void main (String[] args) {
		Demo d = new Demo();
		
		int x = d.div(4, 0);
		System.out.println("x=" + x);
		
		System.out.println("over");
	}
}

我们先把主函数中的try-catch语句去掉,然后在Demo类中的div函数声明异常,throws Exception,这代表这个函数有可能会发生异常,希望调用他的人能够处理一下。在Eclipse中,主函数div那里会飘红,它希望你给div加上异常处理,如果在控制台编译的会发生下面的错误


也就是说,如果你在某函数上声明异常,调用这个函数的人可以有两种解决办法,一种是用try-catch进行捕获,自己写代码解决,另外一种是调用的人继续将这个异常抛出,也就是在主函数上也加上throws Exception,最终这个异常会被抛给java虚拟机,让程序停止运行并输出错误信息。


例子3:多异常处理

例子2的异常声明并不规范,在平时写代码的时候我们不能笼统的将异常定义为Exception,必须具体问题具体处理,看代码

 class Demo {

	int div(int a, int b) throws ArithmeticException,ArrayIndexOutOfBoundsException{
		int[] arr = new int[a];
		System.out.println(arr[4]);
		
		return a/b;
	}
}
public class ExceptionDemo {
	public static void main (String[] args) {
		Demo d = new Demo();
		
		try{
			int x = d.div(4, 0);
			System.out.println("x=" + x);
		} catch (ArithmeticException e) {
			System.out.println("不能除以0");
		} catch (ArrayIndexOutOfBoundsException e) { 
			System.out.println("越界了");
		}/* catch (Exception e) {
			System.out.println(e.toString());
		}*/
		System.out.println("over");
	}
}
抛出几个异常,我们就catch几个异常,分别处理,不要定义多余的catch块,另外需要注意的是,如果多个catch块中出现异常的继承关系,父类catch块放在下面,否则子类就没有存在的意义。比如把上面的Exception提到前面,那么算术异常和角标越界异常都无效了,因为Exception都可以处理,这种做法不提倡。还有最好不要在最后加上Exception,这样虽然当函数出现我们声明之外的异常之后,程序也可以运行,但是你无法确定知道该问题是什么,相当于你把问题隐藏起来了,会造成一定的隐患,所以宁愿开发的时候让程序停止报错,找出问题所在去处理。

例子4:自定义异常

因为项目中会出现特有的问题,而这些问题并未被java所描述并封装成对象,所以对于这些特有的问题可以按照java对问题封装的思想,将特有的问题进行自定一的封装。
比如在上面的代码中,我们设定,对于负数,也视为错误的,无法进行运算,那么就需要对这个问题进行自定义的描述。
class FuShuException extends Exception{
	private int value;
	
	FuShuException() {
		super();
	}
	FuShuException(String msg, int value) {
		super(msg);
		this.value = value;
	}
	public int getValue() {
		return value;
	}
}
class Demo {
	int div(int a, int b) throws FuShuException{
		if(b < 0)
			throw new FuShuException("出现除数是负数的情况", b);
		return a/b;
	}
}
public class ExceptionDemo {
	public static void main (String[] args) {
		Demo d = new Demo();
		
		try {
			int x = d.div(4, -1);
			System.out.println("x=" + x);
		} catch (FuShuException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("错误的负数是" + e.getValue());
		}
		System.out.println("over");
	}
}
运行结果:


1.要自定义异常,我们先得新建一个类并且继承Exception
2.当除数为0时,由于java已经有ArithmeticException对它进行描述,java可以自动识别,但是我们自己定义的异常java并不知道,所以我们必须用throw关键字,手动将异常信息抛出。
3.自定义异常的有参构造函数是为了加上自己描述的异常信息,由于FuShuException继承了Exception,所以不必自己写函数输出异常描述,调用父类的函数就可以了。除了异常的描述,我们还可以获取我们需要的信息,比如上面的代码还获取到底出现了哪个负数。
P.S:1.throws用在函数上,后面跟的是异常类
2.throw用在函数内,后面跟的是异常对象

         

例子5:RuntimeException

当throw一个异常的时候,我们要么直接try-catch,要么把异常声明抛出,否则就会编译失败,但有一个种类的异常非常特殊,那就是Runtime异常,这类异常不需要我们声明也能编译通过。
class Demo {
	int div(int a, int b) {
		if(b < 0)
			throw new ArithmeticException();	//编译通过
		//	throw new Exception();		//编译失败
		return a/b;
	}
}
public class ExceptionDemo {
	public static void main (String[] args) {
		Demo d = new Demo();
		
		int x = d.div(4, 0);
		System.out.println("x=" + x);
		
		System.out.println("over");
	}
}
之所以不用在函数上进行声明,是因为不需要让调用者处理,当该异常发生,希望程序停止,对代码进行修正。
举算术异常可能不是特别恰当,举个别的,比如说空指针异常,if(name.euqals("lisi")),如果name为空,会抛出空指针异常,如果硬给这个函数加个函数声明让调用者去处理这个空指针异常,就会把这个问题给隐藏掉。在比如,Object类中有一个wait函数


    暂且不论wait函数是干嘛用的,我们看到wait函数有三个异常,但是函数上只声明了一个,其另外两个就是运行时异常。我们再看它的参数,是时间。如果传了一个负数的时间,这程序根本没有必要继续运行下去,出现这种情况,我们不应该把这个异常给处理掉,而应该让程序停止,修改程序,传入一个正确的时间。
所以异常分两种,
1.编译时被检测的异常
     该异常在编译时,如果没有处理(没有抛也没有try),编译失败
     该异常被标识,代表这可以被处理
2.运行时异常(编译时不检测)
     在编译时,不需要处理,编译器不检查
     该异常的发生,建议不处理,让程序停止,需要对代码进行修改

 

例子6:try-catch-finally

<pre name="code" class="java">class FuShuException extends Exception{
	private int value;
	FuShuException(String msg, int value) {
		super(msg);
		this.value = value;
	}
	public int getValue() {
		return value;
	}
}
class Demo {
	int div(int a, int b)throws FuShuException{
		if(b < 0)
			throw new FuShuException("出现负数了", b);	
		return a/b;
	}
}
public class ExceptionDemo {
	public static void main (String[] args) {
		Demo d = new Demo();
		
		try {
			int x = d.div(4, -1);
			System.out.println("x=" + x);
		} catch (FuShuException e) {
			System.out.println(e.toString());
			return;
			//System.exit(0);  //这个例外,这代表jvm退出,finally不会执行
		} finally {
			System.out.println("finally");
		}
		System.out.println("over");
	}
}


 
 
运行结果:
当catch捕获到异常,并且return之后,最后的over不会执行,但是finally一定会执行。连接数据库时经常在finally中写上断开连接的代码。
例子7:异常在子父类重写时的体现
1.子类在重写父类时,如果父类方法抛出异常,那么子类重写的方法,只能抛出父类的异常,或者该异常的子类。
2.如果父类方法抛出多个异常,那么子类在重写该方法时,只能抛出父类异常的子集。
3.如果父类或者接口的方法中没有抛出异常,那么在子类重写方法时,也不可以抛出异常。
	如果子类方法发生了异常,就必须要进行try处理,绝对不能抛。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值