总的来说就是先把没有异常的代码先写好,然后在容易出现异常的代码部分进行加工,加入异常处理机制
## 异常关键字
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 -- 用在方法签名中,用于声明该方法可能抛出的异常。主方法上也可以使用throws抛出。如果在主方法上使用了throws抛出,就表示在主方法里面可以不用强制性进行异常处理,如果出现了异常,就交给JVM进行默认处理,则此时会导致程序中断执行。
抛出异常
try-catch代码用来捕获异常
throws 声明来将异常抛出给更上一层的调用者进行处理,新的异常包含原始异常
throw抛出一个异常,程序终止
try {
//可能出现异常的代码
} catch (Exception e) {
//异常处理的相关代码,如:getMessage()、printStackTrace()
} finally {
//无论是否异常,都需执行的代码结构,常用于释放资源
}
方法覆盖
就是多态 自动向上转型,转型后父类引用指向的子类对象只能调用和父类相同的方法并且会覆盖父类方法
import java.io.IOException;
import java.sql.SQLException;
public class TestOverrideExceptionMethod {
public static void main(String[] args) {
Super sup = new Sub();// 父类引用指向子类对象 多态 自动向上转型
try {
sup.method();// 在编译期间,调用的父类中声明的方法是有异常的,需要处理
} catch (Exception e) {
e.printStackTrace();// 处理方案之1:打印堆栈跟踪信息
}
}
}
// 带有异常的方法覆盖
// 1.父类中方法声明了异常。子类重写后可声明也可不声明
// 2.父类中方法没有声明异常,则子类也不可以声明异常。
// 3.父类中方法声明了异常,子类可以声明的异常与其相等或是其子类
// 4.子类可以声明比父类更多的异常。必须小于其父类声明的异常(子类)
class Super {
public void method() throws Exception {
System.out.println("method in Super");
}
}
class Sub extends Super {
public void method() throws ClassNotFoundException, RuntimeException, IOException, SQLException {
System.out.println("method in Sub");
}
}
// 带有异常的接口方法也是与父类方法覆盖的原理相同,这里我就不再一一重复了
interface Printable {
public void print() throws Exception;
}
class MyClass implements Printable {
public void print() throws ClassNotFoundException, RuntimeException {
}
}
自定义异常
import java.util.InputMismatchException;
public class TestDefinedException {
public static void main(String[] args) {
Student stu = new Student();
try {
stu.setAge(250);//是可能出现异常的代码
}catch(Exception e) {
System.err.println(e.getMessage());//只获得报错的原因即可
//这个程序没有这行代码是没法输出红色的异常原因的!!!
}
try {
stu.setSex("未知");//受查异常,编译期间就报错,需要处理
}catch(SexMismatchException se) {//根据方法声明的异常,捕获相应的类型
System.err.println(se.getMessage());
}catch(Exception e ) {
e.printStackTrace();
}
}
}
//自定义异常必须要加上异常类继承Eception
//受查异常(是在编译期间,就必须处理的异常! 需要声明出去)
class SexMismatchException extends Exception{
public SexMismatchException() {}
public SexMismatchException(String message) {
super(message);
}
}
//运行时异常
class AgeInputException extends RuntimeException{
public AgeInputException() {}//支持创建无异常原因信息的异常对象
public AgeInputException(String message) {//提供有参构造方法,支持编写异常原因信息
super(message);//调用父类的有参构造方法,为message属性赋值。
}
}
//在应用场景下,可以根据自身的需要,自定义异常。
class Student{
private int age;//年龄
private String sex;//性别 男 女
public void setSex(String sex) throws SexMismatchException {
//告知调用者,使用该方法,会存在异常。必须处理.声明的异常类型最好与抛出的异常类型一致
if(sex.equals("男") || sex.equals("女")) {
this.sex = sex;
}else {
//在用户输入一个性别后!就做好提醒!性别的输入可能不准确! 受查异常。
throw new SexMismatchException("性别输入的值为:“男”或者“女”");
}
}
public String getSex() {
return this.sex;
}
public int getAge() {
return this.age;
}
public void setAge(int age){
if(age > 0 && age < 123) {
this.age = age;
}else {
throw new AgeInputException("年龄的赋值应该在0岁到123岁之间");
//抛运行时异常的父类。 不合理。现存的定义好的异常,没有符合现在程序的场景
}
}
}
**自定义的异常应该总是包含如下的构造函数:**
- 一个无参构造函数
- 一个带有String参数的构造函数,并传递给父类的构造函数。
- 一个带有String参数和Throwable参数,并都传递给父类构造函数
- 一个带有Throwable 参数的构造函数,并传递给父类的构造函数。
**throw和throws的区别?**
throw和throws都是在异常处理中使用的关键字,区别如下:
1. throw:指的是在方法中人为抛出一个异常对象(这个异常对象可能是自己实例化或者抛出已存在的);
2. throws:在方法的声明上使用,表示此方法在调用时必须处理异常。
**throws抛出异常**
一个方法可能会出现异常但是没法处理异常,可以在方法声明处用throws子句来声明抛出异常。该方法并不会对这些异常进行处理,而是调用此方法的方法会进行异常处理.
**throws抛出异常的规则:**
1. 如果是不可查异常(unchecked exception),即Error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,编译仍能顺利通过,但在运行时会被系统抛出。
2. 如果一个方法可能出现可查异常(checked exception),要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误。
3. 只有当抛出了异常时,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常的时候,应该继续抛出。
4. 调用方法必须遵循任何可查异常的处理和声明规则。若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。
最基本的throw用法
public class TestException {
public static void main(String[] args) {
int a = 6;
int b = 0;
try {
if (b == 0) {
throw new ArithmeticException();
//"除数为0"等ArithmeticException,是RuntimException的子类。而运行时异常将由运行时系统自动抛出,不需要使用throw语句,这里把throw new ArithmeticException()去掉也是不影响运行结果的。
}
System.out.println("a/b的值是:" + a / b);
} catch (ArithmeticException e) {
System.out.println("程序出现异常,变量b不能为0。");
}
System.out.println("程序正常结束。");
}
}
throw都是在try语句模块内完成的,哪怕有时候try语句后面只引用了一个方法,但是在这个方法里面还是throw。
throws抛出异常,方法的调用者必须处理该异常,就是必须try catch处理这个异常,或者继续throws继续往上抛,并且throws并不确定到底是个什么异常;throw抛出异常,会明确告诉你这个地方是什么异常,程序到这终止了,调用该方法的地方报错,不继续向后执行。
继承与异常
步骤:
1.创建包com.itheima.level2_02
2.构建异常体系
a)定义PainExecption(疼异常)类继承Exception
i.提供空参和有参构造,注意有参构造必须调用父类的有参构造
b)定义ToothPainException(牙疼异常)类和TonguePainException(舌头疼异常)类继承PainException
i.提供空参和有参构造,注意有参构造必须调用父类的有参构造
c)定义BigToothPainException(大牙疼),FrontToothPainExcption(门牙疼)继承ToothPainException(牙痛)
i.提供空参和有参构造,注意有参构造必须调用父类的有参构造
3.定义用于测试的父类Father
a)提供一个吃(eat)的成员方法,抛出牙疼异常
i.输出吃到一个石子
ii.抛出一个牙疼的异常
b)提供一个喝(drink)的成员方法不抛出异常
i.输出: 喝什么都没有问题
4.定义用于测试的子类Son,继承Father
a)验证: 重写方法不能抛出与父类平级的其他异常
i.重写父类的eat方法,抛出与牙疼平级舌头疼异常,编译报错,说明这句话是对的
ii.注释掉这个重写的方法,验证下一个
b)验证: 重写方法不能抛出比父类高级的异常
i.重写父类的eat方法,抛出比牙疼高级疼异常,编译报错,说明这句话是对的
c)验证:重写方法: 可以抛出和父类同样的异常
i.重写父类的eat方法,也抛出牙疼异常,编译通过,说明这句话是对的
d)验证:重写方法: 可以抛出父类抛出异常的子类异常.
i.重写父类的eat方法,抛出牙疼异常的子类异常大牙疼异常,编译通过,说明这句话是对的
e)验证: 重写方法: 可以抛出多个 “父类抛出异常的子类异常”.
i.重写父类的eat方法,抛出牙疼异常的两个子类异常,大牙疼和门牙疼,编译通过,说明这句是对的
f)验证:父类不抛出异常,子类也不能抛出异常(编译时异常)
i.重写父类的drink方法,抛出(牙疼,舌头疼,门牙疼)任意一个编译异常,编译都报错,说明这句话是对的
g)验证: 如果子类内部抛出了异常(或调用抛出异常的方法)只能自己处理,不允许向外抛.
i.重写父类的drink方法
ii.输出喝到了100度的水
iii.抛出舌头疼异常
iv.这个时候编译报错
v.把上面两句代码放到try代码块中
vi.在catch代码块中打印异常信息编译通过.
//1.重写方法不能抛出与父类平级的其他异常
public void eat() throws TonguePainException{
throw new TonguePainException("吃到一个石子");
}
//2.重写方法不能抛出比父类高级的异常
public void eat() throws PainException{
throw new PainException("吃到一个石子");
}
//3.重写方法: 可以抛出和父类同样的异常
public void eat() throws ToothPainException{
throw new ToothPainException("吃到一个石子");
}
//4.重写方法: 可以抛出父类抛出异常的子类异常
public void eat() throws BigToothPainException{
throw new BigToothPainException("吃到一个石子");
}
//5.重写方法: 可以抛出多个 "父类抛出异常的子类异常".
public void eat() throws BigToothPainException,FrontToothPainExcption{
System.out.println("吃到一个石子");
}
//6.父类不抛出异常,子类也不能抛出异常(编译时异常)
public void drink() throws ToothPainException{
System.out.println("喝什么都没有问题");
}
//7.如果子类内部抛出了异常(或调用抛出异常的方法)只能自己处理,不允许向外抛.
public void drink(){
try {
throw new TonguePainException("喝到100度的水");
} catch (TonguePainException e1) {
// TODO 自动生成的 catch 块
e1.printStackTrace();
}
}