异常
·程序运行时发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。异常发生时,是任程序自生自灭、立刻退出终止?
程序员真理
凡是程序一定会出错
出错不是问题,关键是出错之后,错误如何处理?谁处理?
程序可以从错误中恢复吗?恢复不了就崩溃么?
意外产生和处理过程概述
运行时有许多因素引起出错,硬件失败,除法溢出,数组下标越界。出错的位置都在方法Method里
出错后方法生成一个Exception对象,并把它交给JVM。这个对象里包括许多信息:错误类型,错误位置。JVM负责处理Exception对象
public class Test1 {
public static void main(String[] args) {
int k=0;
System.out.println(10/k);
System.out.println("计算结果为: “”+10/k);
}
}
//运行结果
Exception in thread “main” java.7ang.ArithmeticException: / by zero
at com.yan.excO1.Test1.main(Test1.java:6)
//其中ArithmeticException就是异常的类型,其后紧邻的就是异常的说明l /Test1.java:6告知出现问题的行
这种生成Exception对象并交给系统的过程叫抛出意外throwing an exception
public class Test1 {
public static void main(String[] args) throws Exception { //在方法上声明可能会有的异常,由调用方进行处理
int k = 0;
if (k < 1)
throw new Exception("我们爱胖子! ");//人为编程产生异常
System.out.println(10 / k);
System.out.println(“计算结果为:” + 10 / k);
一个方法抛出意外后,JVM就试图在调用栈里找能处理这个类型Exception对象的方法。找到就执行,找不到程序中止
出错的处理方式
以前是正常流程代码和问题处理代码相结合。现在将正常流程代码和问题处理代码分离,以提高阅读性。
//伪代码
int res=func1(10,20);if(res>0){
println(""正常执行结果! “);
}
else if(res==0){
println(”“程序太胖了,被卡住了!”);
}
else if(res<0){
println("程序太傻了,不知道怎么处理! ");
}
可以根据返回值判断程序的执行情况,如果>=0表示正常执行完毕,如果<0,则返回不同的负值表示不同的运行情况。调用方法时可以根据不同的返回值进行不同的处理
java中引入异常对象的目的在于给调用方更加详细的出错说明,以达到异常处理和正常代码分离,并且按照不同的异常进行处理的目的
其实异常就是Java通过面向对象的思想将程序中的问题封装成了对象,用异常类对其进行描述。1、不同的问题用不同的类进行具体的描述。2、问题很多,意味着描述的类也很多,将其共性进行向上抽取就形成了异常体系
异常
Java异常是Java提供的用于处理程序中错误的一种机制
所谓错误是指在程序运行的过程中发生的一些异常事件。如除0溢出、数组下标越界、所需要读取
的文件不存在
设计良好的程序应该在异常发生时提供处理这些错误,使得程序不会因为异常的发生而阻断或者产生不可预见的结果
Java程序的执行过程中如果出现异常事件,可以生成一个异常类对象,该异常对象封装了异常事件的信息并经被提交给Java运行时系统,这个过程称为抛出异常throw 比如:鲁棒性的问题
当Java运行时系统接收到异常对象时,代码的执行流程会自动中断,转向寻找能处理这一异常的代码并把当前异常对象交给其处理,这个过程称为捕获异常
Java异常处理的关键字有throws、throw、try、catch、finally共5个关键字
异常用途
异常就是在程序运行时由代码所产生的不正常状态。换句话说,异常就是一个运行时不支持异常处理的计算机语言中,错误必须被人工进行检查和处理,这显然麻烦而低效
Java语言提供了异常处理机制,为方法的异常终止和出错处理提供了清楚的接口
用来在发生运行异常时告诉程序如何控制自身的运行,以防止错误的进一步恶化
每个方法必须对他可能抛出的异常进行预先声明,在定义方法时,必须声明这个方法可能会抛出哪一种或几种异常
异常的分类
JavaSE中定义了很多异常类,这些类可以对应各种各样可能出现的异常事件。
Throwable类是Java异常类型的顶层父类,一个对象只有是Throwable类的〈直接或者间接)实例,他才是一个异常对象,才能被异常处理机制识别
Java异常可以分为3大类
所有异常对象的父类为Throwable–Object
1、Error及其子类:错误,一般指的是虚拟机的错误,是由Java虚拟机生成并抛出,程序不能进行处理所以也不加处理,例如OutOfMemoryError内存溢出、调用栈溢出StackOverFlowError
MOM异常:
public class Test3 {
public static void main(String[] args) {
System.out.println(Integer.MAX_VALUE);
int[] arr = new int[Integer.MAX_VALUE] ;
System. out.println( "MOM END. … . ");
}
}
调用栈溢出StackOverFlowError
public class Test4 {
public static void main(String[] args) {
pp();
public static void pp() {
System.out.println(“pp. . …”");
pp();
}
2、RuntimeException及其子类:运行时异常(非受检型异常),是由于编程bug所导致,希望越早发现越好,所以不进行处理,直接中断报错即可,编程人员针对报错信息修改程序bug来解决问题。
面试题:请说出至少5种常见的异常类型,用于检查应试者平常的编码量
面试题:请说出至少5种常见的异常类型,用于检查应试者平常的编码量
常见的第一种运行时异常:ArithmeticException算术异常,就是执行数学计算时产生的非正常情况。如除以0
常见的第二种运行时异常:NullPointerException空指针异常(试图访问null对象的引用)常见的第三种运行时异常:IndexOutOfBoundsException下标出界
常见的第四种运行时异常:ClassCastException试图把一个对象转换成非法类型
常见的第五种运行时异常:NumberFormatException数据格式异常,一般出现在数据类型转换中
ArithmeticException算术异常
public class Test1 {
public static void main(String[] args) {
int k = 0 ;
System.out.println(10 / k);
}
}
解决方案:判断分母是否为0
public class Test1 {
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int k = sc.nextInt();
if (k == 0) {
System.out.println(“除数不合法!”);
}else
System.out.println(10 / k);
}
}
NullPointerException空指针异常
public class Test2 {
public static void main(String[] args){
Object obj = null;
System.out.println(obj); // string.valueof(x) x.tostring();
System.out.println(obj.tostring()) ;
解决方案:调用对象的方法之前先进行判空处理
public class Test2{
public static void main(String[] args) {
Object obj = null;
System.out.println(obj); // string.valueof(x) x.tostring();
if (obj != null)
System.out.print1n(obj.tostring());
}
}
lndexOutOfBoundsException下标出界
public class Test3 {
public static void main(String[] args) {
//stringIndexoutofBoundsException
String ss=“sadf1kasjdf”;
char cc=ss.charAt(ss.length);
System.out.println(cc);
}
}
解决方案:首先进行下标的合法性验证
public class Test3 {
public static void main(Sring[] args) {
// stringIndexoutofBoundsException
String ss - “sadflkasjdf” ;
Scanner sc=new Scanner(System.in);int pos = sc.nextInt();
//下标的取值范围为[0,ss.length())
if (pos > -1 && pos < ss.length()) {
char cc = ss.charAt(pos);
System.out.println(cc);
}else {
System.out.println(“没有对应下标的字符!”);
sc.close();
}
}
ClassCastException试图把一个对象转换成非法类型
public class Test4 {
public static void main(String[] args){
Object obj = new Date();
System.out.println(obj);
Integer kk = (Integer) obj;
System.out.print1n(kk);
}}
解决方案:要求进行窄化操作之前必须进行对象类型判定
public class Test4 {
public static void main(String[] args) {
object obj = new Date();
System.out.println(obj);
if (obj != null && obj instanceof Integer) {
Integer kk = (Integer) obj;
System.out.println(kk);
} else {
System.out.println(“执行其它处理”);
}
}}
NumberFormatException数据格式异常
public class Test5 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
//int kk=sc.nextInt(;//这里系统提供了针对8种基本数据类型的键盘录入方法
String ss=sc.nextLine();//可以读取用户键盘录入的一行内容,以回车为结束符号
int kk=Integer.parseInt(ss);
System.out.println(kk);
}}
解决方案:针对用户录入的内容进行合法性判断,正则式–字符串处理的终结版解决方案
方法2:可以利用异常进行处理
public class Test5 {
public static void main(String[] args){
Scanner sc = new Scanner(System.in) ;
//int kk=sc.nextInt(;//这里系统提供了针对8种基本数据类型的键盘录入方法int kk=-1;
while (true) {
try {
String ss = sc.nextLine(;// 可以读取用户键盘录入的一行内容,以回车为结束符号
kk = Integer.parseInt(ss);
system.out.println(kk);
break ;
}catch (Exception e) {
System.out.println("您输入的数据不合法! “”);
}
}
}
}
3、Exception及其子类中除了RuntimeException及其子类之外的其它异常:受检型异常(非运行时异常).这类异常属于明知道可能出现,但是没有办法杜绝的异常。这类异常一般采用try/catch或者throws声明抛出的方式进行异常处理,当程序出现了非正常情况,尽量保证程序正常结果,而不是立即中断
受检型异常:明知道存在这种异常的可能,但是没有办法杜绝,所以必须进行编码异常处理
非受检型异常:这种异常不需要进行处理,发现的越早越好
异常的捕获和处理
语法规则:
try{
try代码段中包含可能产生异常的代码,有人称为陷阱代码,在执行过程中如果出现了异常,则异常之后的java语句不会执行。转而执行catch部分的代码
}catch(SomeException e){可以写多个
try后可以跟一个多个catch代码段,针对不同异常执行不同的处理逻辑。当异常发生时,程序会中止当前的流程,根据获取异常的类型去执行响应的代码段。注意异常类型判定时是从上向下逐个判断的。
}finally{
fina11y代码是无论是否发生异常都会执行的代码
}
样例:
package com.yan.excO3 ;
import java.uti1.scanner;
public class Test1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println(“请输入整数”);//可能是多个整数
String ss = sc.nextLine();
int[] intArr = null;
if (ss != nu11 &&ss.trim().length() > 0) {
String[] arr = ss.trim().split(" “);
intArr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
Integer kk=null;
try {
kk = Integer. parseInt(arr[i]);
System. out.println(“正确!”);
}catch (NumberFormatException e) {
System.out.println(“输入数据格式错误! “”);kk=0;
catch(Exception e){
System.out.println(“其它错误!””);
}
intArr[i] = kk ;
}
}
if (intArr != null) {
System.out.println(“您输入的数据为:””);for (int tmp : intArr)
System.out.print(tmp + “t”");
}else {
System.out.println("没输入任何数据! ");
}
sc.close();
}
}
注意:
. try块中的局部变量和catch块中的局部变量〈包括异常变量),以及finally中的局部变量,他们之
间不可共享使用
int kk=100;
try {
int bb = 99;
System.out.println(kk) ;
}catch (Exception e) {
string bb = “abcd”";
system.out.print1n(kk);
}finally {
boolean bb = true;
System.out.println(kk);
}
System.out.println(kk) ;
Java采用的是终结式异常处理机制,java中异常处理的任务就是将执行控制流从异常发生的地方转移到能够处理这种异常的地方去。也就是说:当一个函数的某条语句发生异常时,这条语句的后面的语句不会再执行,它失去了焦点。执行流跳转到最近的匹配的异常处理catch代码块去执行,异常被处理完后,执行流会接着在"处理了这个异常的catch代码块"后面接着执行。
try{}后面必须跟一个finally或者catch,其中有个特殊写法可以省略后面的finally和catch
try后可以紧邻0-n个catch,如果没有catch则必须紧邻finally
Java异常处理
try-用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
catch–用于捕获异常。catch用来捕获try语句块中发生的异常。
finally-finally语句块总是会被执行。它主要用于回收在try块里打开的资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
throw-人为编程实现抛出异常。
throws-用在方法签名中,用于声明该方法可能抛出的异常,要求谁调用谁处理这个异常。主方法上也可以使用throws抛出。如果在主方法上使用了throws抛出,就表示在主方法里面可以不用强制性进行异常处理,如果出现了异常,就交给JVM进行默认处理,则此时会导致程序中断执行。
try语句
try语句指定一段代码,该段代码就是一次捕获并处理异常的范围
在执行过程中,该段代码可能会产生并抛出一种或几种类型的异常对象,它后面的catch语句要分别对这些异常做响应的处理
如果没有异常产生,所有的catch代码段都能略过不执行
catch语句
在catch语句块中是对异常进行处理的代码,每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象,同时允许异常的继续抛出
catch只匹配成功一次即可,注意不是最佳匹配,例如交换Exception和NumberFormatException的位置,这里就会语法报错,报错的提示信息为Unreachable catch block for NumberFormatException. ltis already handled by the catch block forException,含义是catch(NumberFormatException e)是不可达语句,NumberFormatException是Exception的子类。
强调:在编写多个catch时,小异常一定在大异常之前
在catch中声明的异常对象封装了异常发生的信息,在catch语句块中可以使用这个对象的一些方法获取这些信息
getMessage():String用于获取有关异常事件的信息,一般是异常信息的描述,例如【For inputstring: “123.456”]
try{
String ss=sc.nextLine();
int kk=Integer.parseInt(ss);
}catch(Exception e){
System.out.println(e.getMessage();
}
toString():String,输出格式为【java.lang.NumberFormatException:For input string:“123.456”】
try{
String ss=sc.nextLine(;
int kk=Integer.parseInt(ss);
}catch(Exception e){
System.out.println(e); //e.tostring();
}
printStackTrace():void用来跟踪异常事件发生时执行堆栈的内容,注意:这里的输出默认采用错误流System.err进行输出,如果还使用了System.out进行输出,则不能保证显示顺序
public class Test3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in) ;
try {
String ss = sc.nextLine();
int kk = Integer. parseInt(ss);
catch (Exception e) {
System.err.println(“异常处理…”);
e.printstackTrace(;
}
System.err.println(“代码结束…”");
sc.close();
}
}
注意:使用printStackTrace输出调用栈使用的是System.err输出报错信息,不是编程使用的System.out,所以输出顺序有可能和预计不一致
常见的3种输出异常的用法
. System.out.printIn(e);//java.lang.ArithmeticException: / by zero
println(obj)操作细节;
public void print1n(object x) {
String s = String.valueof(x);
…
String.valueof
public static string valueof(object obj) {
return (obj == nu11) ? “null” : obj.tostring(;
System.out.printIn(e.getMessageO); l / by zero. e.printStackTrace();调用栈 syserr
JDK1.7引入多异常捕获
大部分情况下可能会出现多种不同异常但是处理方法一致时,注意这多个异常类型之间没有继承关系,如果有继承关系则应该写成try{}catch(父类型e){}即可
两次捕捉异常的处理是一样的,就可以将两个异常卸载一个catch中,其中多个异常类型之间使用|分割
trycatch(IndexOutOfBoundException | NumberFormatException | ArithmeticException e)(}。这里需要注意的是捕获一种异常时,异常对象没有final的含义,但是多种异常时隐含了final含义,所以不能修改对象引用[考试]
try{
}catch(IOException e){
System.out.println(e);
}catch(SQLException e){
System.out.println(e);
简化写法
try{}
catch(IOException | SQLException e){
System.out.println(e);}
最后需要记得catch可以写多个,但是有顺序,需要时先小后大,如果先大后小,则小代码不可及
finally语句
finally语句为异常处理提供一个统一的出口,使得在控制流程转到程序的其他部分以前,能够对程序的状态作统一的管理
无论try所指定的程序块是否抛出异常,finally所指定的代码都要执行。
try可以没有catch而直接使用finally,当然在方法上声明抛出
通常在finally语句中可以进行资源的清除工作,例如关闭打开的文件,删除临时文件等。注意Java的垃圾回收机制只能回收再堆内存中对象所占用的内存,而不会回收任何物理资源〈如数据库连接、网络连接和磁盘文件等)
特殊情况导致finally执行出现问题
在前边的代码中使用System.exit(int)退出应用
终止当前运行的Java虚拟机。这个int参数用作状态代码;按照惯例,非零状态代码表示异常终止
程序所在的线程死亡或者cpu关闭
如果在finally代码块中的操作又产生异常,则该finally代码块不能完全执行结束
connection conn = null;
try {
conn = DriverManager.getconnection(“jdbc:mysq1:/ /ltest?serverTimezone=UTc”,“root”,“123456”);
}catch (Exception e) {
e.printstackTrace;}
finally {
try {
if (conn != null)
conn.close();
catch (SQLException e) {
e.printstackTrace();
}
}
考点: finally、final和finalize的区别
一、final这个关键字在Java中代表不可改变的,可用来修饰类,方法,变量。
对class来说,用法如: final class A)代表类A为终极类,不能被继承,也就没有子类之说与接口实现者之说。因此一个类不能既被声明为abstract。又被声明为final。
对method来说,用法如: public final void test(0代表此方法不能被重写。对variable来说,必须在声明时赋值,且不能再改变。
二、 finally关键字用在异常处理中,用于处理异常后的清理工作,实际中一般用于关闭文件流,释放资源等操作。
三、 finalize一方法名。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的,它是在Object类中定义的,因此所有的类都继承了它,子类覆盖finalize()方法以整理系统资源或者执行其他清理工作。finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。
JDK1.7+引入的自动关闭的写法
JDK1.7引入了自动关闭资源的try语句块。语法结构为try(FileInputstream fis=new
FileInputstream("“a.txt"))0在方法上声明抛出异常即可,没有finally语句块。这里实际上引入了Closeable和AutoCloseable接口,可以自动关闭资源,
旧有写法
public class Test7 {
pub1ic static void main(String[] args) {
FileInputstream fis = null;
try {
fis = new FileInputstream(“d: / dd.data”);
}catch (IOException e) {
e.printstackTrace(;
} finally {
try {
if (fis != null)
fis.close();
}catch (IOException e) {
e.printstackTrace();
}
}
}
}
try-resource的写法
try (FileInputstream fis1 = new FileInputstream(“d 😕 dd.data”);
Fileoutputstream fos1 = new Fileoutputstream(“d : /bb.data”); ) {
int kk=fis1.read();
fos1.write(kk);
}catch (IOException e) {
System.out.println(e. getMessage();
}
异常的捕获和处理
Java的异常处理机制使得异常事件能够沿着被调用的顺序向前寻找,只要找到符合该异常种类的异常处理程序,就会进行异常处理逻辑的执行,并且被消费掉,不会继续查找。
try{
}catch(Exception e){
//空处理是隐藏异常
}
容易出现的一个编码问题:隐藏异常
异常处理允许嵌套,但是一般不建议嵌套层数过多
最佳异常相关编程实践
1、不要在fianlly中使用return。
2、不要在finally中抛出异常。
3、减轻finally的任务,不要在finally中做一些其它的事情,finally块仅仅用来释放资源是最合适的。
4、将尽量将所有的return写在函数的最后面,而不是try … catch … finally中
自定义异常
1、通过继承java.lang.Exception类【受检型异常】或者RuntimeException【非受检型异常】声明自定义异常类【也允许继承Throwable或者Exception的子类】。例如因为业务导致可能出现的问题账户余额不足
public class InsufficientAccountBalanceException extends Exception {
public InsufficientAccountBa1anceException() {
super(“账户余额不足!”);
public InsufficientAccountBalanceException(String message,Throwable cause,boolean enablesuppression,boolean writablestackTrace){
super(message,cause,enablesuppression,writablestackTrace);
}
public InsufficientAccountBalanceException(String message,Throwable cause) {
super(message,cause);
public InsufficientAccountBalanceException(String message){
super(message) ;
}
public InsufficientAccountBalanceException(Throwable cause) {
super(cause);
}}
2、在方法适当的位置生成自定义异常的实例,并使用throw语句抛出
public class Accountoperation {
private long balance = 0;
public void 取款(long amount) throwsInsufficientAccountBalanceException{
//Illega1ArgumentException运行时异常,非受检型异常
if (amount < 0){
throw new Illega1ArgumentException(“参数错误!”);
}
if (this.balance < amount)
throw new InsufficientAccountBalanceException();//非运行时异常、受检型异常,必须进行处理
this.balance -= amount;
}}
3、在方法的声明部分用throws语句声明该方法可能会抛出的异常或者使用try/catch结构直接进行处理或者自定义异常为运行时异常
·使用运行时异常是可以简化调用方编程,但是也可能导致调用方根本不知道这里可能会出现异常4、注意:方法重写时需要抛出比原来方法一致或者更少的异常
异常处理建议规则
1、不要过度使用异常,不要把异常当做不同的逻辑分支对待
2、不要使用过于庞大的try块
3、避免使用catch all语句try(catch(Throwable t){}
4、坚决不要忽略捕获的异常,坚决不允许catch块中内容为空