9.01 异常的由来
代码在运行时期发生的问题就是异常。在java中使用Exception来描述异常,throwable是Java 语言中所有错误或异常的超类,可以通过查看API。
异常内容:
异常名称,异常产生原因,异常发生的位置,异常再生的线程。
(名称和原因我们可以自定义,位置和线程由JVM决定)
9.02 常见异常
ArrayIndexOutOfBoundsException: 数组下标越界异常,当出现超过数组下标范围0-array.length-1范围外的下标时,出现这个异常
ArithmeticException:算数异常,在除数为0时报出异常
ClassCastException:类型转换异常,当父类引用指向子类对象的多态时,如果调用的引用不是父类的话,无法转换,报出异常
NullPointerException:空指针异常,当创建对象后,其中的String类型数据指向空,此时去使用这些数据会报异常
FileNotFoundException:找不到文件异常,在I/O流里因为想要使用文件,但是文件不存在所产生的异常。
NoSuchMethodException:没有这个方法异常,当调用(静态或者实例)类的方法,而类已经不再具有时,抛出这个异常。
SecurityException:安全检测异常,由安全管理器抛出,表示存在安全侵犯UnknownHostException: 找不到主机异常,使用socket关键字访问时,找不到主机
IllegalArgumentException: 非法参数异常,如有些方法的参数需要int,你传入String引用数据类型
MalformedURLException:URL解析格式异常,当使用为URL格式的网址时,找不到协议,或者URL格式非法。
InputMismacthException:输入不匹配异常。输入要获取int类型,结果你给出的时float。
NumberFormatException:转换字符串格式异常,当打算把字符串的数据转换为int型时不匹配发出的异常,如"abc"转为int失败。
9.03 异常的产生及模拟处理
异常的产生
基本是由JVM自动检测的,也可以人为的手动抛出异常。throw 异常对象;(throw只能在函数内部,属于执行语句)
模拟处理:
package part1.异常;
public class ExceptionDemo {
public static void main(String[] args) {
int[] a=new int[4];
int[] b=new int[1024*1024*1024]; //异常 堆溢出
show(a,5); //角标越界
show(null,2); //异常 数组为空
System.out.println("嘿嘿");
}
private static void show(int[] a, int index) {
//JVM模拟:
/*
if(a==null){
throw new NullPointerException("数组为空!");
}
if(index<0||index>=a.length){
throw new ArrayIndexOutOfBoundsException("数据角标越界:"+index);
}
*/
System.out.println(10/0);
System.out.println(a[index]);
}
}
9.04 异常体系
问题很多,意味着描述的类也很多 将其共性进行向上抽取,就形成了异常体系。
Throwable是异常的的最终父类。(除object类外–object是任何类的父类。)
最终问题分成两大类:
1.一般不可处理 Error
2.可处理 Exception
Error,Exception:无论是哪个,都是问题,问题发生就应该抛出,让调用者知道,并可以处理。所以,该体系的特点就在于Throwable及其所有的子类都具有可抛性。
Error特点:
是由JVM抛出的严重性的问题,这种问题发生一般不针对性处理,直接修改程序,比如类找不到,数据内存过大。
Exception特点:
是由虚拟机或Java throw抛出的一般性错误(能够有解决方案——程序不一定终止!)
该体系的特点:
子类的后缀名都是用其父类名后缀
9.05 自定义异常
就是按照项目实际需求 定义出所需的异常类
自定义类继承Exception或其子类 通过构造函数定义异常信息,都是找父类。 通过throw将自定义异常抛出。
例
电脑有时会出现蓝屏的问题,将蓝屏问题定义 一个异常类。
class BlueScreenOfDeath extends Exception{
BlueScreenOfDeath(String message){
super(message);
}
}
定义完成后,throw,throws,try-catch-finally,和Java自带的异常用法相同。
9.06 异常的分类
主要分为两大类:
1.编译时被检测异常(编译时异常):
编译时异常,这种问题一旦出现,希望在编译时就进行检测,让这种问题有相对应的处理方式。这种问题都可以针对性处理。
只要是Exception和其子类,除了RuntimeException体系。
2.编译时不检测异常(运行时异常):
运行时异常,这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致,也有内部原因。这种问题一般不处理,直接编译通过,在运行时,让调用者调用时程序强制停止,让调用者对代码进行修正。
就是Exception中的RuntimeException和其子类。
9.07 throw与throws的区别
1.throws使用在函数上;throw使用在函数内。
2.throws抛出的是异常类,可以抛出多个,用逗号隔开;throw抛出的是异常对象。
9.08 异常的处理
对于Exception而言 解决方案一般有两种形式
声明 throws
通过声明提前告诉调用者该函数可能会存在问题
如果问题发生 首先会反馈给函数自身 函数自身再将该问题反馈给调用者 由调用者自行解决
对于该函数而言 确实是一种解决方案 但是并没有在函数内部实质解决 甩锅~
throws 只能在函数定义之后 函数体之前
throws 后面只能跟异常类 可以多个类名 类名之间,分隔
捕获 try-catch-finally
先尝试运行某段代码try
如果该段代码出现问题 则立马解决 catch
无论代码是否出错 将都会执行的代码放入finally
具体格式:
try{
//需要被检测异常的代码
}catch(异常类 变量){ //该变量用于接收发生的异常对象
//处理异常的代码
}finally{
//一定会被执行的代码
}
异常处理的原则
1.函数内容如果抛出需要检测的异常,那么函数上必须声明。否则必须在函数内用trycatch捕捉,否则编译失败。
2.如果调用到了声明异常的函数,要么trycatch要么throws,否则编译失败。
3.什么时候catch,功能内部可以解决用,解决不了用throws,由调用者解决。
4.一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理。内部有几个需要检测的异常,就抛几个异常,抛几个,就catch几个。
注意:
异常在子父类中的特点(编译时异常)
1.父类函数如果没有声明异常 子类就不能声明 只能捕获
2.父类如果声明异常 子类只能声明父类异常的子类或子集
(运行时异常)解决与否都可以
9.09 异常中有return的情况
没有异常的情况下 try里有return try->finally
没有异常的情况下 catch里有return try->finally->之后
没有异常的情况下 finally里有return try->finally 一旦有 之后则不执行
有异常的情况下 try里有return try-catch-finally->之后
有异常的情况下 catch里有return try->catch->finally
有异常的情况下 finally里有return try-catch-finally 一旦有 之后则不执行
没异常的情况下
try catch里有return try->finally之后不执行
有异常的情况下
try catch里有return try->catch->finally
综上而言 不推荐try-catch-finally里写return 但是理论可写
9.10 本章小结
1.异常处理使一个方法能够抛出一个异常给它的调用者。
2.Java异常是扩展自java.lang.Throwable 的类的实例。Java 提供大量预定义的异常类,例如, Error、Exception、RuntimeException、ClassNotFoundException、Nul1PointerException 和 ArithmericException。也可以通过扩展 Exception 类来定义自己的异常类。
3.异常发生在一个方法的执行过程中。RuntimeException 和 Error 都是免检异常、所有其他的异常 都是必检的。异常处理和文本 I/O
4.当声明一个方法时,如果这个方法可能抛出一个必检异常,则必须进行声明,从而告诉编译器可能会出现什么错误。
5.声明异常的关键字是 throws, 而抛出异常的关键字是 throw。
6.如果调用声明了必检异常的方法,必须将该方法调用放在 try 语句中。在方法执行过程中出现异常时,catch块会捕获并处理异常。
7.如果一个异常没有被当前方法捕获,则该异常被传给调用者。这个过程不断重复直到异常被捕获或 者传递给main 方法。
8.可以从一个共同的父类派生出各种不同的异常类。如果一个 catch块捕获到父类的异常对象,它也 能捕捉这个父类的子类的所有异常对象。
9.在 catch 块中,异常的指定顺序是非常重要的。如果在指定一个类的异常对象之前,指定了这个异 常类的父类的异常对象,就会导致一个编译错误。
10.当方法中发生异常时,如果异常没有被捕获,方法将会立刻退出。如果想在方法退出前执行一些任务,可以在方法中捕获这个异常,然后再重新抛给它的调用者。
11.任何情况下都会执行 finally 块中的代码,不管 try 块中是否出现了异常,或者出现异常后是否捕获了该异常。
12.异常处理将错误处理代码从正常的程序设计任务中分离出来,这样就会使得程序更易于阅读和修改。
13.不应该使用异常处理代替简单的测试。应该尽可能地使用if 语句来进行简单的测试,将异常处理留作处理那些无法用if 语句处理的场景。