引
现有录入学生年龄系统如下:
public class Student {
private int age;//封装
public void setAge(int age) {
if (age > 0 && age < 20) {
this.age = age;
}else {
System.out.println("超出年龄限制!");
}
}
public int getAge(){
return age;
}
}
测试类如下:
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setAge(48);
System.out.println(stu.getAge());
}
}
由于测试类Test中在调用方法setAge时传递的值“48”不满足设定年龄“大于0小于20”的条件,因此结果输出“超出年龄限制!”同时return 0。
使用throw处理异常
用法:throw用于抛出具体异常类的对象,一般用于方法体中。当所写的代码因不满足某些条件致使程序无法运行时可以借助throw抛出一个异常对象提醒程序员。
通过上面实例我们注意到,当不满足设定年龄的条件时,其最终虽然输出“超出年龄限制!”但对程序员来说,不能起到提醒错误的作用。此时,可以使用throw关键字抛出一个异常来起到警示作用,同时这类报错信息也可被写到log日志中。
例如
public class Student {
private int age;//封装
public void setAge(int age) {
if (age > 0 && age < 20) {
this.age = age;
}else {
throw new NullPointerException("超出年龄限制!");//抛出一个异常对象给方法使用者
}
}
public int getAge(){
return age;
}
}
此时运行结果如下:
思考:在上面代码片段中,我们在创建异常对象时使用了NullPointerException空指针异常类型,但程序实际异常原因是因为年龄不符合要求,这样与异常类型相比显得“词不达意”。此时,我们就可以选择自定义异常类型!
自定义异常类型
前面博客中(可以参考:详述异常)已经讲到过,异常的体系是子类与父类的关系,因此我们如果要创建新的自定异常类,那么它必须是已存在异常类的子类(一般继承Exception或RuntimeException)。
1、自定义方法:
1.我们首先在工程目录的src文件夹下创建exception包
2.再在包内创建以我们想要自定异常类型的名称命名(一般格式为:XXException),这里以AgeException为例
3.将该类继承已存在的异常类,这里以继承RuntimeException为例
4.创建有参构造方法并使用super()调用父类方法
5.生成新异常类型UID编码(鼠标放在类名上,出现提示后选择“Add generated serial Version ID”)
6.创建完成!
深入理解throw抛出
在定义了AgeException异常类型后,由于我们定义的异常类是运行时异常的子类,因此上面实例中的代码可以改写为:
public class Student {
private int age;
public void setAge(int age) {
if (age > 0 && age < 20) {
this.age = age;
}else {
throw new AgeException("超出年龄限制!");
}
}
public int getAge(){
return age;
}
}
此时,可以理解为throw的作用是将异常抛给Test类中调用setAge这一方法的语句(student.setAge(48);),在Test类运行到调用setAge方法的这一语句时,由于该语句本身并未做处理,因此该语句将把异常“抛”给JVM虚拟机,虚拟机再根据一定规则区处理异常(JVM处理异常可以参考:详述异常处理方式及try-catch-finally的使用)。
当然,也可对Test测试类中的调用方法语句使用try-catch语句处理异常,因此测试类可以改写为:
public class Test {
public static void main(String[] args) {
Student student = new Student();
try {
student.setAge(100);
} catch (AgeException e) {
e.printStackTrace();
}
System.out.println(student.getAge());
}
}
当然,除了把异常抛给别人(其他类中调用该方法的语句),也可以把异常抛给自己。此时需要在学生类中使用try-catch处理语句throw new AgeException("超出年龄限制!");,这样当程序出现异常时,可由try-catch语句在本类中自行处理,并且异常在本类中处理过后就不必要在Test类中处理。
public class Student {
private int age;
public void setAge(int age) {
if (age > 0 && age < 20) {
this.age = age;
}else {
try {//抛给自己:在本类中已经处理异常
throw new AgeException("超出年龄限制!");
} catch (AgeException e) {
e.printStackTrace();
}
}
}
public int getAge(){
return age;
}
}
思考
如果将我们自定义的异常类改为继承“检查时异常”类,并且学生Studengt类中仍然值只使用throw抛给调用该方法的语句,这一处理方式来处理异常,那么会出现什么后果呢?
public class AgeException extends Exception {
private static final long serialVersionUID = -2280228205254752480L;
public AgeException(String message) {
super(message);
}
}
public class Student {
private int age;
public void setAge(int age) {
if (age > 0 && age < 20) {
this.age = age;
}else {
throw new AgeException("超出年龄限制!");
}
}
public int getAge(){
return age;
}
}
结果就是:由于AgeException变成了检查时异常类型,那么在编写到throw new AgeException("超出年龄限制!");语句时,编译器会检测到异常,并提示需要出错。
那么要在不改动else语句情况下使程序运行,应该怎么办呢?
使用throws处理异常
throws用于声明方法可能抛出的异常,其后为异常类,可以有多个,异常类之间用英文逗号间隔。
什么时候使用:
- 当方法体中的throw关键字抛出由检查时异常创建的对象时,如果该异常对象在抛出的同时已经通过try-catch进行了处理,则可以不使用throws,否则必须使用throws抛出创建该对象的异常类或其父类。
- 所调用的方法抛出了检查时异常时,如果该方法在调用的同时已经通过try-catch进行了处理,则可以不使用throws继续上抛该异常,否则必须使用throws才能上抛该异常,此时上抛的异常类可以是调用方法时方法抛出的异常类也可以是其父类。
联系上面实例,我们就可以利用throws将Student异常类抛出(最终还是抛给JVM处理),达到处理异常的目的。
public class Student throws AgeException {
private int age;
public void setAge(int age) {
if (age > 0 && age < 20) {
this.age = age;
}else {
throw new AgeException("超出年龄限制!");
}
}
public int getAge(){
return age;
}
}
注意:由于AgeException更改为了检测时异常类,因此测试类中调用setAge方法的语句也要使用try-catch或者throws抛出异常类的方法处理异常
小结
- 如果异常对象为运行时异常,则方法参数列表后面可以不使用throws抛出对象,也可以把异常抛给方法调用者
- 如果异常对象为检查时异常,则列表参数后面必须使用throws抛出创建该对象的类;如果没有throws则必须使用try-catch
throw与throws的区别
1.抛出的东西不同:throw抛出的是具体的异常对象,而throws抛出的是抽象的异常类
2.使用位置不同:throw一般用在方法体中,也可用在代码块中,但是如果抛出的是检查时异常类创建的对象,则必须使用try-catch自行处理;throws只能用在方法声明括号后面