异常
异常
现实生活的病
现实生活中万物在发展和变化会出现各种各样不正常的现象。
1:例如:人的成长过程中会生病。
|——病
|——不可治愈(癌症晚期)
|——可治愈
|——小病自行解决(上火,牙痛)
|——去医院(感冒,发烧)
java异常体系图
现实生活中的很多病况从面向对象的角度考虑也是一类事物,可以定义为类。
java中可以通过类对这一类不正常的现象进行描述,并封装为对象。
- java的异常体系包含在java.lang这个包默认不需要导入。
- java异常体系
|——Throwable (实现类描述java的错误和异常)
|——Error (错误)一般不通过代码去处理。
|——Exceprion (异常)
|——RuntimeException (运行时异常)
|——非运行时异常
常见的Error
错误原因:内存溢出。需要的内存已经超出了java虚拟机管理的内存范围。
错误原因:找不到类文件。
错误(Error):
它指的是一个合理的应用程序不能截获的严重的问题。大多数都是反常的情况。错误是JVM的一个故障(虽然它可以是任何系统级的服务)。所以,错误是很难处理的,一般的开发人员(当然不是你)是无法处理这些错误的。比如内存溢出.
3、异常体系图的对应
Throwable类
- toString() 输出该异常的类名。
- getMessage() 输出异常的信息,需要通过构造方法传入异常信息(例如病态信息)。
- printStackTrace() 打印栈信息。
人生病:流鼻涕,感冒,呼吸道感染,肺炎。。。最后体现的是肺炎。
医生要处理需要获知这些信息。从外到里处理。最后找病源
1 /* 2 Throwable类 3 4 printStackTrace() 打印栈信息 5 6 肺炎 7 上呼吸道感染 8 发烧 9 流鼻涕感冒 10 小感冒 11 */ 12 class Demo6 { 13 14 public static void main(String[] args) { 15 16 // Throwable able=new Throwable(); 17 Throwable able = new Throwable("想吐。。。"); 18 System.out.println(able.toString()); // 输出该异常的类名 19 System.out.println(able.getMessage()); // 输出异常的信息 20 able.printStackTrace(); // 打印栈信息 21 } 22 }
程序中的异常处理
- 当除数是非0,除法运算完毕,程序继续执行。
- 当除数是0,程序发生异常,并且除法运算之后的代码停止运行。因为程序发生异常需要进行处理。
1 class Demo7 { 2 3 public static void main(String[] args) { 4 div(2, 0); 5 System.out.println("over"); 6 } 7 8 public static void div(int x, int y) { 9 //该行代码的y值可能是0,程序会出现异常并停止 10 System.out.println(x / y); 11 System.out.println("除法运算"); 12 } 13 } 14 //ArithmeticException
疑问: 出现异常如何处理?
自行处理
- try{//可能发生异常的代码 }catch(异常类 变量名){//处理}。
- 案例除法运算的异常处理。
- 如果没有进行try catch处理,出现异常程序就停止。进行处理后,程序会继续执行。
1 class Demo7 { 2 3 public static void main(String[] args) { 4 div(2, 0); 5 System.out.println("over"); 6 } 7 8 public static void div(int x, int y) { 9 10 try { 11 System.out.println(x / y); // 可能出现异常的语句,放入try中。 12 } catch (ArithmeticException e) { // 进行异常匹配, 13 //异常信息 14 System.out.println(e.toString()); 15 System.out.println(e.getMessage()); 16 e.printStackTrace(); 17 System.out.println("除数不能为0"); 18 } 19 System.out.println("除法运算"); 20 } 21 }
多个异常
- 案例print方法,形参中增加数组。
- 在print方法中操作数组会发生新的异常
ArrayIndexOutOfBoundsException,NullPointerException),如何处理?
- 使用将可能发生异常的代码放入try语句中,添加多个catch语句即可。
- 可以处理多种异常,但是同时只能处理一种异常。
- try中除0异常和数组角标越界同时出现,只会处理一种。
1 public class Demo8 { 2 3 public static void main(String[] args) { 4 5 System.out.println(); 6 int[] arr = { 1, 2 }; 7 arr = null; 8 9 // print (1, 0, arr); 10 print (1, 2, arr); 11 12 System.out.println("over"); 13 } 14 15 public static void print(int x, int y, int[] arr) { 16 17 try { 18 System.out.println(arr[1]); 19 System.out.println(x / y); 20 } catch (ArithmeticException e) { 21 e.toString(); 22 e.getMessage(); 23 e.printStackTrace(); 24 System.out.println("算术异常。。。"); 25 } catch (ArrayIndexOutOfBoundsException e) { 26 e.toString(); 27 e.getMessage(); 28 e.printStackTrace(); 29 System.out.println("数组角标越界。。。"); 30 } catch (NullPointerException e) { 31 e.toString(); 32 e.getMessage(); 33 e.printStackTrace(); 34 System.out.println("空指针异常。。。"); 35 } 36 System.out.println("函数执行完毕"); 37 } 38 }
总结
- 程序中有多个语句可能发生异常,可以都放在try语句中。并匹配对个catch语句处理。
- 如果异常被catch匹配上,接着执行try{}catch(){} 后的语句。没有匹配上程序停止。
- try中多个异常同时出现,只会处理第一条出现异常的一句,剩余的异常不再处理。
- 使用多态机制处理异常。
- 程序中多态语句出现不同异常,出现了多个catch语句。简化处理(相当于急诊)。
- 使用多态,使用这些异常的父类进行接收。(父类引用接收子类对象)
-
1 public static void div(int x, int y, int[] arr, Father f) { 2 3 try { 4 System.out.println(arr[1]); // 数组越界 5 System.out.println(x / y); // 除零 6 Son s = (Son) f; // 类型转换 7 8 } catch (Exception e) { 9 e.toString(); 10 e.getMessage(); 11 e.printStackTrace(); 12 System.out.println("出错啦"); 13 } 14 System.out.println("函数执行完毕"); 15 }
多个catch语句之间的执行顺序。
- 是进行顺序执行,从上到下。
- 如果多个catch 内的异常有子父类关系。
- 子类异常在上,父类在最下。编译通过运行没有问题
- 父类异常在上,子类在下,编译不通过。(因为父类可以将子类的异常处理,子类的catch处理不到)。
- 多个异常要按照子类和父类顺序进行catch
1 class Father { 2 3 } 4 5 class Son extends Father { 6 7 } 8 9 public class Demo8 { 10 11 public static void main(String[] args) { 12 13 System.out.println(); 14 int[] arr = { 1, 2 }; 15 arr = null; 16 Father f = new Father(); 17 div(1, 0, arr, f); 18 19 System.out.println("over"); 20 } 21 22 public static void div(int x, int y, int[] arr, Father f) { 23 24 try { 25 System.out.println(arr[1]); 26 System.out.println(x / y); 27 Son s = (Son) f; 28 29 } catch (ArithmeticException e) { 30 e.toString(); 31 e.getMessage(); 32 e.printStackTrace(); 33 System.out.println("算术异常。。。"); 34 } catch (ArrayIndexOutOfBoundsException e) { 35 e.toString(); 36 e.getMessage(); 37 e.printStackTrace(); 38 System.out.println("数组角标越界。。。"); 39 } catch (NullPointerException e) { 40 e.toString(); 41 e.getMessage(); 42 e.printStackTrace(); 43 System.out.println("空指针异常。。。"); 44 } catch (Exception e) { 45 e.toString(); 46 e.getMessage(); 47 e.printStackTrace(); 48 System.out.println("出错啦"); 49 } 50 System.out.println("函数执行完毕"); 51 } 52 }
总结
处理异常应该catch异常具体的子类,可以处理的更具体,不要为了简化代码使用异常的父类。
疑惑:感觉异常没有作用.
抛出处理
定义一个功能,进行除法运算例如(div(int x,int y))如果除数为0,进行处理。
功能内部不想处理,或者处理不了。就抛出使用throw new Exception("除数不能为0"); 进行抛出。抛出后需要在函数上进行声明,告知调用函数者,我有异常,你需要处理如果函数上不进行throws 声明,编译会报错。例如:未报告的异常 java.lang.Exception;必须对其进行捕捉或声明以便抛出throw new Exception("除数不能为0");
1 public static void div(int x, int y) throws Exception { // 声明异常,通知方法调用者。 2 3 if (y == 0) { 4 throw new Exception("除数为0"); // throw关键字后面接受的是具体的异常的对象 5 } 6 System.out.println(x / y); 7 System.out.println("除法运算"); 8 }
main方法中调用除法功能
调用到了一个可能会出现异常的函数,需要进行处理。
1:如果调用者没有处理会编译失败。
如何处理声明了异常的函数。
1:try{}catch(){}
1 public static void main(String[] args) { 2 3 try { 4 div(2, 0); 5 } catch (Exception e) { 6 e.printStackTrace(); 7 } 8 System.out.println("over"); 9 10 } 11 12 public static void div(int x, int y) throws Exception { // 声明异常,通知方法调用者。 13 14 if (y == 0) { 15 throw new Exception("除数为0"); // throw关键字后面接受的是具体的异常的对象 16 } 17 System.out.println(x / y); 18 System.out.println("除法运算"); 19 } 20 }
2:继续抛出throws
1 public static void main(String[] args) { 2 3 try { 4 div(2, 0); 5 } catch (Exception e) { 6 e.printStackTrace(); 7 } 8 System.out.println("over"); 9 10 } 11 12 public static void div(int x, int y) throws Exception { // 声明异常,通知方法调用者。 13 14 if (y == 0) { 15 throw new Exception("除数为0"); // throw关键字后面接受的是具体的异常的对象 16 } 17 System.out.println(x / y); 18 System.out.println("除法运算"); 19 } 20 } 21 22 2:继续抛出throws 23 class Demo9 { 24 25 public static void main(String[] args) throws Exception { 26 div(2, 0); 27 System.out.println("over"); 28 } 29 30 public static void div(int x, int y) throws Exception { // 声明异常,通知方法调用者。 31 if (y == 0) { 32 throw new Exception("除数为0"); // throw关键字后面接受的是具体的异常的对象 33 } 34 35 System.out.println(x / y); 36 System.out.println("除法运算"); 37 } 38 }
throw和throws的区别
- 相同:都是用于做异常的抛出处理的。
- 不同点:
- 使用的位置: throws 使用在函数上,throw使用在函数内
- 后面接受的内容的个数不同:
- throws 后跟的是异常类,可以跟多个,用逗号隔开。
- throw 后跟异常对象。
1 //throws 处理 2 public static void main(String[] args) throws InterruptedException { 3 Object obj = new Object(); 4 obj.wait(); 5 6 } 7 8 9 public static void main(String[] args) { 10 11 //try catch 处理 12 Object obj = new Object(); 13 try { 14 obj.wait(); 15 } catch (InterruptedException e) { 16 17 e.printStackTrace(); 18 } 19 20 }
总结
- try语句不能单独存在,可以和catch、finally组成 try...catch...finally、try...catch、try...finally三种结构。
- catch语句可以有一个或多个,finally语句最多一个,try、catch、finally这三个关键字均不能单独使用。
- try、catch、finally三个代码块中变量的作用域分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。
- 多个catch块时候,Java虚拟机会匹配其中一个异常类或其子类,就执行这个catch块,而不会再执行别的catch块。(子类在上,父类在下)。
- throw语句后不允许有紧跟其他语句,因为这些没有机会执行。
- 如果一个方法调用了另外一个声明抛出异常的方法,那么这个方法要么处理异常,要么声明抛出。
自定义异常
问题:现实中会出现新的病,就需要新的描述。
分析: java的面向对象思想将程序中出现的特有问题进行封装。
案例: 定义功能模拟凌波登录。(例如:lb(String ip))需要接收ip地址
- 当没有ip地址时,需要进行异常处理。
1. 当ip地址为null是需要throw new Exception("无法获取ip");
2. 但Exception是个上层父类,这里应该抛出更具体的子类。
3. 可以自定义异常
- 自定义描述没有IP地址的异常(NoIpException)。
1. 和sun的异常体系产生关系。继承Exception类,自定义异常类名也要规范,结尾加上Exception,便于阅读
1 /* 2 自定义异常 3 */ 4 class NoIpException extends Exception { 5 6 NoIpException() { 7 8 } 9 10 NoIpException(String message) { 11 super(message); 12 } 13 14 } 15 16 class Demo10 { 17 18 public static void main(String[] args) throws NoIpException { 19 20 System.out.println(); 21 String ip = "192.168.10.252"; 22 ip = null; 23 try { 24 Lb(ip); 25 } catch (NoIpException e) { 26 System.out.println("程序结束"); 27 } 28 29 } 30 31 /* 32 * 33 * 凌波教学 34 */ 35 public static void Lb(String ip) throws NoIpException { 36 if (ip == null) { 37 // throw new Exception("没插网线吧,小白"); 38 throw new NoIpException("没插网线吧,小白"); 39 } 40 41 System.out.println("醒醒了,开始上课了。"); 42 } 43 }
案例:模拟吃饭没带钱的问题
- 定义吃饭功能,需要钱。(例如:eat(double money))
- 如果钱不够是不能吃放,有异常。
- 自定义NoMoneyException();继承Exception 提供有参无参构造,调用父类有参构造初始化。at 方法进行判断,小于10块,throw NoMoneyException("钱不够");
- eat 方法进行声明,throws NoMoneyException
- 如果钱不够老板要处理。调用者进行处理。try{}catch(){} 。
1 class NoMoneyException extends Exception { 2 3 NoMoneyException() { 4 5 } 6 7 NoMoneyException(String message) { 8 super(message); 9 } 10 } 11 12 class Demo11 { 13 14 public static void main(String[] args) { 15 16 System.out.println(); 17 try { 18 eat(0); 19 } catch (NoMoneyException e) { 20 System.out.println("跟我干活吧。"); 21 } 22 } 23 24 25 public static void eat(double money) throws NoMoneyException { 26 if (money < 10) { 27 throw new NoMoneyException("钱不够"); 28 } 29 System.out.println("吃桂林米粉"); 30 } 31 }
运行时异常和非运行时异常
RuntimeException
RunntimeException的子类:
ClassCastException
多态中,可以使用Instanceof 判断,进行规避
ArithmeticException
进行if判断,如果除数为0,进行return
NullPointerException
进行if判断,是否为null
ArrayIndexOutOfBoundsException
使用数组length属性,避免越界
这些异常时可以通过程序员的良好编程习惯进行避免的
1:遇到运行时异常无需进行处理,直接找到出现问题的代码,进行规避。
2:就像人上火一样牙疼一样,找到原因,自行解决即可
3:该种异常编译器不会检查程序员是否处理该异常
4:如果是运行时异常,那么没有必要在函数上进行声明。
6:案例
1:除法运算功能(div(int x,int y))
2:if判断如果除数为0,throw new ArithmeticException();
3:函数声明throws ArithmeticException
4:main方法调用div,不进行处理
5:编译通过,运行正常
6:如果除数为0,报异常,程序停止。
7:如果是运行时异常,那么没有必要在函数上进行声明。
1:Object类中的wait()方法,内部throw了2个异常 IllegalMonitorStateException InterruptedException
1:只声明了一个(throws) IllegalMonitorStateException是运行是异常没有声明。
1 class Demo12 { 2 3 public static void main(String[] args){ 4 div(2, 1); 5 } 6 7 public static void div(int x, int y) { 8 if (y == 0) { 9 throw new ArithmeticException(); 10 } 11 System.out.println(x / y); 12 } 13 }
非运行时异常(受检异常)
如果出现了非运行时异常必须进行处理throw或者try{}catch(){}处理,否则编译器报错。
1;IOException 使用要导入包import java.io.IOException;
2:ClassNotFoundException
2;例如人食物中毒,必须进行处理,要去医院进行处理。
3:案例
1:定义一测试方法抛出并声明ClassNotFoundException(test())
2:main方法调用test
3:编译报错
1:未报告的异常 java.lang.ClassNotFoundException;必须对其进行捕捉或声明以便抛出
1 public void isFile(String path){ 2 try 3 { 4 /* 5 根据文件的路径生成一个文件对象,如果根据该路径找不到相应的文件, 6 则没法生成文件对象。 7 */ 8 File file = new File(path); 9 //读取文件的输入流 10 FileInputStream input = new FileInputStream(file); 11 //读取文件 12 input.read(); 13 } 14 catch (NullPointerException e) 15 { 16 System.out.println("读取默认的文件路径.."); 17 } 18 19 }
4:Sun 的API文档中的函数上声明异常,那么该异常是非运行是异常,
调用者必须处理。
5:自定义异常一般情况下声明为非运行时异常
2:函数的重写和异常
1:运行时异常
1:案例定义Father类,定义功能抛出运行是异常,例如(test() throw
ClassCastException)
2:定义Son类,继承Father类,定义test方法,没有声明异常
3:使用多态创建子类对象,调用test方法
4:执行子类方法
1:函数发生了重写,因为是运行时异常,在父类的test方法中,可以声明throws 也可以不声明throws
1 class Father { 2 void test() throws ClassCastException { // 运行时异常 3 System.out.println("父类"); 4 throw new ClassCastException(); 5 } 6 } 7 8 class Son extends Father { 9 void test() { 10 System.out.println("子类"); 11 } 12 } 13 class Demo14 { 14 15 public static void main(String[] args) { 16 Father f = new Son(); 17 f.test(); 18 } 19 }
2:非运行时异常
1:定义父类的test2方法,抛出非运行时异常,例如抛出ClassNotFoundException
1:此时父类test2方法必须声明异常,因为是非运行时异常
2:Son类定义test2 方法,抛出和父类一样的异常,声明异常
3:使用多态创建子类对象,调用test方法,调用test2方法,
1:声明非运行时异常的方法,在调用时需要处理,所以在main方法调用时throws
2:实现了重写,执行子类的test2方法
3:总结子类重写父类方法可以抛出和父类一样的异常,或
者不抛出异常。
1 // 1 子类覆盖父类方法父类方法抛出异常,子类的覆盖方法可以不抛出异常 2 class Father { 3 void test() throws ClassNotFoundException { // 非运行时异常 4 System.out.println("父类"); 5 throw new ClassNotFoundException(); 6 } 7 } 8 9 class Son extends Father { 10 void test() { 11 System.out.println("子类"); 12 // 父类方法有异常,子类没有。 13 } 14 } 15 class Demo14 { 16 17 public static void main(String[] args) throws ClassNotFoundException { 18 Father f = new Son(); 19 f.test(); 20 21 } 22 }
4:子类抛出并声明比父类大的异常例如子类test2方法抛出Exception
1:编译失败,无法覆盖
2:子类不能抛出父类异常的父类。
3:总结子类不能抛出比父类的异常更大的异常。
1 //2:子类覆盖父类方法不能比父类抛出更大异常 2 class Father { 3 void test() throws Exception { 4 // 非运行时异常 5 System.out.println("父类"); 6 throw new Exception(); 7 } 8 } 9 10 class Son extends Father { 11 void test() throws ClassNotFoundException { // 非运行时异常 12 System.out.println("子类"); 13 throw new ClassNotFoundException(); 14 } 15 } 16 class Demo14 { 17 18 public static void main(String[] args) throws Exception { 19 Father f = new Son(); 20 f.test(); 21 22 } 23 }
3:总结
1:子类覆盖父类方法是,父类方法抛出异常,子类的覆盖方法可以不抛
出异常,或者抛出父类方法的异常,或者该父类方法异常的子类。
2:父类方法抛出了多个异常,子类覆盖方法时,只能抛出父类异常的子
集
3:父类没有抛出异常子类不可抛出异常
1:子类发生非运行时异常,需要进行try{}catch的(){}处理,不能
抛出。
4:子类不能比父类抛出更多的异常
finally
1: 实现方式一:
try{ // 可能发生异常的代码 } catch( 异常类的类型 e ){ // 当发生指定异常的时候的处理代码 }catch...
比较适合用于专门的处理异常的代码,不适合释放资源的代码。
2:实现方式二:
try{ } catch(){} finally{ // 释放资源的代码 }
finally块是程序在正常情况下或异常情况下都会运行的。
比较适合用于既要处理异常又有资源释放的代码
3:实现方式三
try{ }finally{ // 释放资源 }
比较适合处理的都是运行时异常且有资源释放的代码。
4:finally:关键字主要用于释放系统资源。
1:在处理异常的时候该语句块只能有一个。
2:无论程序正常还是异常,都执行finally。
5:finally是否永远都执行?
1:只有一种情况,但是如果JVM退出了System.exit(0),finally就不执行。
2:return都不能停止finally的执行过程。
6:案例使用流
1:使用FileInputStream加载文件。
导包import java.io.FileInputStream;
2:FileNotFoundException
导入包import java.io.FileNotFoundException;
3:IOException
import java.io.IOException;
1 public class FinallyDemo { 2 // 本例子使用finally 关闭系统资源。 3 public static void main(String[] args) { 4 5 FileInputStream fin = null; 6 try { 7 System.out.println("1创建io流可能出现异常"); 8 fin = new FileInputStream("aabc.txt"); // 加载硬盘的文本文件到内存,通过流 9 // System.out.println(fin); 10 } catch (FileNotFoundException e) { 11 System.out.println("2没有找到abc.txt 文件"); 12 System.out.println("3catch 了"); 13 // System.exit(0); 14 // return; 15 } 16 // finally 17 finally { 18 System.out.println("4fianlly执行"); 19 if (fin != null) { // 如果流对象为null 流对象就不存在,没有必要关闭资源 20 try { 21 fin.close(); 22 } catch (IOException e) { 23 e.printStackTrace(); 24 System.out.println("close 异常"); 25 } 26 27 } 28 System.out.println("5finally over"); 29 } 30 System.out.println("6mainover"); 31 } 32 } 33 34 // 2:无论程序正常还是异常,都执行finally。 但是遇到System.exit(0); jvm退出。 35 // finally用于必须执行的代码, try{} catch(){}finally{} 36 // try{}finally{}
posted on 2016-10-14 16:15 Michael2397 阅读(...) 评论(...) 编辑 收藏