异常处理

在程序设计和运行过程中,发生错误是不可避免的。为此,Java提供了异常处理机制来帮助编程者检查可能出现的错误,保证程序的可读性和可维护性。Java将异常封装到一个类中,出现错误时,就会抛出异常。

在程序中,错误可能产生于编程者没有预料到的各种情况,或者是超出了可控范围的环境因素,比如用户的坏数据、试图打开不存在的文件,空指针,数组溢出等等异常。异常在Java语言中也是作为类的实例形式出现的。当某一方法发生错误时,这个方法会创建一个对象,并传递给正在运行的系统。这个对象就是异常对象。通过异常处理机制,可以将非正常情况下的处理代码与程序的主逻辑分离,即在编写代码主流程的同时在其他地方处理异常。

在Java中,如果某个方法抛出异常,既可以在当前方法中进行捕获,然后处理异常;也可以将异常向上抛出,由方法调用者来处理。

捕捉异常

异常捕获结构由try、catch、finally3部分组成。

try语句块中存放的是可能发生异常的Java语句;
catch语句块在try之后,用来激发被捕获的异常;
finally语句块是异常处理结构的随后执行部分,无论try语句中的代码如何退出,最终都会执行finally语句块。

public class Take {

	public static void main(String[] args) {
		try {
			String str = "lili";
			System.out.println(str + "年龄是:");
			int age = Integer.parseInt("20L");
			System.out.println(age);
		}catch(Exception e) {
			e.printStackTrace();
		}
		System.out.println("program over");

	}

}

运行结果:

lili年龄是:
java.lang.NumberFormatException: For input string: "20L"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.base/java.lang.Integer.parseInt(Integer.java:652)
	at java.base/java.lang.Integer.parseInt(Integer.java:770)
	at com.jcy.Take.main(Take.java:9)
program over

从结果中看,程序依然会输出异常信息,没有因为异常而终止。在代码中将可能出现的异常用try-catch语句块进行了处理,当try代码块中的局域发生异常时,程序会跳转到catch中执行,执行完成后继续执行后面代码,而不会停止运行。

Exception是try传递给catch的变量类型,e是变量名。catch中的e.printStackTrace用于输出错误性质。通常情况下,有三种函数可用于获取异常的相关信息:
getMessage():输出错误性质
toString():给出异常的类型与性质
printStackTrace():指出异常的类型、性质、栈层次及出现在程序中的位置。

题目:创建类Number,通过类中的方法count可得到任意两个数相乘的结果,并在调用该方法的主方法中使用try-catch语句捕捉可能发生的异常。

/*
 * @author jiacy
 * @date 2020-2-9
 * @version V1.0
 * 模拟除数为0的异常,查看系统报出的异常
 */
 public class Number {
	public static int count(int m , int n) {
		return m * n;
	}

	public static void main(String[] args) {
		try {
			int result = count(1000,1/0);
			System.out.println(result);
		}
		catch(Exception e) {
			e.printStackTrace();

	}

}
}

运行结果:

java.lang.ArithmeticException: / by zero   //除数为0异常
	at com.jcy.Number.main(Number.java:14)

常见异常

以下为Java中提供的常用语描述发生的错误的异常类。

异常类

说明

ClassCastException

类型转换异常

ClassNotFoundException

未找到相应类异常

ArithmeticException

算术异常

ArrayIndexOutOfBoundsException

数组下标越界异常

ArrayStoreException

数组中包含不兼容的值抛出的异常

SQLException

操作数据库异常类

NullPointerException

空指针异常

NoSuchFieldException

字段未找到异常

NoSuchmethodException

方法未找到抛出的异常

NumberFormatException

字符串转换为数字抛出的异常

NegativeArraySizeException

数组元素个数为负数抛出的异常

StringIndexOutOfBoundsException

字符串索引超出范围抛出的异常

IOException

输入输出异常

IllegalAccessException

不允许访问某类异常

InstantiationException

当应用程序试图使用Class类中的newInstance()方法创建一个类的实例,而指定的类对象无法被实例化时,抛出该异常

EOFException

文件已结束异常

FileNotFoundException

文件未找到异常

自定义异常

通过上述的异常类可以描述在编程时出现的大部分异常情况。除此之外,用户只需继Exception类即可自定义异常。

使用自定义异常类大致可分为以下几个步骤:
1)创建自定义异常类
2)在方法中通过throw关键字抛出异常对象
3)如果在当前抛出的方法中处理异常,可以使用try-catch语句块捕获并处理,否则在方法声明处通过throws关键字指明要抛出给方法调用者的异常,继续进行下一步操作
4)在出现异常方法的调用者中捕获并处理异常

throw关键字通常用于方法体中,并抛出一个异常对象。程序在运行到throw时立即终止,后面的语句不再执行。

举例:
1)先定义自定义异常MyException

public class MyException extends Exception{      //创建自定义异常类
	String message;                              //定义String类型变量
	public MyException(String ErrorMessagr) {    //父类方法
		message = ErrorMessagr;
	}
	public String getMessage() {                 //覆盖getMessage()方法
		return message;
	}

}

2)使用throw关键字捕捉异常

public class Captor {                                       //创建类
	static int quotient(int x ,int y) throws MyException{   //定义方法抛出异常
		if(y<0) {                                           //判断参数是否小于0
			throw new MyException("除数不能是负数");         //异常信息
		}
		return x / y;                                       //返回值
	}

	public static void main(String[] args) {                //主方法
		try {                                               //在try语句中加入可能发生异常的语句
			int result = quotient(3,-1);                    //调用方法quotient()
		}catch(MyException e) {                             //处理自定义异常
			System.out.println(e.getMessage());             //输出异常信息
		}catch(ArithmeticException e) {                     //处理ArithmeticException异常
			System.out.println("除数不能为0");               //输出异常信息
		}catch(Exception e) {                               //处理其他异常
			System.out.println("程序发生了其他异常");        //输出异常信息
		}

	}

}

输出结果:

除数不能是负数

题目:编写一个异常类MyException,再编写一个类Student,该类有一个产生异常的方法speak(int m),要求参数m的值大于1000时,方法抛出一个MyException对象。最后编写主类,在主方法中创建Student对象,让该对象调用speak()方法。

编写异常类MyException

package exercise12;

public class MyException extends Exception{
	public MyException(String ErrorMassage) {
		super(ErrorMassage);
	}

}

编写类Student

package exercise12;

public class Student {
	static void speak(int m)throws MyException{
		if(m > 1000) {
			throw new MyException("数值太大了!");
		}
		
	}
}

编写主类

package exercise12;

public class Sl1209 {

	public static void main(String[] args) {
		Student stu = new Student();
		try {
			stu.speak(2000);
		}catch(MyException e) {
			e.printStackTrace();    //指出异常的类型、性质、栈层次及出现在程序中的位置。
		}

	}

}

运行结果:

exercise12.MyException: 数值太大了!            //类Student中定义的异常抛出
	at exercise12.Student.speak(Student.java:6)//调用查看的异常信息
	at exercise12.Sl1209.main(Sl1209.java:8)

异常的使用原则

Java异常强制用户去考虑程序的健壮性和安全性。异常处理不应用来控制程序的正常流程,其主要作用是捕获程序在运行时发生的异常并进行相应的处理。

对于编写代码处理过程中出现的异常,可遵循如下原则:

1)在当前方法声明中使用try-catch语句捕获异常;
2)一个方法被覆盖时,覆盖它的方法必须抛出相同异常或其子类
3)如果父类抛出多个异常,则覆盖方法必须抛出异常的一个子集,不能抛出新的异常。