Java 异常处理机制
Posted 猪八戒1.0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 异常处理机制相关的知识,希望对你有一定的参考价值。
目录
1.3.2 采用 finally 释放 Scanner 资源
1.1Java 异常的概念与分类
1.1.1引入异常(数组越界)
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();
1.1.2 Throwable
异常的最高父类是 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 设为人祸。
采用判断语句的方式进行异常的处理,首先必须意识到哪些地方可能出现异常,在可能出现异常的地方加入判断语句和处理代码。这种处理方式对程序员的要求高,因为开发者很难列举出所有的异常发生情况,而且代码量大,程序结构相对混乱。对于此类情况,一种较好的解决方案,就是使用异常处理机制。
1.2 Java 异常捕获处理
Java 的异常处理机制中仍体现了面向对象的思想。
- Java 程序的执行过程中如出现异常,会自动生成一个异常类对象,该异常对象将被提交给 Java 运行时系统,这个过程称为抛出异常。
- 当 Java 运行时系统接收到异常对象时,会寻找能处理这一异常的代码并把当前异常对象交给其处理,这一过程称为捕获异常。
- 如果 Java 运行时系统找不到可以捕获异常的方法,则运行时系统将终止,相应的 Java 程序也将退出。
程序员通常只能处理违例 (Exception) ,而对错误 (Error) 无能为力。
异常处理机制中已经明确提出需要有代码来处理该异常,而出现异常,系统会自动生成一个异常类对象,这个异常对象是系统生成的,我们无须创建。
try 和 catch 是关键字,用来处理异常的。该结构中,catch 语句块中一定不要空着,啥都不写,这是一种很危险的情况,如果空着,表示捕获异常,而不做任何处理。
try
// 有异常出现的语句,无异常语句也可以放在这个语句块中
catch(ExceptionClass e)
// 当产生 ExceptionClass 类型异常时的处置代码
catch(ExceptionClass2 e)
// 当产生 ExceptionClass2 类型异常时的处置代码
catch(ExceptionClassN e)
// 当产生 ExceptionClassN 类型异常时的处置代码
]
try 语句块:
- 将可能出现异常的代码都放在 try 代码块中。
- try 语句块中发现异常,剩下的语句将不再执行。
catch 语句块:
- 在 catch 语句块中是对异常对象进行处理的代码。
- 每个 try 语句块可以伴随一个或多个 catch 语句,用于处理可能产生的不同类型的异常对象。
- 通过 getMessage( ) 获取异常信息或 printStackTrace( ) 跟踪异常信息。
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]);
1.3 异常处理 finally 语句使用
1.3.1 引入 finally 关键字
之前我们学过的是 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 的区别是什么?
- final 关键字,是用来修饰属性、方法、类的。
- finally 关键字,可以配合异常处理,进行无条件执行操作。
- finalize 不是关键字,是 Object 类中的一个方法,是 Java 垃圾回收机制中进行资源释放的方法。
1.3.2 采用 finally 释放 Scanner 资源
为了避免造成大量的内存占据,我们需要将不在使用的内存空间得到及时的释放。方法结束后,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();
1.3 Java 异常抛出处理
异常的另一种处理方式,便是抛出异常,需要使用关键字 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();
1.3.1 主动抛出异常
之前讲到异常处理机制,当系统发现有异常,会自动产生一个异常对象,然后交给运行时系统,这叫抛出异常;主动抛出异常,那就是不需要系统生成异常对象,而是我们在编写代码时,对于可能会出现异常的部分,自己创建异常对象将它抛出,这就是主动抛出异常。
主动抛出异常,其实是希望提供更明确的异常提示信息。
语法结构:
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
- 学会使用 throw 和 throws 关键字进行异常操作。
- throws 在方法声明的同时抛出异常,可以到调用方法时再进行处理。
- throw 主动抛出异常对象,可以传递更为确切的异常信息。
- 当子类重写父类方法时,需要考虑抛出的异常类,可以相同或是子类,但是不能更大。
1.4 自定义异常类
我们简单的复习一下:
异常处理机制,当发现异常,系统将自动生成异常类对象,然后抛出异常交给运行时系统,系统查找是否有可以处理该异常的代码,找到匹配的 catch ,将异常交给它处理即可,如果找不到处理的代码,那么程序将被迫停止。
异常处理的两种方式:
- 捕获异常,采用 try-catch 语句。
- 抛出异常,采用 throws 在方法声明中抛出。
当 API 中提供的异常类不能满足你的需求时,需要自定义异常类。
还有一种情况是,在企业项目中,可以更明确异常位置和信息,我们需要自定义异常类。
自定义异常类,其实很简单,只需满足以下两个要求即可:
- 声明一个类需要继承 Exception 或是它的子类。
- 提供至少 2 个构造方法,一个无参构造器,一个带参构造器,参数需要包含传递的异常信息。
语法结构:
[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 抛出异常处理。
创建异常类,只需满足以下两个要求:
- 声明一个类需要继承 Exception 或是它的子类。
- 提供至少 2 个构造方法,一个无参构造器,一个带参构造器,参数需要包含传递的异常信息。
1.5 综合案例:
姓名要求输入不少于 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选择你的性别,请重新输入:");
// 内部异常类
// 补充代码
以上是关于Java 异常处理机制的主要内容,如果未能解决你的问题,请参考以下文章