目录
1.3.2 采用 finally 释放 Scanner 资源
- public class TestException {
- /**
- * 会出现 ArrayIndexOutOfBoundsException 异常
- */
- public void method1(){
- String names[] = {"小凳子", "小椅子", "小桌子"};
- for (int i = 0; i < 4; i++) {
- System.out.println(names[i]);
- }
- System.out.println("over");
- }
-
- public static void main(String[] args) {
- TestException te = new TestException();
- te.method1();
- }
- }
异常的最高父类是 Throwable,在 java.lang 包下。
Throwable 类的构造方法主要有:
public Throwable()
构造一个对象,其错误信息串为 null。
public Throwable(String message)
构造一个对象,其错误信息串为 message。
Throwable 类的方法主要有:
方法 | 说明 |
---|---|
public String getMessage() | 返回对象的错误信息 |
public void printStackTrace() | 输出对象的跟踪信息到标准错误输出流 |
public void printStackTrace(PrintStream s) | 输出对象的跟踪信息到输出流 s |
public String toString() | 返回对象的简短描述信息 |
Exception 类也有很多的子类,提供我们使用
Throwable 类的子类有 Error 错误和 Exception 违例。Error 错误并不能通过程序代码来解决问题,而 Exception 违例基本是程序员在编写代码时所造成的问题,所以往往我们会将 Error 设为天灾,Exception 设为人祸。
采用判断语句的方式进行异常的处理,首先必须意识到哪些地方可能出现异常,在可能出现异常的地方加入判断语句和处理代码。这种处理方式对程序员的要求高,因为开发者很难列举出所有的异常发生情况,而且代码量大,程序结构相对混乱。对于此类情况,一种较好的解决方案,就是使用异常处理机制。
Java 的异常处理机制中仍体现了面向对象的思想。
程序员通常只能处理违例 (Exception) ,而对错误 (Error) 无能为力。
异常处理机制中已经明确提出需要有代码来处理该异常,而出现异常,系统会自动生成一个异常类对象,这个异常对象是系统生成的,我们无须创建。
try 和 catch 是关键字,用来处理异常的。该结构中,catch 语句块中一定不要空着,啥都不写,这是一种很危险的情况,如果空着,表示捕获异常,而不做任何处理。
- try{
- // 有异常出现的语句,无异常语句也可以放在这个语句块中
- }catch(ExceptionClass e){
- // 当产生 ExceptionClass 类型异常时的处置代码
- }catch(ExceptionClass2 e){
- // 当产生 ExceptionClass2 类型异常时的处置代码
- }catch(ExceptionClassN e){
- // 当产生 ExceptionClassN 类型异常时的处置代码
- }]
try 语句块:
catch 语句块:
- public class TestException {
- public void method1(){
- String names[] = {"小凳子", "小椅子", "小桌子"};
- try {
- for (int i = 0; i < 4; i++) {
- System.out.println(names[i]);
- }
- System.out.println("over");
- }
- catch (ArrayIndexOutOfBoundsException e){
- System.out.print(e);
- }
-
- }
- public static void main(String[] args) {
- TestException te = new TestException();
- te.method1();
- }
- }
出现异常,系统自动生成 ArrayIndexOutOfBoundsException 异常对象,交给运行时系统,系统去找处理的代码,当匹配到有 catch 的异常是 ArrayIndexOutOfBoundsException 时,就进入 catch 语句块,本次处理方式是打印异常对象,就是调用 toString() 方法返回对象的简短描述信息。使用 catch 语句块,异常对象捕获到一个匹配的 catch ,将不会再考虑其他了
ArithmeticException 数学计算异常(如出现分母为0)
NumberFormatException 数字格式异常(如输入数字时输入字符串)
InputMismatchException 输入不匹配
- public class TestException {
- public void method2(String a,String b){
- double c = 0.0;
- try{
- c = Integer.parseInt(a) / Integer.parseInt(b);
- }catch(ArithmeticException e){
- System.out.println(e.getMessage());
- }catch(NumberFormatException e){
- e.printStackTrace();
- }
- System.out.println(c);
- }
- public static void main(String[] args) {
- TestException te = new TestException();
- te.method2(args[0], args[1]);
- }
- }
最简化的书写方式,也可以这样写,它们都是 Exception 子类。
- public class TestException {
-
- public void method3(String a,String b){
- double c = 0.0;
- try{
- c = Integer.parseInt(a) / Integer.parseInt(b);
- }catch(Exception e){
- System.out.println(e.getMessage());
- }
- System.out.println(c);
- }
- public static void main(String[] args) {
- TestException te = new TestException();
- te.method3(args[0], args[1]);
- }
- }
之前我们学过的是 final 关键字,而现在需要来学习的 finally 关键字。
finally 关键字主要是和捕获异常的 try-catch 语句一起使用,放在 finally 语句块中的内容表示无条件执行的部分,也就是说不管程序有异常或没有异常都需要执行的部分。
- try{
- // 可能产生异常的代码
- }catch( ExceptionClass1 e ){
- // 当产生 ExceptionName1 类型异常时的处置措施
- }catch( ExceptionClass2 e ){
- // 当产生ExceptionName2型异常时的处置措施
- }finally{
- // 无条件执行的语句
- }
运行时异常:编译的时候都是没有问题,但是运行时会出现异常
RuntimeException 是所有运行时异常的父类,API 如下图:
而实际中有许多都是在编译时会提示需要进行异常处理,否则编译将不能通过。如:
我们可以通过 SimpleDateFormat 类调用 format() 方法来进行日期格式化操作
同时对于以上的代码,我们应该完成转换操作之后,将 SimpleDateFormat 资源进行释放。你就应该要想到的是 finally 语句,无论是否有异常存在,都应该执行的部分,因为它是无条件执行语句块。
企业面试时,经常会提到的问题:final、finally 和 finalize 的区别是什么?
为了避免造成大量的内存占据,我们需要将不在使用的内存空间得到及时的释放。方法结束后,Scanner
对象虽然会进入被回收的队列中,但不是立刻回收,而这会给计算机带来一定的负担。
- import java.util.InputMismatchException;
- import java.util.Scanner;
-
- public class DivideApples {
- //苹果数
- int appleNum = 0;
- //学生数
- int stuNum = 0;
- public void divide() {
- System.out.println("**_现在给孩子们分苹果_**");
- Scanner input = new Scanner(System.in);
- try{
- System.out.print("请输入桌子上有几个苹果:");
- appleNum = input.nextInt();
- System.out.print("请输入班上有几个孩子:");
- stuNum = input.nextInt();
- System.out.println("班上每个孩子分得多少苹果:" + appleNum / stuNum);
- System.out.println("孩子们非常开心!");
- }
- catch (InputMismatchException e){
- System.out.print("苹果数量和孩子人数必须为整数值!");
- }catch (ArithmeticException e){
- System.out.print("孩子人数不能为零!");
- }finally {
- input.close();
- System.out.print("Scanner 对象进行释放");
- }
- }
- public static void main(String[] args) {
- new DivideApples().divide();
- }
- }
异常的另一种处理方式,便是抛出异常,需要使用关键字 throws 。throws 抛出的异常可以是 0 或多个,也就是说·可以抛出 1 个,或多个。
知道会出现错误直接抛出不处理,由调用该方法的对象处理(try-catch),也可以提醒别人。
- public void method2(String a,String b) throws ArithmeticException, NumberFormatException{
- double c = Integer.parseInt(a) / Integer.parseInt(b);
- System.out.println(c);
- }
当然你也可以采用最简化的抛出方式:
- public void method3(String a,String b) throws Exception{
- double c = Integer.parseInt(a) / Integer.parseInt(b);
- System.out.println(c);
- }
直接抛出 Exception,就不用一个个的抛出了,当然从规范的角度来说,在企业项目中,还是需要确切到具体的异常类。
-
- public class TestException4{
- /**
- * 采用异常抛出处理方式进行数组下标越界处理
- * @throws ArrayIndexOutOfBoundsException 数组下标越界异常
- */
- public void method1() throws ArrayIndexOutOfBoundsException{
- String names[] = {"小凳子", "小椅子", "小桌子"};
- for (int i = 0; i < 4; i++) {
- System.out.println(names[i]);
- }
- System.out.println("over");
- }
-
- /**
- * 采用异常抛出处理方式进行数学计算和数字格式化处理
- * @param a 命令行参数
- * @param b 命令行参数
- * @throws ArithmeticException 数学计算异常
- * @throws NumberFormatException 数字格式化异常
- */
- public void method2(String a,String b) throws ArithmeticException, NumberFormatException{
- double c = Integer.parseInt(a) / Integer.parseInt(b);
- System.out.println(c);
- }
-
- /**
- * 采用最简化的方法抛出异常处理
- * @param a 命令行参数
- * @param b 命令行参数
- * @throws Exception 包含了所有的异常
- */
- public void method3(String a,String b) throws Exception{
- double c = Integer.parseInt(a) / Integer.parseInt(b);
- System.out.println(c);
- }
-
- public static void main(String[] args) {
- TestException4 te = new TestException4();
- // 调用 method1 方法进行异常处理
- try{
- te.method1();
- }catch(ArrayIndexOutOfBoundsException e){
- System.out.println(e);
- }
- // 调用 method2 方法进行异常处理
- try {
- te.method2(args[0], args[1]);
- } catch (ArithmeticException e) {
- e.printStackTrace();
- } catch (NumberFormatException e){
- e.printStackTrace();
- }
- // 调用 method3 方法进行异常处理
- try {
- te.method3(args[0], args[1]);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
之前讲到异常处理机制,当系统发现有异常,会自动产生一个异常对象,然后交给运行时系统,这叫抛出异常;主动抛出异常,那就是不需要系统生成异常对象,而是我们在编写代码时,对于可能会出现异常的部分,自己创建异常对象将它抛出,这就是主动抛出异常。
主动抛出异常,其实是希望提供更明确的异常提示信息。
语法结构:
throw new 异常类(message);
注意:这里是 throw 而不是 throws,有很多同学会搞混 throw 和 throws,你只需要记住一点,throw 关键字后只能抛出一个确切的异常类对象,而 throws 后可以抛出多个异常类,而非 new 的对象。
- /**
- * 使用 throw 主动抛出异常
- */
- public class TestException5 {
- /**
- * throw 和 throws 配置使用
- * @throws Exception 异常类
- */
- public void method1() throws Exception{
- String names[] = {"小凳子", "小椅子", "小桌子"};
- for (int i = 0; i < 4; i++) {
- if(i > names.length-1){
- throw new ArrayIndexOutOfBoundsException("数组下标越界异常:数组元素提取下标为 " + i + ",超出数组取值范围!");
- }
- System.out.println(names[i]);
- }
- System.out.println("over");
- }
- /**
- * throw 和 try-catch 配置使用
- * @param a 命令行参数
- * @param b 命令行参数
- */
- public void method2(String a,String b){
- double c = 0.0;
- try{
- int a1 = Integer.parseInt(a);
- if(a1 == 0){
- throw new RuntimeException("第一个参数为 0 ,除法计算永远都是零");
- }
- int b1 = Integer.parseInt(b);
- if(b1 == 0){
- throw new ArithmeticException("除数不能为零");
- }
- c = a1 / b1;
- }catch(ArithmeticException e){
- System.out.println(e);
- }catch(NumberFormatException e){
- e.printStackTrace();
- }catch(RuntimeException e){
- System.out.println(e.getMessage());
- }
- System.out.println(c);
- }
-
- public static void main(String[] args) {
- TestException5 te = new TestException5();
- // 调用 mehtod1 方法进行异常处理
- try {
- te.method1();
- } catch (Exception e) {
- System.out.println(e.getMessage());
- }
- // 调用 method2 方法
- te.method2(args[0], args[1]);
- }
- }
方法重写的要求吗?
我们可以发现,throws 是不是在方法上声明的,对吧 ~ 🙂
那么方法的重写需要加上第三个要求,抛出的异常要么和父类一样,或是父类抛出异常的子类,但是不能是抛出异常的父类。
父类 ClassA 中的 method() 方法:
- public void method() throws ArithmeticException, NumberFormatException{
-
- }
子类 ClassB 中重写父类 method() 方法:
可以只抛出父类中的一个异常类。
- public void method() throws ArithmeticException{
-
- }
或是
- public void method() throws NumberFormatException{
-
- }
或是
- public void method() throws ArithmeticException,NumberFormatException{
-
- }
不抛出异常类也是可以的。
- public void method(){
-
- }
但是抛出的异常不能大于父类抛出的异常,则是错误的。
- public void method() throws Exception{
-
- }
我们简单的复习一下:
异常处理机制,当发现异常,系统将自动生成异常类对象,然后抛出异常交给运行时系统,系统查找是否有可以处理该异常的代码,找到匹配的 catch ,将异常交给它处理即可,如果找不到处理的代码,那么程序将被迫停止。
异常处理的两种方式:
当 API 中提供的异常类不能满足你的需求时,需要自定义异常类。
还有一种情况是,在企业项目中,可以更明确异常位置和信息,我们需要自定义异常类。
自定义异常类,其实很简单,只需满足以下两个要求即可:
语法结构:
- [public] class 类名 extends Exception 或是 Exception 子类{
- // 无参构造器
- public 类名(){
- // 不传递默认异常信息也是可以的
- super("传递默认的异常信息");
- }
-
- public 类名(String messag[, 参数类型 参数名 ...]){
- // 通过父类带一个参数的构造器,传递异常信息
- super(message);
- // 其他数据传递操作,和之前要求的是一样的
- }
- }
注意:对于异常类的类名,一般我们在命名时,可以根据作用功能进行命名,后缀加上 Exception,如:「AgeException」、「PersonException」等等。
判断数组是否为空,如果为空给出异常提示信息。
- class ArrayIsNullException extends Exception{
- // 无参构造器
- public ArrayIsNullException(){
- super("ArrayIsNullException: 数组长度为 0");
- }
- // 带参构造器
- public ArrayIsNullException(String message){
- super(message);
- }
- }
使用自定义异常类:
- public int method1(int[] arrays, int index){
- if(arrays.length == 0){
- throw new ArrayIsNullException();
- }
- return arrays[index];
- }
编译后报错提示我们必须对其进行捕获或声明以便抛出。
修改后:
- /**
- * 自定义异常类的使用
- */
- public class TestException6 {
-
-
- public void method1(int[] arrays, int index) throws ArrayIsNullException {
- if (arrays.length == 0) {
- //throw new ArrayIsNullException();
- throw new ArrayIsNullException("数组为空,提取不到下标 " + index + " 的数组元素。");
- }
- System.out.println(arrays[index]);
- }
-
- public static void main(String[] args) {
- TestException6 te = new TestException6();
- try {
- te.method1(new int[] {}, 2);
- } catch (ArrayIsNullException e) {
- System.out.println(e.getMessage());
- }
- }
- }
-
- // 自定义异常类
- class ArrayIsNullException extends Exception{
-
- /**
- * 无参构造器,传递默认异常信息
- */
- public ArrayIsNullException(){
- super("ArrayIsNullException: 数组长度为 0");
- }
-
- /**
- * 带异常信息的构造器
- * @param message 异常信息
- */
- public ArrayIsNullException(String message){
- super(message);
- }
- }
本实验主要学习自定义异常类,并结合 try-catch 语句和 throws 抛出异常处理。
创建异常类,只需满足以下两个要求:
姓名要求输入不少于 1 个字符且不超过 20 个字符,否则应该抛出异常;年龄只能输入为 18 ~ 35 的 int
类型数字,否则应该抛出异常;性别只能输入 1 或 2 进行选择,否则应该抛出异常,抛出异常后应提示“请重新输入”,并且可以重新输入当前信息。
- import java.util.Scanner;
-
- public class Registration extends Exception{
-
- public Registration() {
- super();
- }
-
- public Registration(String message) {
- super(message);
- }
-
- public static Scanner input = new Scanner(System.in);
-
- public static void main(String[] args) {
-
- System.out.println("请输入您的姓名(不小于 1 个字符且不超过 20 个字符):");
- String name = new String();
- do{
- try {
- name = inputName();
- } catch (Registration e) {
- System.out.println(e.getMessage());
- }
- }while ((name.length()<1)||(name.length()>20));
-
- System.out.println("请输入您的年龄(18~35):");
- int age = 0;
- do{
- try {
- age = inputAge();
- } catch (Registration e) {
- System.out.println(e.getMessage());
- }
- }while (age<18||age>35);
-
-
- System.out.println("请选择您的性别:1. 男 2. 女");
- String gender = new String();
- do{
- try {
- gender = setGender();
- } catch (Registration e) {
- System.out.println(e.getMessage());
- }
- }while (!gender.equals("男")&&!gender.equals("女"));
-
-
- System.out.println("您的报名信息为: 姓名:" + name + ";年龄:" + age + ";性别:" + gender);
- }
-
- /**
- * 输入姓名的方法
- */
- public static String inputName() throws Registration {
- String name=null;
-
- name=input.next();
- if(name.length()>=1&&name.length()<=20){
- return name;
- }
- else {
- throw new Registration("你输入的姓名长度不符合要求,请重新输入:");
- }
- }
-
- /**
- * 输入年龄的方法
- */
- public static int inputAge() throws Registration {
- // 补充代码
- int age=input.nextInt();
- if(age>=18&&age<=35){
- return age;
- }
- else {
- throw new Registration("年龄设置为"+age+"不符合要求,请重新输入:");
- }
- }
-
- /**
- * 选择性别的方法
- */
- public static String setGender() throws Registration {
- // 补充代码
- String gender=input.next();
- if(gender.equals("1")){
- return "男";
- }
- else if(gender.equals("2")){
- return "女";
- }
- else {
- throw new Registration("请输入1或2选择你的性别,请重新输入:");
- }
- }
-
- // 内部异常类
- // 补充代码
-
- }