一、为什么要自定义异常类?(以操作一个Student类的属性为例来说明自定义异常类的用途)
1、创建Student类,对其属性进行封装。封装的目的:以公共方法操作私有属性来确保数据的安全性。
package exception;
public class Student {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>0&&age<=150) {
this.age = age;
}else {
System.out.println("你输入的数据不合规");
}
}
}
2、在Main类中创建Student对象,对其属性赋值并查看。
package exception;
public class Main {
public static void main(String[] args) {
Student stu =new Student();
stu.setAge(1000); //对age属性赋值,执行setAge方法的else语句块。
System.out.println(stu.getAge()); // 由于1000不满足if语句判断条件,故未成功赋值,此时的age为默认值0.
}
}
输出结果如下
你输入的数据不合规
0
3、我们来思考一个问题:我们对属性进行封装固然是对的,但是输出结果中的这句“你输入的数据不合规”会让开发者一层一层查看其源代码(当项目代码量相当大时,会明显耗费开发者的精力)。所以我们需要自定义一个异常类,当对属性的操作不合规时就抛出一个异常。开发者通过控制台输出的异常信息来轻松查看其源代码,便于项目的维护。
4、如果jdk中有关于设置属性的异常类的话,我们直接使用此类就行了。但是jdk中并没有这些类,使用其它的异常类又不能起到“见名知意”的效果。所以我们直接来自定义一个异常类吧。
package exception;
// 1、定义一个异常类,如果此类直接或者间接继承RuntimeException,则为运行时异常。否则为检查时异常。
public class AgeException extends RuntimeException{ //此类为运行时异常类。
//2、定义一个构造方法,传入一个String类型的参数,同时调用父类的有参构造方法(目的是为了将异常的信息打印出来)。
public AgeException(String message) {
super(message);
}
}
5、在Student类中抛出自定义异常。
package exception;
public class Student {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>0&&age<=150) {
this.age = age;
}else {
throw new AgeException("age的范围为0~150"); //AgeException为运行时异常,故可以不必对其显式处理。
}
}
}
6、Main类输出结果如下
我们可以点击异常信息直接查看其源代码,增强了代码的可读性。另外需要注意的一点是:Main类中语句“stu.setAge(1000);”爆发了运行时异常,故其后的语句“System.out.println(stu.getAge());” 无法继续执行。与上述输出结果一致。
7、如果AgeException 为检查时异常类的话,代码该如何写呢?
AgeException类代码如下
package exception;
// 1、定义一个异常类,如果此类直接或者间接继承RuntimeException,则为运行时异常。否则为检查时异常。
public class AgeException extends Exception{ //此类为检查时异常类。
//2、定义一个构造方法,传入一个String类型的参数,同时调用父类的有参构造方法(目的是为了将异常的信息打印出来)。
public AgeException(String message) {
super(message);
}
}
由于AgeException为检查时异常,故在Student类中必须对其进行显式处理(try-catch或者 throws)
①使用try-catch处理,
package exception;
public class Student {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
if (age>0&&age<=150) {
this.age = age;
}else {
//AgeException为检查时异常,使用try-catch处理。
try {
throw new AgeException("age的范围为0~150");
} catch (AgeException e) {
e.printStackTrace();
}
}
}
}
②使用throws抛出
package exception;
public class Student {
private int age;
public int getAge() {
return age;
}
public void setAge(int age) throws AgeException {
if (age>0&&age<=150) {
this.age = age;
}else {
throw new AgeException("age的范围为0~150"); //AgeException为检查时异常,使用throws抛出。
}
}
}
③另外值得注意的是,如果Student类中对检查时异常使用try-catch处理,则Main类中后面的语句可以执行。
④如果Student类中对检查时异常使用throws抛出,那么在Main中必须对其进行显式处理。并且Main类在使用throws抛出时,异常语句后面的代码不会运行。
二、在我们明白了为什么要自定义异常类和使用自定义异常类来输出异常信息之后,我们来看下面的两个代码
1、代码1如下
package exception;
public class Test {
static void show(int age) throws AgeException{ //因为此时的检查时异常已经被try-catch处理了,throws可以不写。
if(age<0||age>150){
try {
throw new AgeException("age的范围为0~150");
} catch (AgeException e) {
e.printStackTrace();
}
return;
}
System.out.println("会执行吗"); //不会执行,因为前面有个return。
}
public static void main(String[] args) {
try {
show(1000);
}
//show(1000)即使抛出了异常,catch里面的语句也无法执行,因为show方法已经对AgeException进行了处理。
catch (AgeException e) {
System.out.println("main方法");
e.printStackTrace();
}
}
}
输出结果如下
2、代码2如下
package exception;
public class Test {
static void show(int age) throws Exception{ //show方法里的语句是正确的语句,throws可以不写。
System.out.println("会执行吗");
}
public static void main(String[] args) {
try {
show(1000);
}
//catch里面的语句块不会执行,原因与上个例子一样。
catch (Exception e) {
System.out.println("永远不会执行");
e.printStackTrace();
}
}
}
输出结果如下
三、我们在通过自定义异常类和以上的例子的分析过程中可以归纳出以下特点:
1、异常大致分为运行时异常和检查时异常。检查时异常必须显式处理(try-catch或者throws)。运行时异常不必显式处理,但是如果想让其后的代码正常执行,则需要显式处理。
2、运行时异常类直接或者间接继承RuntimeException,直接继承Exception的类为检查时异常类。
3、throw抛出的是异常对象,可用于方法体或者代码块中。
4、throws只能用于方法体括号后面,不能用于代码块。throws抛出的是类,如果有多个类,使用逗号分隔。