一.异常处理的概念
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出java.lang.ArithmeticException的异常。
异常发生的原因有很多,通常包含以下几大类:
用户输入了非法数据。
要打开的文件不存在。
网络通信时连接中断,或者JVM内存溢出。
这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。-
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
异常指不期而至的各种状况,如:文件找不到、网络连接失败、除0操作、非法参数等。异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程。
Java语言在设计的当初就考虑到这些问题,提出异常处理的框架的方案,所有的异常都可以用一个异常类来表示,不同类型的异常对应不同的子类异常(目前我们所说的异常包括错误概念),定义异常处理的规范,在JDK1.4版本以后增加了异常链机制,从而便于跟踪异常。
Java异常是一个描述在代码段中发生异常的对象,当发生异常情况时,一个代表该异常的对象被创建并且在导致该异常的方法中被抛出,而该方法可以选择自己处理异常或者传递该异常。
二.常见到的异常
Throwable 异常处理的父类
Error 系统崩溃 数据库崩溃 (可以说和我们的代码没有关系)
Exception 异常 (是跟我们代码有关系的)
RuntimeException 运行时异常
常见的异常
// 空指针异常
// 角标异常
// 算数异常
int[] array = {1,3,5,7};
// 这时候就是角标越界
System.out.println(array[]);
array = null;// array 指向一个空内存
// 这个时候就是空指针异常(访问一块不属于你的内存区域)
System.out.println(array[]);
// 算数异常 也就是说不符合算数规则
System.out.println( / );
2.出现异常 系统如何进行处理?
a).main函数自己解决
b).报问题给上级去解决 (这里谁调用main函数 谁去解决)
交给JVM虚拟机去解决 然后JVM给你报错 打印错误信息 错误发生在什么类的什么位置
三.异常处理
Java异常处理涉及到五个关键字,分别是:try、catch、finally、throw、throws。下面将骤一介绍,通过认识这五个关键字,掌握基本异常处理知识。
• try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
• catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。
• finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
• throw – 用于抛出异常。
• throws – 用在方法签名中,用于声明该方法可能抛出的异常。
1.捕获异常的流程
1.发生异常
2.根据发生的异常 产生对应的异常对象
3.这个异常对象会返回给调用者
4.如果调用者处理了这个异常(try – catch) 异常对象会跟catch进行匹配 匹配上 执行catch中的语句 程序会继续执行
5.如果这个调用者没有处理这个异常 默认交给JVM去处理 根据产生异常 打印对应的错误信息 程序停止
1.举个算数异常的例子
// 测试异常
// 举个算数异常的例子
TestException test = new TestException();
try {
//可能发生异常的代码 要检测的代码
int number = test.fun(, );
// 这块返回了异常对象
// new ArithmeticException ("/ by zero")
System.out.println(number);
} catch (ArithmeticException e) {
// 捕获异常 该怎么办
// catch 如何就能捕获异常?
// ArithmeticException e = new ArithmeticException ("/ by zero")
// catch会匹配这个异常对象
// 如果匹配上了 就执行 catch中的语句
// 捕获异常后 程序会继续执行
System.out.println("吃药没 ? 除数为0");
}
class TestException{
// 报异常方法
public int fun(int a,int b) {
/*
* 这里发生了算数异常
* 相当于 产生了异常对象
* new ArithmeticException ("/ by zero")
*
* 一旦发生异常 系统会产生一个对应异常现象
* 发生异常后 谁调用我 这个异常就给谁
* 处理不了 就给JVM虚拟机去处理
* JVM默认处理方法: 打印错误信息(根据异常对象的类型 去打印对应错误信息)
* 然后打印完会结束你的程序
*
*/
return a / b;
}
}
使用多重的catch语句:很多情况下,由单个的代码段可能引起多个异常。处理这种情况,我们需要定义两个或者更多的catch子句,每个子句捕获一种类型的异常,当异常被引发时,每个catch子句被依次检查,第一个匹配异常类型的子句执行,当一个catch子句执行以后,其他的子句将被旁路。
编写多重catch语句块注意事项:
顺序问题:先小后大,即先子类后父类
public static void main(String[] args) {
int[] array = {,,,};
try {
// 测试代码异常
System.out.println(/);
// 产生new ArithmeticException();
// 之后就去匹配
// 匹配成功后 程序继续向下执行
System.out.println(array[]);
}catch (ArithmeticException e) {
/*
* 注意: 如果使用Exception直接匹配异常
* 多个catch同时捕获时 需要写在最后
* 写前面 后面的catch相当于白写了
*/
System.out.println("病了治 除数为0");
}catch (ArrayIndexOutOfBoundsException e) {
// 如果发生多个异常 需要匹配多个catch
System.out.println("继续治疗吧 别放弃");
}catch (Exception e) {
System.out.println("你出错了");
}
注意:
Java通过异常类描述异常类型。对于有多个catch子句的异常程序而言,应该尽量将捕获底层异常类的catch子句放在前面,同时尽量将捕获相对高层的异常类的catch子句放在后面。否则,捕获底层异常类的catch子句将可能会被屏蔽。
RuntimeException异常类包括运行时各种常见的异常,ArithmeticException类和ArrayIndexOutOfBoundsException类都是它的子类。因此,RuntimeException异常类的catch子句应该放在最后面,否则可能会屏蔽其后的特定异常处理或引起编译错误。
2.finally
记住: 不管你异常有没有发生 有没有被匹配到 都会执行
finally有什么作用?
可以关闭系统资源 避免资源的浪费
(例如 关闭输入流 和 关闭数据库)
例子:
public static void main(String[] args) {
try {
System.out.println(/);
return;
} catch (ArithmeticException e) {
System.out.println("除0");
}finally {
System.out.println("你看我执行了吗");
}
System.out.println("我是下面的语句");
}
那么 final finally finalize有什么区别?
没什么联系 一点关系没有
final 用于修饰属性、方法和类,分别表示 属性不可变、方法不能被重写和类不可被继承
final属性:被final修饰的变量不可变。由于不可变有两重含义:一是引用不可变;二是对象不可变。
final方法:当一个方法被修饰为final时,该方法不允许任何子类重写这个方法,但子类仍可以使用这个方法。
final类:当一个类被修饰为final时,此类不能被继承,所有方法都不能被重写。
当这并不表示final类的成员变量也是不可改变的。
finally:中语句 一定被执行(异常处理的语句)
finalize:是Object类的一个方法 所有java中对象都有这个方法
该方法在垃圾回收时候 被系统自动调用
当一块内存空间 无人引用 这个块内存就是垃圾
3.运行时异常 和 编译时异常
运行时异常 和 编译时异常
除了运行时异常 就是编译时异常
出现运行时异常---就是代码问题(程序员犯的错误)
出现编译时异常---问题的提前预警 强制你去处理
不处理 编译不通过
public class Demo05 {
public static void main(String[] args) throws FileNotFoundException {
// 编译时异常
// 系统这时候 不知道你到底有没有这个文件
// 相当于系统问你 要是没有这个文件咋整?
// 这时系统会强制 要求你对这个问题作出解释
// 这就是编译时异常(相当于对问题的 一种提前准备)
//
/*
* 解决异常有两张方式
* 1.自己处理 try..catch
* (想让程序继续执行 就用try)
* 2.把异常抛出去(谁调用我 抛给谁 谁去处理)
* (向让程序遇到错误停止 就抛)
*/
/*
try {
FileInputStream fileInputStream = new FileInputStream("wl.txt");
} catch (Exception e) {
System.out.println("读入文件没找到");
}
*/
FileInputStream fileInputStream = new FileInputStream("wl.txt");
}
}
4 . throw和throws关键字的区别
throw用来抛出一个异常,在方法体内。语法格式为:throw 异常对象。
throws用来声明方法可能会抛出什么异常,在方法名后,语法格式为:throws 异常类型1,异常类型2…异常类型n。
5 . 自定义异常类
创建Exception或者RuntimeException的子类即可得到一个自定义的异常类。例如:
public class MyException extends Exception{
public MyException(){}
public MyException(String smg){
super(smg);
}
}
6、使用自定义的异常
用throws声明方法可能抛出自定义的异常,并用throw语句在适当的地方抛出自定义的异常。例如:
在某种条件抛出异常
public void test1() throws MyException{
…
if(….){
throw new MyException();
}
}
public static void main(String[] args) throws Exception{
Prson prson = new Prson();
prson.setAge();
System.out.println(prson.getAge());
}
}
// 创建一个人类
class Prson{
// 姓名
private String name;
// 年龄
private int age;
public Prson(String name, int age) {
this.name = name;
this.age = age;
}
public Prson() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
// 如果这个方法 要抛出异常 给调用者 那么必须在方法的声明上 标识出这个方法为异常方法
// throws Exception 标识抛出什么异常
/*
* throws 和 throw 区别?
* 1.throws 标识这个方法 可以抛出一个异常(方法的声明出现)
* throw 抛出一个异常对象(方法中出现)
*
*/
public void setAge(int age) throws Exception{
// 年龄的判断
if (age > && age < ) {
this.age = age;
}else {
// 抛出一个异常 告诉他你输入的不对
// throw 关键字 抛出一个异常对象
// 创建一个异常对象
//Exception e = new Exception("年龄非法");
// 把这个异常对象抛出去
// 抛给谁? 谁用抛给谁
// throw new Exception("年龄非法")
// 使用自定义类处理异常
AgeOutOfBoundsException e = new AgeOutOfBoundsException("年龄非法");
throw e;
}
}
@Override
public String toString() {
return "Prson [name=" + name + ", age=" + age + "]";
}
}
// 创建一个自定义的异常类(继承 Exception)
// 再创建异常类时 需要直观让人知道这是什么异常
class AgeOutOfBoundsException extends Exception{
// 重写构造方法
public AgeOutOfBoundsException() {
super();
}
public AgeOutOfBoundsException(String message) {
super(message);
// TODO Auto-generated constructor stub
}
}
7.异常转译
将异常转型(也叫转译),使得异常更易读易于理解
public void test2() throws MyException{
…
try{
…
}catch(Exception e){
…
throw new MyException();
}
}
还有一个代码,很有意思:
public void test2() throws MyException{
…
try {
…
} catch (MyException e) {
throw e;
}
}
这段代码实际上捕获了异常,然后又和盘托出,没有一点意义,如果这样还有什么好处理的,不处理就行了,直接在方法前用throws声明抛出不就得了。异常的捕获就要做一些有意义的处理。
其实就是在
异常转译
必须捕获算数异常 但无权处理
将异常上抛 且进一步明确异常种类