java-异常机制
/**
* 传统处理异常的方式
*/
public class anomaly01 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); //Scannner:用于数据输入/也就是在键盘上输入
System.out.println("请输入被除数 :");//scanner.hasNextInt() 整型
if (scanner.hasNextInt()){
int num = scanner.nextInt();
System.out.println("请输入除数 :");
int num1 = 0;
if (scanner.hasNextInt()){
num1 = scanner.nextInt();
if (0 == num1){
System.out.println("除数不能为 0");
}else {
int num2 = num / num1;
System.out.println(num2);
}
}else {
System.out.println("除数输入有误!");
}
}else {
System.out.println("被除数输入有误!");
}
}
}
Java的异常处理机制相对于传统的异常处理方式有什么优势
1、传统的异常处理方式表达异常的能力非常有限,光凭函数返回值来承载程序的异常信息是非常不够的,尤其是在复杂的系统中使用这种异常处理方式将很难定位问题的类型以及出现的原因。/传统
而在Java中把各种不同类型异常情况进行分类,用Java类来表示异常情况,这种类被称为异常类。把异常情况表达成异常类的形式,可以充分利用类的扩展性和可重用性。/机制
2、传统的异常处理方式,将异常处理逻辑嵌入到正常的逻辑代码中,增加了系统的复杂性。/传统
在Java中异常代码和正常的逻辑代码是分开的,提高了程序的可读性,简化了程序的结构。/机制
3、随着系统规模的扩大,传统的异常处理方式使得系统变得不可控,称为维护大型系统的障碍。/传统
在Java中,可以灵活处理异常,如果当前方法有能力处理这个异常,就将其捕获并且处理这个异常,否则可以将异常抛出,有调用者来作进一步的处理。/机制
一 异常概述
异常就是程序在运行时出现意外的,不正常的情况。若异常产生后没有正确的处理,会导致程序的中断,程序不继续执行以造成损失。
二 Java 异常分类
异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程。Java 通 过 API 中 Throwable 类的众多子类描述各种不同的异常。因而,Java 异常都是对象,是 Throwable 子类的实例,描述了出现在一段编码中的错误条件。当条件满足时,错误将引发异常。
Java 异常的继承层次结构:
图片中我们看到 Java 异常的基类是 Throwable 类型,然后它有两个子类 Error(错误)和 Exception()类型,然后 Exception 类又分为运行时异常(RuntimeException)及非运行时异常(编译异常)。以下将分别介绍。
Error(错误)
Error 一般表示编译时或者系统错误,例如:Virtual MachineError(虚拟机运行错误),StackOverflowError:栈溢出错误、OutOfMemoryError:内存不足错误等。此类错误开发者无法处理且不可恢复,JVM 将终止线程,因此不应该实现任何新的 Error 子类。
Exception(异常)
程序本身可以捕获并且可以处理的异常,也就是要么捕获异常并作出处理,要么继续抛出异常。Exception 这种异常又分为两类:运行时异常和非运行时异常。
运行时异常(非受检查异常)
RuntimeException 及其子类都统称为运行时异常,例如:NullPointExecrption(空指针)、ArithmeticException(算术错误)、ArrayIndexOutOfBoundsException(数组越界)等。
ArrayIndexOutOfBoundsException:(数组越界)用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。
ArithmeticException:(算术错误)当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
NullPointerException:(空指针)当应用程序试图在需要对象的地方使用null时,抛出该异常。
这类异常是非受检查的,具体根据需要来判断是否需要捕获和处理,并不会在编译期强制要求。开发者应该从程序编码阶段就尽可能避免其发生。
runtime异常,顾名思义在编译时期不被检测,只有在运行时期才会被检查出来。运行异常可以不使用try…catch处理,但一旦出现异常就将由JVM处理(打印堆栈信息)。RuntimeException(运行时异常)通常是指因设计或实现方式不当而导致的问题。程序员小心谨慎是可以避免的异常。如:事先判断对象是否为null就可以避免NullPointerException异常,事先检查除数不为0就可以避免ArithmeticException异常。运行时异常特点:在编译阶段,Java编译器检查不出来。一般的,程序可以不用使用try-catch和throws处理运行异常。
运行时异常:程序可处理,也可不处理。都继承于RuntimeException
InputMismatchException:输入不匹配异常
ArithmeticException:数学计算异常,例如除数为0
ArrayIndexOutOfBoundsException:数组越界异常
NumberFormatException:数字格式化异常例如:“abc”转成数字
NullPointerException:空指针异常
非运行时异常/编译时异常(受检查异常)
RuntimeException 以外的异常都属于非运行时异常。例如 IOException、SQLException 等以及用户自定义的 Exception 异常等。
这类异常是受检查的,在源代码里必须显式地进行捕获处理,是编译期检查的一部分。这种异常要么用 try-catch 语句捕获它,要么用 throws 子句声明抛出它,否则编译不会通过。
编译被检查异常,顾名思义就是在编译时期就会被检测到的异常。除了RuntimeException以及子类以外,其他的Exception及其子类都是编译异常,有时候也称之为检查时异常。特点:在编译阶段,Java编译器会检查出异常,也就说程序中一旦出现这类异常,要么使用try-catch语句捕获,要么使用throws语句声明抛出它,否则编译就不会通过。简而言之:程序要求必须处理编译异常,使用try-catch或throws处理。
编译时异常(检查时异常) :程序必须做出处理,一般继承于Exception
ParseException:解析异常(把字符串解析成日期时间对象)
IOException:IO流异常
SQLException:数据库相关的异常
ClassNotFoundException:类未找到异常
FileNotFoundException:文件未找到异常
遇到不懂的异常类时,首先要分辨它属于检查时还是运行时异常。通过快捷键查看 IEDA : ctrl+h
三 异常处理机制
所以我们在开发中要一套机制来处理各种可能会发生的异常,并对其作出正确的处理,确保程序的正常执行。这种机制称为异常处理机制,java语言是最先提供异常处理机制的。
程序预先设置好处理异常的方式 --程序运行–》异常 --对异常处理–》异常捕获异常匹配分析异常处理–处理完毕–》正常接受
异常处理机制可以引导程序向正确的方向运行,程序不至于崩溃。
四 java异常处理
4.1 异常关键字
try
用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当 try 语句块内发生异常时,异常就被抛出。
catch
用于捕获异常。catch用来捕获try语句块中发生的异常。
finally
为确保一段代码不管发生什么异常状况都要被执行。主要用于回收在 try 块里打开的物理资源(如数据库连接、网络连接和磁盘文件)。
throw
用来明确地抛出一个异常。
throws
用来声明一个方法可能抛出的各种异常。
4.2 java异常处理包含两种代码块
4.2.1 try…catch
把有可能产生异常的代码放到try中,如果产生异常由catch代码块处理。语法
try{
//可能产生异常的代码块
}catch (异常类型 e){
//异常处理
}
//后面如果还有代码,无论try中的代码有没有异常,这里都会继续执行
try…catch 三种情况
情况1:没有发生异常,正常执行
情况2:出现异常,catch代码块捕获异常,并进行异常处理,处理完成后程序继续执行。
try -- 发生异常 --》 产生异常对像 ----》 异常类型匹配 --进入catch块--》 catch ----》 try-catch后续代码
/**
* 需求:从控制台输入两个数并做除法
*/
public class anomaly02 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); //输入a 出现异常 产生异常对像 程序不往下执行
System.out.println("请输入被除数 :");
try{
int num = scanner.nextInt();
System.out.println("请输入除数 :");
int num1 = scanner.nextInt();
int num2 = num / num1;
System.out.println(num2);
}catch (Exception e){
//负责处理异常
System.out.println("出现不匹配的异常异常");
}
System.out.println("程序结束");
}
}
异常发生后,从异常发生的那句代码开始,程序不继续向下运行,立即转入异常处理。
/**
* 需求: 定义一个两个数的除法方法
*/
public class anomaly03 {
public static void main(String[] args) {
System.out.println("begin");
//divide方法会出现错误,但是因为divide方法已经处理,不会影响到调用方法的继续向下执行。
divide(17, 0);
System.out.println("ending"); //能够执行到ending
}
public static void divide(int a,int b){
try {
System.out.println(a/b);
}catch (ArithmeticException e){ //指定捕获异常类型 捕获ArithmeticException类型的异常
System.out.println("除法运算数有错误");
}
}
}
/**
* 输出
*/
begin
除法运算数有错误
ending
情况3:出现异常,异常不匹配。
try --发生异常--》 产生异常对象 ----》 异常不类型匹配 --程序中断断运行--》 终止
/**
* 需求:从控制台输入两个数并做除法
*/
public class anomaly04 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个数");
try{
int num = scanner.nextInt();
System.out.println("请输入第二个数");
int num1 = scanner.nextInt();
int num2 = num / num1;
System.out.println(num2);
}catch (InputMismatchException e){
System.out.println("出现不匹配的异常,处理");
}
System.out.println("结束");
}
}
异常不匹配,程序中断。
4.2.2 多重catch
可以为try代码书写多个catch用于捕获多个具体的异常。
public class anomaly05 {
public static void main(String[] args) {
System.out.println("begin");
//divide方法会出现错误,但是因为divide方法已经处理,不会影响到调用方法的继续向下执行。
divide("17", "2");
System.out.println("ending"); //能够执行到ending
// try{
// //可能出现异常的代码
// }catch(异常类型 A变量){
// //处理A类型异常的代码
// }catch(异常类型 B变量){
// //处理B类型异常的代码}// ...
}
public static void divide(String a,String b){
try {
int num = Integer.parseInt(a);
int num1 = Integer.parseInt(b);
System.out.println( num / num1);
}catch (NumberFormatException e){ //指定捕获异常类型 捕获NumberFormatException类型的异常
//处理数字格式化异常的代码
e.printStackTrace();
}catch (ArithmeticException e){
//处理算是异常的代码
e.printStackTrace();
}catch (Exception e){
//处理其他未知的异常;
e.printStackTrace();
}
}
}
4.2.3 try…catch…finally
try…catch和之前一样用于捕获并处理异常,finally代码块用于处理异常后的收尾工作。
不管是否发生异常,finally总执行。
finally的收尾工作包含释放内存、关闭文件、关闭网络连接、关闭数据库、关闭…
public class anomaly06 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个数");
try{
int num = scanner.nextInt();
System.out.println("请输入第二个数");
int num1 = scanner.nextInt();
int num2 = num / num1;
System.out.println(num2);
}catch (Exception e){
//e.printStackTrace();
System.out.println(e.toString());
}finally {
System.out.println(" try...catch...finally");
}
System.out.println("程序正常结束!");
}
}
存在return的try…catch…finally
public class anomaly07 {
public static void main(String[] args) {
System.out.println(div(10,0));
System.out.println("程序正常结束!");
}
public static int div(int num,int num1){
int num2 = 0;
try {
num2 = num / num1;
return num2;
}catch (Exception e){
System.out.println(e.toString());
}finally {
System.out.println(" try...catch...finally");
}
return num2;
}
}
存在return的try…catch…finally
return总是最后执行的。
4.3 声明异常
4.3.1 throws
在Java语言中通过throws声明某个方法可能抛出的各种异常。
当方法的定义者在定义方法时知道调用该方法时可能出现异常,定义者又不知道如何处理时,此时方法定义者可以选择声明异常,使用throws关键字,可以声明多个异常,用(,)号分隔。形式:
[修饰符] 返回值类型 方法名(形参列表) throws 异常1,异常2,...{}
/**
* 需求:声明异常
*/
public class anomaly08 {
public static void main(String[] args) {
try{
divide(10,1);
divide(10,0);//调用divide方法,调用者必须处理或者抛出
}catch (Exception e){
e.printStackTrace();
}
}
//divide方法可能有异常,但divide处理不了该异常就抛出,让divide方法的调用者来处理
public static void divide(int a,int b)throws Exception{
System.out.println(a/b);
}
}
声明异常的原因:该方法自身处理不了该异常,只能使用throws提醒该方法的调用者需要处理异常。当然调用者也有两种处理方式:自己捕获处理或再次抛出(要么try…catch ,要么也throws)。
/**
* 需求:异常继续上抛
*/
public class anomaly09 {
public static void main(String[] args) throws Exception {
//调用divide方法,调用者必须处理或再次抛出
divide(10,1);
}
//divide方法可能有异常,但divide处理不了该异常,就抛出,让divide方法的调用者来处理
public static void divide(int a,int b) throws Exception{
System.out.println(a/b);
}
}
在异常声明或者上抛出的过程中,应该遵循以下原则:能在调用处明确处理优先处理,否则继续上抛。声明异常时要根据异常的分类来确定是否外界(调用处)是否必须处理该方法产生的异常。如果需要外界必须处理,需要声明检查时异常,否则声明运行时异常。
4.3.2 手动抛出异常throw
在实际开发过程中根据程序的需要,手动抛出异常,通过throw关键字。语法
XxException ex = new XxException();
throw ex;
或者
throw new XxException();
当程序出现某种逻辑错误时主动抛出某种特定类型的异常。
public class anomaly10 {
public static void main(String[] args) {
try {
anomaly10.isTest("test");
}catch (Exception e){
System.out.println(e.getMessage());
}
}
public static boolean isTest(String userName)throws Exception{
String[] date = {"test","guangd","hao"};//假设已注册的用户
if (userName != null && userName .length() >0){
for (String name : date) {
if (name.equals(userName)){
throw new Exception("用户"+userName+"已存在");
}
}
}
return false;
}
}
4.3.3 自定义异常
当JDK中的异常类型不能满足程序的需要时(也即需要定义具体的业务异常时),可以自定义异常。
自定义异常的步骤
[1]确定异常类型(检查时异常、运行时异常)。
[2]继承异常的父类(检查时异常Exception、运行时异常RuntimeException)
[3]声明构造方法