1 异常的概述和分类
异常的概述 ( 异常就是Java程序在运行过程中出现的错误)
异常的继承体系
Throwable
Error ( 服务器宕机,数据库崩溃等 比较严重的错误,不在本次学习范围内)
Exception (重点 )
RuntimeException (重点 运行时异常)
2 JVM默认是如何处理异常的
main函数收到这个问题时,有两种处理方式:
a:自己将该问题处理,然后继续运行
b:自己没有针对的处理方式,只有交给调用main的jvm来处理
jvm有一个默认的异常处理机制,就将该异常进行处理.
并将该异常的名称,异常的信息.异常出现的位置打印在了控制台上,同时将程序停止运行
2.1 小实例
public class Damo1 { public static void main(String[] args) { //调用除数方法将返回结果给x 出现异常 x处理不了 上抛异常交给调用main的jvm处理 jvm处理该异常 并将异常信息打印在控制台 程序停止运行 int x = div(10, 0); System.out.println(x); } public static int div(int a,int b) { //a = 10,b = 0 //被除数是10,除数是0 当除数是0的时候违背了算数运算法则,抛出异常返回new ArithmeticException("/ by zero"); return a / b; } }
3 处理异常的基本格式 ( try…catch…finally)
try:用来检测异常的
catch:用来捕获异常的
finally:不管有没有异常都会执行 通常用来释放资源
当通过tryc atch将问题处理了,程序会继续执行
3.1 注意事项
try后面如果跟多个catch,那么小的异常放前面,大的异常放后面,根据多态的原理如果大的放前面,就会将所有的子类对象接收后面的catch就没有意义了
3.2 jdk7 新特性
try{} catch (ArithmeticException | ArrayIndexOutOfBoundsException e) {}可用这种语法在一个catch里写入多个异常
不过开发不常用这种语法 通常直接一个try{} catch (Exception e){} 捕获多个异常
3.3 异常处理小实例
public class Damo2 { public static void main(String[] args) { try { int x = 10/0; // 1 将计算结果给x出现异常 x处理不了 上抛异常被catch捕获 System.out.println(x); } catch (ArithmeticException a) { // 2 捕获异常 ArithmeticException a = new ArithmeticException(); System.out.println("捕获异常"); } finally { // 3 finally不管有没有捕获异常都会执行 System.out.println("执行最终finally"); } } }
4 编译期异常和运行期异常的区别
Java中的异常被分为两大类:编译时异常和运行时异常。
所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常
运行时异常
就是程序员所犯得错误,需要回来修改代码 无需显示处理,但也可以和编译时异常一样处理
编译时异常
在编译某个程序的时候,有可能会有这样那样的事情发生,比如文件找不到,这样的异常就必须在编译的时候处理 如果不处理编译通不过
4.1 常见编译时异常 (XXXX.text 这个文件很有可能是不存在的 所以需要处理这个异常)
public static void main(String[] args) { FileInputStream fis = new FileInputStream("XXXX.text"); }
4.1.1 处理方式
public static void main(String[] args) { try { FileInputStream fis = new FileInputStream("XXXX.text"); } catch (FileNotFoundException e) { e.printStackTrace(); } }
5 异常Throwable的几个常见方法
Throwable的几个常见方法
getMessage() 获取异常信息,返回字符串。
toString() 获取异常类名和异常信息,返回字符串。
printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。
5.1 Throwable的几个常见方法的基本使用
public class Damo4 { public static void main(String[] args) { try { System.out.println(1/0); } catch (Exception e) { //捕获异常 Exception e = new ArithmeticException("/ by zero"); System.out.println(e.getMessage()); //获取异常信息 System.out.println("=============="); System.out.println(e); //默认自动调用toString方法,打印异常类名和异常信息 System.out.println("=============="); e.printStackTrace(); //获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void jvm默认使用这种方式处理异常 } } }
6 throws的方式处理异常
定义功能方法时,需要把出现的问题暴露出来让调用者去处理 那么就通过throws在方法上标识。
6.1 throws和throw的区别
throws
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throw
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
6.2 举例分别演示编译时异常和运行时异常的抛出 ( 编译时异常的抛出必须对其进行处理 运行时异常的抛出可以处理也可以不处理,因为这是逻辑错误,需要修改代码的)
编译时异常抛出
public class Damo5 { public static void main(String[] args) throws Exception { // 3 继续上抛异常交给jvm处理 Person p = new Person(); p.setAge(-11); // 因Person类的setAge()方法声明并上抛异常 所以这里main方法要继续上抛异常 } } class Person{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) throws Exception { // 2 上抛异常 if(age >0 && age <= 150) { this.age = age; }else { throw new Exception("年龄非法"); // 1 声明异常 } } }
运行时异常的抛出
public class Damo5 { public static void main(String[] args) { Person p = new Person(); p.setAge(-11); } } class Person{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { if(age >0 && age <= 150) { this.age = age; }else { // 方法中抛出一个运行时异常 方法上不用做处理 throw new RuntimeException("年龄非法"); } } }
7 finally关键字的特点及作用
finally的特点
被finally控制的语句体一定会执行
特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))
finally的作用
用于释放资源,在IO流操作和数据库操作中会见到
finally关键字小实例
public class Damo6 { public static void main(String[] args) { try { System.out.println(10 / 0); } catch (Exception e) { System.out.println("除数为零了"); //System.exit(0); //提前退出jvm虚拟机那么finally语句块不会执行 //return语句相当于是方法结束时的最后一口气,那么在它即将结束之前会看一看有没有finally帮其完成遗愿,如果有就将finally执后在彻底返回 return; } finally { System.out.println("看看我执行了吗"); } } }
常见面试题题
7.1 final,finally和finalize的区别
final 可以修饰类,不能被继承 修饰方法,不能被重写修饰变量,只能赋值一次
finally 是try语句中的一个语句体,不能单独使用,用来释放资源
finalize 是一个方法,当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
7.2 如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问是在return前还是return后。
答: finally会执行,在return前。 return会先建立返回路径,然后去执行finally语句块的内容,最后再彻底返回
小练习
public class Damo7 { // 测试 x 的值 public static void main(String[] args) { Demo d = new Demo(); System.out.println(d.method());// 输出30 } } class Demo { public int method() { int x = 10; // 1 x =10 try { x = 20; // 2 x = 20 System.out.println(1 / 0); // 3 发现异常 被catch捕获 return x; } catch (Exception e) { x = 30; // 4 x = 30 return x; // 5 建立返回路径 将x=30暂时存起来 查看finally语句块后 将暂存内容返回 } finally { x = 40; // 6 虽然这里对 x 进行了赋值 可是catch只会返回暂存的内容所以这句话是没有意义的 //return x; 千万不要在finally里面写返回语句,因为finally的作用是为了释放资源,是肯定会执行的 //如果在这里面写返回语句,那么try和catch的结果都会被改变,所以这么写就是犯罪 } } }
8 自定义异常
为什么需要自定义异常( 通过名字区分到底是什么异常,有针对的解决办法,有利于我们排错)
8.1 小实例 自定义Exception 异常类 继承Exception
public class AgeOutOfBoundsException extends Exception { public AgeOutOfBoundsException() { } // 重写构造方法 可以传入指定的异常信息 // message 异常信息是Throwable类的字符串变量detailMessage //当调用getMessage()方法时 返回的就是字符串变量detailMessage public AgeOutOfBoundsException(String message) { super(message); } }
使用自定义异常AgeOutOfBoundsException
public class Damo5 { public static void main(String[] args) throws AgeOutOfBoundsException { //上抛异常 jvm做处理 Person p = new Person(); p.setAge(-11); //因为Person类的setAge()方法抛出了一个异常 所以这里要处理 我们选择main方法继续上抛 } } class Person{ private String name; private int age; public void setAge(int age) throws AgeOutOfBoundsException { //上抛异常 if(age >0 && age <= 150) { this.age = age; }else { // 使用自定义异常AgeOutOfBoundsException() 异常方法中抛出Exception编译时异常 方法要进行处理 throw new AgeOutOfBoundsException("年龄非法"); } } }
8.2 小实例 自定义RuntimeException 异常类 继承自RuntimeException
public class AgeOutOfBoundsException extends RuntimeException { public AgeOutOfBoundsException() { } // 重写构造方法 可以传入指定的异常信息 // message异常信息是Throwable类的字符串变量detailMessage //当调用getMessage()方法时 返回的就是字符串变量detailMessage public AgeOutOfBoundsException(String message) { super(message); } }
使用自定义异常AgeOutOfBoundsException (因为是RuntimeException 所以不用 throws 抛出异常也可以)
public class Damo5 { public static void main(String[] args) { Person p = new Person(); p.setAge(-11); } } class Person{ private String name; private int age; public void setAge(int age) { if(age >0 && age <= 150) { this.age = age; }else { // 方法中抛出一个运行时异常 方法上不用做处理 throw new AgeOutOfBoundsException("年龄非法"); } } }
9 异常注意事项
子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)
如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws
如何使用异常处理
原则:如果该功能内部可以将问题处理,用try,如果处理不了,交由调用者处理,这是用throws
区别:
后续程序需要继续运行就try
后续程序不需要继续运行就throws
如果JDK没有提供对应的异常,需要自定义异常。
小练习
键盘录入一个int类型的整数,对其求二进制表现形式
如果录入的整数过大,给予提示,录入的整数过大请重新录入一个整数BigInteger
如果录入的是小数,给予提示,录入的是小数,请重新录入一个整数
如果录入的是其他字符,给予提示,录入的是非法字符,请重新录入一个整数
import java.math.BigDecimal; import java.math.BigInteger; import java.util.Scanner; public class Damo8 { public static void main(String[] args) { System.out.println("请输入一个整数:"); // 1 创建键盘录入对象 Scanner sc = new Scanner(System.in); while (true) { //死循环 // 2 将键盘录入的结果存储在String字符串中,存储int如果有不符合条件的结果会直接报错,无法进行后续判断 String str = sc.nextLine(); try { int num = Integer.parseInt(str);//如果字符串正常将字符转换为整数 不正常抛出异常 System.out.println(Integer.toBinaryString(num));//将字符转换为二进制数并输出 break;//正确则跳出循环 } catch (Exception e1) { try { new BigInteger(str); //能放在BigInteger 证明是一个过大的整数 放不进则继续抛出异常 System.out.println("录入错误,你输入的是一个过大的整数,请重新输入"); } catch (Exception e2) { try { new BigDecimal(str);//能放在BigDecimal 证明是一个小数 放不进则继续抛出异常 System.out.println("录入错误你输入的是一个小数,请重新输入"); } catch (Exception e3) {//直接抛异常 死循环继续 System.out.println("录入错误你输入的是非法字符,请重新输入"); } } } } } }