一、引例
新建一个Student类,代码如下:
public class Student {
private int age;
public int getAge() {
return age;
}
public void setAge(int age){
if (age>=1 && age<=150) {//由于生活中人的年龄不可能为负数也不可能太大,所以在此处限定age属性的范围
this.age = age;
}
}
}
新建一个Test类,代码如下:
public class Test {
public static void main(String[] args){
Student student = new Student();
student.setAge(1000);
System.out.println(student.getAge());
}
}
运行代码,结果显示为0。对于编写代码的程序员来说,自然知道是因为赋的值太大,超出了age属性的范围,但是仅仅返回一个0并不能将发生了什么错误,在哪个类的哪行代码处发生了错误告知程序员,就会给修改代码带来极大的不便。那么此时就应该改进代码,明确发生错误的详细信息,此时就可以用异常类对其进行描述。改进代码如下:
public void setAge(int age){
if (age>=1 && age<=150) {
this.age = age;
} else {
throw new NullPointerException("age范围是:1~150");
}
}
运行截图如下图所示:
此时,程序员就可以根据运行结果明确知道程序中发生了什么错误以及错误发生的具体位置,便于修改代码。但此时又出现一个问题,明明是超出了age范围的错误,可是运行结果却显示空指针异常。这是因为Java中并没有定义过这个异常,所以此时,为了更好地描述错误信息,就需要自定义异常。代码如下:
public class AgeException extends RuntimeException {
public AgeException(String msg) {
super(msg);
}
}
运行截图如下图所示:
此时,程序员就可以明确知道是年龄超出范围的错误了。
二、编译时检测异常&运行时异常的区别
1、编译时被检测异常:只要是Exception和其子类都是,除了特殊子类RuntimeException体系。这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。这样的问题都可以针对性的处理。
2、编译时不检测异常(运行时异常):就是Exception中的RuntimeException和其子类。这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的或者引发了内部状态的改变导致的。那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修正。
所以自定义异常时,要么继承Exception。要么继承RuntimeException。
上例中由于AgeException继承RuntimeException,所以在代码中并未对异常进行处理,下面是继承Exception的代码:
Student类中的代码如下:
public void setAge(int age) throws AgeException{
if (age>=1 && age<=150) {
this.age = age;
} else {
throw new AgeException("age范围是:1~150");
}
}
Test类中代码如下:
public class Test {
public static void main(String[] args) throws AgeException{
Student student = new Student();
student.setAge(1000);
System.out.println(student.getAge());
}
}
上述代码中都用throws的方式对异常进行了处理,也可以采用try-catch的方式处理异常,但是如果没有对异常进行处理,程序会报错,编译不通过。
三、throw和throws的区别
1、throw抛出的是异常对象;throws抛出的是类,如果有多个类,则使用逗号间隔。
2、throw用于代码块或者方法体中;throws只能用于方法参数列表后面,不能用于代码块中。
3、如果throw抛出的异常是运行时异常,则可以不使用throws,否则必须显示处理。
四、细节补充
1、如果方法中的异常已经通过try-catch进行了捕获则无需再使用throws上抛该异常了,否则即使上抛也无效,只会做无用功。请看如下代码:
public class Test{
public static void show(int age) throws PrinterException{
if(age<0||age>150){
try {
throw new PrinterException("操作失败:年龄取值范围为0~150");
} catch (PrinterException e) {
System.out.println("show方法");
e.printStackTrace();
}
return;
}
System.out.println(age);
}
public static void main(String[] args) {
try {
show(1000);
} catch (PrinterException e) {
System.out.println("main方法");
e.printStackTrace();
}
}
}
分析:在show方法中异常已经通过try-catch语句进行了处理,所以show方法无需再使用throws抛出异常了,否则即使上抛也无效。执行代码会发现,即使这里异常被触发了,在main方法中catch依然没有执行,所以此时再通过throws抛出异常类纯属无用功。
2、如果方法中的代码或者方法中所调用的代码没有使用throw抛出异常类对象,则无需使用throws否则该throws所抛出的异常类无效。即如果使用throws,则其方法中的代码一定存在使用throw抛出异常对象的代码。请看如下代码:
public class Test {
public static void show(int data)throws Exception{
System.out.println(data);
}
public static void main(String[] args) {
try {
show(100000);
} catch (Exception e) {
System.out.println("永远不会执行");
e.printStackTrace();
}
}
}
分析:由于show方法中只是一个简单的输出语句,方法体和方法体中所调用的方法均没有使用throw抛出异常对象,所以此处没有必要使用throws抛出异常类,即便抛出了也是无用的——由于这里使用了throws抛出了异常类,main方法对该异常类进行了处理,可是我们会发现main方法中的catch语句块永远不可能执行。