什么是异常
非正常的意外事件为异常,即在编译时无法发现错误,运行时才会出现的问题。
Java异常类
异常类是处理运行时错误的特殊类,每种异常类对应一种特定的运行错误。
异常类分为:系统定义异常和用户自定义异常。
系统定义异常
Java中所有的异常类都是java.lang.Throwable的子类。
Throwable类有两个直接子类:Error类和Exception类
Error类
Error类描述的是内部系统错误,包括动态链接失败、虚拟机错误等,Java程序不做处理。不由Java程序处理,用户也无法捕获
Exception类
Exception类描述的是程序和外部环境引起的错误,这些错误能通过程序捕获和处理。Exception出现的原因有:打开文件不存在、网络中断等。
运行时异常
运行时异常是一些可以通过适当处理而避免的异常,在程序中自动由JVM引发,编译时不需要捕获或声明,这类异常是编码时考虑不周产生的,完全可以通过判断来避免的
非运行时异常
非运行时异常通常是由环境因素引起的。Java编译器要求Java程序必须捕获或声明所有的非运行时异常。
Java的异常处理机制
Java异常处理机制分为三个步骤:
1、异常的抛出:程序在执行过程中发生异常,Java虚拟机会发现并产生一个异常对象,这个过程就是异常的抛出。可以由系统来抛出异常,也可以由程序员在代码中使用throw强制抛出某种类型的异常。
2、异常的捕获:出现异常后,Java可以通过try、catch、finally语句来捕获异常,并做相应的处理。
3、如果没有捕获异常的代码,程序将终止运行。
try-catch-finally语句
try和catch实际指:尝试执行这块可能导致异常的代码,如果它执行正常,那么继续执行下面的程序。如果该代码无法执行,捕获该异常并进入catch的代码块运行,由catch代码对它进行处理。最终进入finally语句块中。
格式:try{//可能产生异常的代码放在此处}
catch(异常类名1 异常对象){//在此处处理异常类型1对应的异常}
catch(异常类名2 异常对象名){//在此处处理异常类型2对应的异常}
finally{//最终的程序出口,往往会写一些清理资源的语句放在此处}
try-catch-finally语句执行过程
1、该语句块把可能产生异常的语句放入try{}语句块中。当try代码块中的语句发生了异常,系统将这个异常发生的代码行号、类别等信息封装到一个对象中,并将这个对象传递给catch代码块。
2、在try{}语句后紧跟一个或多个catch块,对try代码块中所产生的异常对象进行处理,每个catch块处理一种可能抛出的特定类型的异常。运行时,如果try{}语句块中产生的异常与某个catch中的异常类型匹配(一场参数类型与实际产生的异常类型一致或者是一个父类),则将停止执行try中的剩余语句,并跳转到catch中执行该catch语句块。
3、finally语句是异常处理的最后一步,为异常处理提供一个统一的出口。无论try做指定的程序块中是否抛出异常、抛出哪种异常,finally语句块都要被执行。通常在finally语句中可以进行资源的清楚工作,如:关闭打开的文件,删除文件。
public class NullExceptionDemo {
Bike bike;//bike没有实例化
public void driveBike() {
bike.run();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
NullExceptionDemo demo=new NullExceptionDemo();
demo.driveBike();//会抛出空指针异常
System.out.println("骑自行车成功");//该处代码不执行
}catch(NullPointerException e) {
System.out.println("出现错误"+e.getMessage());
//getMessage()函数,用于返回有关异常的详细描述性消息,是Throwable类提供的方法,用于得到有关异常事件的信息
//printStackTrace()函数,输出异常类型、栈层次及出现在程序的位置
}
System.out.println("main执行结束");
}
}
class Bike{
public void run() {
System.out.println("开始跑了");
}
}
异常的抛出及声明
一个方法中的语句执行时可能生成某种异常,或者语句执行时存在隐患,但是并不确定如何处理或者不想在此方法中处理,则可以将异常抛给调用该方法的程序,此时可以在方法首部声明该异常,表明该方法将不对这些一场进行处理,而是由该方法的调用者负责处理,若调用者仍不处理这些异常,继续抛,知道main方法也无法处理,抛给JVM处理。
而抛异常用到的是throw和throws
throws
[访问修饰符] 返回值类型 方法名 ([形参列表]) throws 异常类列表{...}
throw
所有系统定义的异常都可以由系统自动抛出,程序员也可以根据实际情况在程序中抛出一个异常,必须在程序中明确抛出异常。
throw new 异常类名([参数列表]); 或
异常类名 对象名=new 异常类名([参数列表]);
throw 对象名;
public class TestThrow {
static int divide(int a,int b)throws Exception{
//divide方法不做异常处理,用throw关键字抛出异常对象给调用者
if(b==0)
throw new Exception("除数为零");
int c=0;
c=a/b;
return c;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int a=3,b=0,c=0;
try {
c=divide(a,b);//divide方法会抛出异常
}catch(Exception e) {//捕获异常
System.out.println("调用divide出现错误:"+e.getMessage());
}
System.out.println("c="+c);
}
}
关于抛出异常,在编写子类继承父类的代码时要注意:一个方法被覆盖时,覆盖它的方法必须抛出跟被覆盖的方法相同的异常或者异常的子类,或者不抛出异常。如果父类抛出多个异常,那么重写(覆盖)的方法必须抛出那些异常的子集,也就是说不能抛出新的异常。
package demo01.test01;
import java.io.IOException;
public class A {
public void method() throws IOException{ }
}
class B extends A{
public void method() throws Exception{ }
}//错误,不能抛出IOException的父类
class C extends A{
public void method() throws IOException, ClassNotFoundException{ }
}//错误,不能抛出新的异常类型
class D extends A{
public void method(){ }
}//正确,不抛出异常
class E extends A{
public void method() throws IOException{ }
}//正确,抛出和父类一样的异常
class F{
public void method() throws IOException,ArithmeticException{ }
}
class G extends F{
public void method() throws NullPointerException{ }
}//正确,抛出的异常类型是被覆盖方法抛出的异常类型的子集
自定义异常
声明的一个新的异常类,这个异常类必须从Java已定义的异常类继承。
因为Java异常处理机机制只能处理Throwable类或其子类的对象,但在Throwable类的子类中Error说系统内部比较严重的错误,不由程序处理,因此一般由自定义异常类实现。
Throwable的几个常用方法:1、String getMessage():返回一个异常的描述;
2、void printStackTrace():把堆栈轨迹输出到指定的输出流;
3、String getLocalizedMessage():返回一个异常的局部描述
public class People {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) throws AgeException {
if(age>=300||age<0) {
throw new AgeException();
}
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static void main(String[] args) {
People pl=new People();
try {
pl.setAge(400);
}catch(AgeException e) {
System.out.println("给age的赋值错误"+e.getMessage());
}
}
}
class AgeException extends Exception{
public AgeException(){
//调用父类的构造方法,将“年龄超过范围”赋值给父类的message变量
super("年龄超过范围");//异常错误信息是固定的
}
public AgeException(String message) {
//调用父类的构造方法,将message传递给父类的message变量
super(message);//异常错误信息由调用者指定
}
}