由于现实世界中,学生的年龄不可能为负数或者太大,我们可以用公有方法来封装私有属性(age),进而约束属性
代码如下:
public class Student { private int age; //如果是public 没办法约束 public int getAge() { return age; } public void setAge(int age) { if (1<=age && age<=150) { this.age = age; }else { System.out.println("错误,注意age范围"); } } }
创建测试类进行测试,代码如下:
public class Test { public static void main(String[] args) { Student student = new Student(); student.setAge(10000); System.out.println(student.getAge()); } }
运行结果:错误,注意age范围
0分析结果:仅仅返回一个0并不能知道具体发生了什么错误,带来极大的不便。如果想知道具体原因就需要查代码,如果代码太多的情况下,查起来就特别麻烦。那么此时就应该改进代码,明确发生错误的详细信息,此时就可以用异常的知识对其进行描述
Java中的异常:
Java语言将程序运行过程中所发生的不正常严重错误称为异常,对异常的处理称为异常处理。 它会中断正在运行的程序,正因为如此异常处理是程序设计中一个非常重要的方面,也是程序设计的一大难点。
异常分类:
Throwable有两个直接子类,Error类和Exception类。
Error : 指合理的应用程序在执行过程中发生的严重问题。当程序发生这种严重错误时,通常的做法是通知用户并中止程序的执行。
Exception:异常可分为运行时异常(RuntimeException)和检查时异常(CheckedException)两种:
RuntimeException:运行时异常,即程序运行时抛出的异常。这种异常在写代码时不进行处理,Java源文件也能编译通过。 RuntimeException异常类及其下面的子类均为运行时异常,即一定会直接继承RuntimeException类或者间接继承。
CheckedException:检查时异常,又称为非运行时异常,这样的异常必须在编程时进行处理,否则就会编译不通过。Exception异常类及其子类(除去RuntimeException异常类及其子类)都是检查时异常。
获取异常信息
常用的方法:public String getMessage():获取异常信息; public void printStackTrace():输出异常堆栈中的异常信息
常用的构造方法: public Exception():无参构造方法; public Exception(String message):可以指定详细异常信息的有参构造方法;
捕获异常
语法:
try{ //可能抛出异常的语句块 }catch(SomeException1 e){ // SomeException1特指某些异常 //当捕获到SomeException1类型的异常时执行的语句块 } catch( SomeException2 e){ //当捕获到SomeException2类型的异常时执行的语句块 }finally{ //无论是否发生异常都会执行的代码 }
try…catch…finally异常处理结构中,try语句块是必须的, catch和finally语句块至少出现一个。
注意:如果try语句块包含的是检查时异常,则在没有通过throws抛出该异常类的情况下,try必须和catch一起使用,当该行代码去掉或注销掉时,catch相应的异常语句块必须去掉
多重catch: try语句块中的代码可能会引发多种类型的异常,当引发异常时,会按顺序查看每个 catch 语句,并执行第一个与异常类型匹配的catch语句,其后 catch 语句被忽略。在捕获异常的时候,应按照“从小到大”的顺序捕获异常,即先子类后父类。
Java异常在try/catch块后加入finally块,可以确保无论是否发生异常finally块中的代码总能被执行。
抛出异常
throw
throw用于抛出具体异常类的对象,一般用于方法体中, 也可以用在代码块中,但如果代码块中抛出的异常对象是由检查时异常创建的,则必须使用try-catch或throws进行处理;
public class Test { int a = 9; int b = 0; { if (b == 0) { try { throw new Exception("操作失败:分母不能为0"); } catch (Exception e) { e.printStackTrace(); } } System.out.println(a / b); } public static void main(String[] args) { new Test(); } }
什么时候使用:当所写的代码因不满足某些条件致使程序无法运行时可以借助throw抛出一个异常对象提醒程序员。
throws
throws用于声明方法可能抛出的异常,其后为异常类,可以有多个,异常类之间用英文逗号间隔。
什么时候使用: 当方法体中的throw关键字抛出由检查时异常创建的对象时,如果该异常对象在抛出的同时已经通过try-catch进行了处理,则可以不使用throws,否则必须使用throws抛出创建该对象的异常类或其父类。
所调用的方法抛出了检查时异常时,如果该方法在调用的同时已经通过try-catch进行了处理,则可以不使用throws继续上抛该异常,否则必须使用throws才能上抛该异常,此时上抛的异常类可以是调用方法时方法抛出的异常类也可以是其父类。
如果方法中的异常已经通过try-catch进行了捕获则无需再使用throws上抛该异常了,否则即使上抛也无效,只会做无用功。
import java.awt.print.PrinterException; public class T { public static void show(int age) throws PrinterException { if (age < 0 || age > 150) { try { throw new PrinterException("操作失败:年龄取值范围为0~150");// 此处异常已经通过try-catch语句进行了处理,所以show方法无需再使用throws抛出异常了,否则即使上抛也无效:执行代码会发现,即使这里异常被触发了,在main方法中catch依然没有执行,所以此时在通过throws抛出异常类纯属无用功 } 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(); } } }
去除无用功,代码如下:
import java.awt.print.PrinterException; public class T { public static void show(int age) { if (age < 0 || age > 150) { try { throw new PrinterException("操作失败:年龄取值范围为0~150");// 此处异常已经通过try-catch语句进行了处理,所以show方法无需再使用throws抛出异常了,否则即使上抛也无效:执行代码会发现,即使这里异常被触发了,在main方法中catch依然没有执行,所以此时在通过throws抛出异常类纯属无用功 } catch (PrinterException e) { System.out.println("show方法"); e.printStackTrace(); } return; } System.out.println(age); } public static void main(String[] args) { show(1000); } }
如果方法中的代码或者方法中所调用的代码没有使用throw抛出异常类对象,则无需使用throws否则该throws所抛出的异常类无效:即使用throws则其方法中的代码一定存在使用throw抛出异常对象的代码。
public class Te { 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(); } } }
运行结果:100000
由于该方法中只是一个简单的输出语句,方法体和方法体中所调用的方法均没有使用throw抛出异常对象,所以此处没有必要使用throws抛出异常类,即便抛出了也是无用的——由于这里使用了throws抛出了异常类,main方法对该异常类进行了处理,可是我们会发现main方法中的catch语句块永远不可能执行
throw和throws的区别:
抛出的东西不同:throw抛出的是具体的异常对象,而throws抛出的是抽象的异常类;
使用位置不同:throw一般用在方法体中,也可用在代码块中,但是如果抛出的是检查时异常类创建的对象,则必须使用try-catch自行处理;throws只能用在方法声明括号后面;
自定义异常类
将最上面的代码改成抛出异常的方式:
public void setAge(int age) { if (1<=age && age<=150) { this.age = age; }else { throw new NullPointerException("错误,注意age范围"); //System.out.println("错误,注意age范围"); } }
运行结果:
这时报错后知道具体原因和具体位置。可这时词不达意,不是空指针异常,jdk没有符合的年龄异常,可以自定义异常类:
1.定义一个类,继承已有的异常类
2.定义一个构造方法,调用父类构造方法
//定义一个类,继承已有异常类 public class AgeException extends RuntimeException { //定义构造方法,调用父类构造方法 public AgeException(String message) { super(message); } }
public class Student { private int age; //如果是public 没办法约束 public int getAge() { return age; } public void setAge(int age) { if (1<=age && age<=150) { this.age = age; }else { throw new AgeException("错误,注意age范围:1-150"); //System.out.println("错误,注意age范围"); } } }
public class Test { public static void main(String[] args) { Student student = new Student(); student.setAge(10000); System.out.println(student.getAge()); } }
如果将上面自定义的异常类改成继承检查时异常,代码如下
//定义一个类,继承已有异常类publicclassAgeExceptionextendsException{//定义构造方法,调用父类构造方法publicAgeException(String message){super(message); } }
此时,因为检查时异常需要在编程时进行处理,否则编译不通过,代码如下:
publicclassStudent{privateint age;//如果是public 没办法约束publicintgetAge(){return age; }publicvoidsetAge(int age)throws AgeException{if (1<=age && age<=150) {this.age = age; }else {thrownew AgeException("错误,注意age范围:1-150");//System.out.println("错误,注意age范围"); } } }
publicclassTest{publicstaticvoidmain(String[] args){ Student student =new Student();try { student.setAge(10000); }catch (AgeException e) { e.printStackTrace(); } System.out.println(student.getAge()); } }