1. 异常的概述
-
异常的定义
异常指程序运行过程中出现的意外状况,如不及时处理会导致JVM异常终止。 -
异常处理机制
Java采用面向对象的方式处理异常:
- 每种异常类型对应一个类
- 当异常发生时,系统会创建相应异常类的对象并抛出(throw)
- 程序员可以捕获(catch)异常对象进行处理
- 未被捕获的异常将导致程序终止
- 异常处理策略
处理异常主要有两种方式:
- 直接终止程序运行
- 主动预防和处理:
- 预先考虑可能发生的异常
- 编写异常检测和处理代码
- 确保程序健壮性
2. 异常的体系结构及常见的异常
java.lang.Throwable:异常体系的根父类
|---java.lang.Error:错误。Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。
一般不编写针对性的代码进行处理。
|---- StackOverflowError、OutOfMemoryError
|---java.lang.Exception:异常。我们可以编写针对性的代码进行处理。
|----编译时异常:(受检异常)在执行javac.exe命令时,出现的异常。
|----- ClassNotFoundException
|----- FileNotFoundException
|----- IOException
|----运行时异常:(非受检异常)在执行java.exe命令时,出现的异常。
|---- ArrayIndexOutOfBoundsException
|---- NullPointerException
|---- ClassCastException
|---- NumberFormatException
|---- InputMismatchException
|---- ArithmeticException
【面试题】说说你在开发中常见的异常都有哪些?
开发1-2年:
|----编译时异常:(受检异常)在执行javac.exe命令时,出现的异常。
|----- ClassNotFoundException
|----- FileNotFoundException
|----- IOException
|----运行时异常:(非受检异常)在执行java.exe命令时,出现的异常。
|---- ArrayIndexOutOfBoundsException
|---- NullPointerException
|---- ClassCastException
|---- NumberFormatException
|---- InputMismatchException
|---- ArithmeticException
开发3年以上:
OOM。
3. 异常处理的方式
异常处理机制详解
抛出过程:
-
自动抛出:当程序执行过程中遇到异常时,系统会自动在异常发生处生成对应的异常类对象并将其抛出。
-
手动抛出:在程序执行过程中,当不满足特定条件时,开发人员可以主动使用"throw + 异常类对象"的方式抛出异常对象。
捕获过程:
- 狭义理解:通过try-catch结构捕获并处理异常。
- 广义理解:将"捕获"视为"处理",包含两种处理方式: ① try-catch-finally结构 ② throws声明
3.1 try-catch-finally
1. 基本结构:
try{
...... //可能产生异常的代码
}
catch( 异常类型1 e ){
...... //当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){
...... //当产生异常类型2型异常时的处置措施
}
finally{
...... //无论是否发生异常,都无条件执行的语句
}
2. 使用细节:
> 将可能出现异常的代码声明在try语句中。一旦代码出现异常,就会自动生成一个对应异常类的对象。并将此对象抛出。
> 针对于try中抛出的异常类的对象,使用之后的catch语句进行匹配。一旦匹配上,就进入catch语句块进行处理。
一旦处理接触,代码就可继续向下执行。
> 如果声明了多个catch结构,不同的异常类型在不存在子父类关系的情况下,谁声明在上面,谁声明在下面都可以。
如果多个异常类型满足子父类的关系,则必须将子类声明在父类结构的上面。否则,报错。
> catch中异常处理的方式:
① 自己编写输出的语句。
② printStackTrace():打印异常的详细信息。 (推荐)
③ getMessage():获取发生异常的原因。
> try中声明的变量,出了try结构之后,就不可以进行调用了。
> try-catch结构是可以嵌套使用的。
3. finally的使用说明:
3.1 finally的理解
> 我们将一定要被执行的代码声明在finally结构中。
> 更深刻的理解:无论try中或catch中是否存在仍未被处理的异常,无论try中或catch中是否存在return语句等,finally
中声明的语句都一定要被执行。
> finally语句和catch语句是可选的,但finally不能单独使用。
3.2 什么样的代码我们一定要声明在finally中呢?
> 我们在开发中,有一些资源(比如:输入流、输出流,数据库连接、Socket连接等资源),在使用完以后,必须显式的进行
关闭操作,否则,GC不会自动的回收这些资源。进而导致内存的泄漏。
为了保证这些资源在使用完以后,不管是否出现了未被处理的异常的情况下,这些资源能被关闭。我们必须将这些操作声明,在finally中!
3.2 throws
1. 格式:在方法的声明除,使用"throws 异常类型1,异常类型2,..."
2. 举例:
public void test() throws 异常类型1,异常类型2,.. {
//可能存在编译时异常
}
3. 是否真正处理了异常?
> 从编译是否能通过的角度看,看成是给出了异常万一要是出现时候的解决方案。此方案就是,继续向上抛出(throws)。
> 但是,此throws的方式,仅是将可能出现的异常抛给了此方法的调用者。此调用者仍然需要考虑如何处理相关异常。
从这个角度来看,throws的方式不算是真正意义上处理了异常。
4. 方法的重写的要求:(针对于编译时异常来说的)
子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类。
开发中的经验之谈:
开发中,如何选择异常处理的两种方式?(重要、经验之谈)
- 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,
保证不出现内存泄漏。
- 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能考虑使用try-catch-finally
进行处理,不能throws。
- 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,
我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。
4. 手动throw异常对象
在方法内部,满足指定条件的情况下,使用"throw 异常类的对象"的方式抛出。
5. 如何自定义异常类
① 继承于现有的异常体系。通常继承于RuntimeException \ Exception
② 通常提供几个重载的构造器
③ 提供一个全局常量,声明为:static final long serialVersionUID;
6.练习案例
编写应用程序DivisionDemo.java,接收命令行的两个参数,要求不能输入负数,计算两数相除。
对数据类型不一致(NumberFormatException)、缺少命令行参数(ArrayIndexOutOfBoundsException、
除0(ArithmeticException)及输入负数(BelowZeroException 自定义的异常)进行异常处理。
提示:
(1)在主类(DivisionDemo)中定义异常方法(divide)完成两数相除功能。
(2)在main()方法中调用divide方法,使用异常处理语句进行异常处理。
(3)在程序中,自定义对应输入负数的异常类(BelowZeroException)。
(4)运行时接受参数 java DivisionDemo 20 10 //args[0]="20" args[1]="10"
(5)Interger类的static方法parseInt(String s)将s转换成对应的int值。
如:int a=Interger.parseInt("314"); //a=314;
public class DivisionDemo {
public static void main(String[] args) {
try {/// 使用快捷键CTRL+alt+t快速生成结构体
int m =Integer.parseInt(args[0]);///通过arge传递两个数并进行转换
int n = Integer.parseInt(args[1]);
int result= divide(m,n);
System.out.println("结果为"+result);
} catch (BelowZeroException e) {
///throw new RuntimeException(e);///将编译时异常抛出
System.out.println(e.getMessage());///不抛出,
}catch (NumberFormatException e) {
System.out.println("输出语句不一致");
}catch (ArrayIndexOutOfBoundsException e) {
System.out.println("缺少命令行参数");///角标越界
}catch (ArithmeticException e){
System.out.println("除0");
}
}
public static int divide(int m,int n) throws BelowZeroException{
/// 手动抛出异常,自定义异常类BelowZeroException
if(m<0||n<0){
throw new BelowZeroException("输入负数了");///非运行时异常得处理
}
return m/n;
}
}
public class BelowZeroException extends Exception {/// 继承非运行时异常
static final long serialVersionUID = -33875186579948L;
public BelowZeroException() {}
public BelowZeroException(String message) {
super(message);
}
}