JavaSE进阶回顾第三天-异常

第三章

  • 异常的基本概念
  • 异常的分类
  • 异常的捕捉和处理
  • 自定义异常
  • 方法覆盖与异常
java异常的处理机制及分类

异常在Java中以类和对象的形式存在。异常的继承结构图怎样的呢?

Object

Throwable(可抛出的)

Throwable下有两个分支Error(不可处理,直接退出JVM)和Exception(可处理的)

Exception下有两个分支:

​ Exception的直接子类:编译时异常。要求必须在编写程序阶段对这些异常进行处理,否则会报错。

​ RunTimeException:运行时异常。在编写程序阶段可以预处理,也可以不管,都行。

注意:编译时异常和运行时异常,都是发生在运行阶段,编译阶段异常是不会发生的。

编译时异常必须在编译(编写)阶段预处理,如果不处理编译器会报错,因此得名。

编译时异常和运行时异常的区别?

编译时异常发生的概 率高。

运行时异常发生的概率低。

对于一些发生概率较高的异常,需要在运行之前对其进行预处理。

编译时异常又称:

受检异常 CheckedException

受控异常

运行时异常又称:

未受检异常 UnCheckedException

非受控异常

所有异常都是发生在运行阶段。

Java异常

异常的继承结构图1:
在这里插入图片描述

在这里插入图片描述

异常的捕捉和处理

方式一:throws抛出异常,抛给上一级。

方式二:使用 try…catch语句进行异常的捕捉。

/**
 * 程序执行到此处发生java.lang.ArithmeticException: / by zero异常,
 * 底层new了一个ArithmeticException异常对象,然后抛出了,由于main方法
 * 调用了100/0,所以这个异常ArithmeticException抛给了main方法,main
 * 方法没有处理,将这个异常自动报给了JVM。
 * JVM最终终止程序的执行。
 *
 * ArithmeticExceptionj继承RuntimeException,属于运行时异常,在编写程序阶段
 * 不用对该异常进行预先处理
 */
public class ExceptionTest03 {
    public static void main(String[] args) {
        System.out.println(10/0);
        System.out.println("hahaha");
    }
}
两种处理异常方式
public class ExceptionTest05 {
    //第一种方式:上抛throws ClassNotFoundException
    //上抛类似于推卸责任(继续报异常传递给调调用者。)
    /*
    public static void main(String[] args) throws ClassNotFoundException {
        doSome();
    }*/

    //第二种方式:try...catch进行捕捉
    //捕捉等于把异常拦下了,异常真正的解决了。(调用者是不知道的)
    public static void main(String[] args) {
        try {
            doSome();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void doSome() throws ClassNotFoundException{
        System.out.println("doSome方法执行!!!");
    }
}
异常例子
/**
 * 注意:只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行。
 * 另外需要注意:try语句块某一行出现异常,该行后面的代码不会执行,try...catch捕捉后,
 * 后续代码可以执行。
 */
public class ExceptionTest06 {
    public static void main(String[] args) {
        //一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,这个异常一定会抛给JVM,JVM只有终止。
        //异常处理机制的作用就是增强程序的健壮性。
        //一般main方法中的异常建议使用try...catch进行捕捉。main就不要继续上抛了。
        try {
            m1();
            //以上代码出现异常,直接进入catch语句块。
            System.out.println("aaa");
        } catch (FileNotFoundException e) {
            //这个分支可以使用e引用,e引用保存的内存地址是哪个new出来的异常对象的内存地址。
            //catch是捕捉异常之后走的分支。
            System.out.println("文件找不到");
        }
        //try...catch把异常抓住之后,这里的代码会继续执行。
        System.out.println("111");
    }

    private static void m1() throws FileNotFoundException {
        m2();
        //以上代码出异常,下面的代码是无法执行的
    }

    private static void m2() throws FileNotFoundException {
        m3();
        //以上代码出异常,下面的代码是无法执行的
    }

    private static void m3() throws FileNotFoundException {
        //调用SUN jdk中某个类的构造方法
        //创建一个输入流,该流指向一个文件
        /*
        编译保存的原因:
            第一:调用了一个构造方法
         */
        new FileInputStream("D:\\aa.txt");
        //以上代码出异常,下面的代码是无法执行的

    }
}
深入try…catch
/**
 * 深入try...catch
 * 1、catch后面的小括号中的类型可以是具体的异常类型,也可以是该类型的父类异常类型。
 * 2、catch可以写多个。建议catch的时候精确的一个一个处理,这样有利于查询的调试。
 * 3、catch写多个的时候,从上到下,必须遵守从小到大的,否则会编译报错。
 * 先写catch FileNotFoundException e,再catch IOException e
 */
public class ExceptionTest07 {
    /*
    public static void main(String[] args) throws Exception, FileNotFoundException,NullPointerException {

    }
     */
    /*
    public static void main(String[] args) throws Exception{

    }
     */
    public static void main(String[] args) {
        /*
        try {
            FileInputStream fis = new FileInputStream("F:\\研生课程\\1. 英语写译(周一56节)\\新建 文本文档.txt");
            System.out.println("aaa");
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在!");
        }
        System.out.println("hello Tom");
         */

        //以下异常用父类引用也可以
        /*
        try {
            FileInputStream fis = new FileInputStream("F:\\研生课程\\1. 英语写译(周一56节)\\新建 文本文档.txt");
            System.out.println("aaa");
        } catch (IOException e) {//多态:IOException e = new FileNotFoundException();
            System.out.println("文件不存在!");
        }
        System.out.println("hello Tom");
         */

        /*
        try {
            //创建输入流
            FileInputStream fis = new FileInputStream("F:\\研生课程\\1. 英语写译(周一56节)\\新建 文本文档.txt");
            //读文件
            fis.read();
        } catch (FileNotFoundException e) {
            System.out.println("文件不存在!");
        } catch (IOException e) {
            System.out.println("读文件报错了!");
        }
        System.out.println("hello Tom");
         */

        //JDK8的新特性!
        try {
            //创建输入流
            FileInputStream fis = new FileInputStream("F:\\研究生课程\\1. 英语写译(周一56节)\\新建 文本文档.txt");

            //进行数学运算
            System.out.println(10/0);//运行时异常

        } catch (FileNotFoundException | ArithmeticException | NullPointerException e) {
            System.out.println("文件不存在?数学异常?空指针异常?");
        }
        System.out.println("hello Tom");
    }
}
异常对象的两个方法
/**
 * 异常对象有两个非常重要的方法:(一般使用第二个!!!)
 *
 * 获取异常简单的描述信息:
 *      String msg = exception.getMessage();
 *
 * 打印异常追踪的堆栈信息:
 *      exception.prinntStackTrace();
 */
public class ExceptionTest08 {
    public static void main(String[] args) {
        //这里只是为了测试getMessage()方法和printStackTrace()方法。
        //这里只是new了异常对象,但是没有将异常对象抛出。JVM会认为这是一个普通的java对象。
        NullPointerException npe = new NullPointerException("空指针异常");
        //获取异常简单的描述信息
        String msg =npe.getMessage();
        System.out.println(msg);

        //打印异常追踪的堆栈信息
        npe.printStackTrace();

        for(int i=0;i<1000;i++){
            System.out.println("i="+i);
        }

        System.out.println("Hello World");

    }
}
/**
 * 异常对象的两个方法
 *      String msg = exception.getMessage();
 *      exception.prinntStackTrace();
 * 我们以后查看异常的追踪信息,我们应该怎么看?
 * 从上往下一行一行看,SUN写的代码不用看。主要的问题出现在自己编写的代码上先看33行、然后29、25、14。
 */
public class ExceptionTest09 {
    public static void main(String[] args) {
        try {
            m1();
        } catch (FileNotFoundException e) {
            //打印异常追踪的堆栈信息,在实际开发中建议用这个。
            e.printStackTrace();
            /*
            java.io.FileNotFoundException: F:\研究生程\1. 英语写译(周一56节)\新建 文本文档.txt (系统找不到指定的路径。)
	            at java.io.FileInputStream.open0(Native Method)
	            at java.io.FileInputStream.open(FileInputStream.java:195)
	            at java.io.FileInputStream.<init>(FileInputStream.java:138)
	            at java.io.FileInputStream.<init>(FileInputStream.java:93)
	            at com.chinasoft.javase.exception.ExceptionTest09.m3(ExceptionTest09.java:33)
	            at com.chinasoft.javase.exception.ExceptionTest09.m2(ExceptionTest09.java:29)
	            at com.chinasoft.javase.exception.ExceptionTest09.m1(ExceptionTest09.java:25)
	            at com.chinasoft.javase.exception.ExceptionTest09.main(ExceptionTest09.java:14)
             //
             */
        }

        //这里不耽误执行,很健壮!(服务器不会因为异常而宕机)
        System.out.println("hello Tom");
    }

    private static void m1() throws FileNotFoundException {
        m2();
    }

    private static void m2() throws FileNotFoundException {
        m3();
    }

    private static void m3() throws FileNotFoundException {
        new FileInputStream("F:\\研究课程\\1. 英语写译(周一56节)\\新建 文本文档.txt");

    }
}
try…catch中的finally子句
/**
 * 关于try..catch中的finally子句:
 * 1、在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块代码中出现了异常。
 *      finally语句必须与try一起出现,不能单独使用。
 * 2、finally语句通常使用在哪些情况呢?
 *      通常在finally语句中完成资源的释放/关闭。
 *      因为finally中的代码比较有保障。
 *      即使try语句块出现了异常,finally中的代码也会正常执行。
 */
public class ExceptionTest10 {
    public static void main(String[] args) {
        FileInputStream fis = null;//声明位置放到try外面这样在finally中才能用。
        try {
            fis = new FileInputStream("D:\\aa.txt");
            //...
            String s=null;
            //这里一定会出现空指针异常。
            s.toString();

            //流使用完需要关闭,因为流是占用资源的。
            //即使以上程序出现异常,流也必须关闭。
            //放在这里有可能流关不了
            //fis.close();
            System.out.println("aaa");

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NullPointerException e){
            e.printStackTrace();
            System.out.println("空指针异常了");
        } finally {
            System.out.println("hello 浩克");
            //流的关闭放在这里比较保险。
            //finally中的代码一定会执行。
            //即使try中出现了异常!
            if (fis!=null){//避免空指针异常
                try {
                    //close()方法有异常采用捕捉的方式。
                    fis.close();
                    System.out.println("fis已关闭");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("finally代码执行!");
        }
    }
}
try…finally联合使用
/**
 * 放在finally语句中的代码一定会执行的。(再次强调!!!)
 */
public class ExceptionTest11 {
    public static void main(String[] args) {
        /*
        try和catch,没有catch可以吗?可以。
        try不能单独使用
        try finally 可以联合使用。
        以下代码的执行顺序:
            先执行try...
            在执行finally...
            最后执行 return(return语句只要执执行,方法比如结束。);
         */
        try {
            System.out.println("try....");
            return;
        }finally {
            System.out.println("finally...");
        }

        //这里的语句不能执行,因为这个代码是无法执行得到
        //System.out.println("hello world");
    }
}
/**
 * 退出JVM之后finally语句就不执行了。
 * 运行结果:
 * try....
 */
public class ExceptionTest12 {
    public static void main(String[] args) {
        try {
            System.out.println("try....");
            //退出JVM finally里的代码就不能执行了
            System.exit(0);
        }finally {
            System.out.println("finally...");
        }
    }
}
finally面试题
/**
 * finally面试题
 */
public class ExceptionTest13 {
    public static void main(String[] args) {
        int ret = m();
        System.out.println("ret = "+ret);
    }

    public static int m() {
        int i=100;
        try {
            return i;
        }
        finally {
            i++;
        }
    }
}

在这里插入图片描述

final、finally、finalize的区别?
/**
 * final、finally、finalize的区别?
 * final:
 *      final修饰的类无法被继承。
 *      final修饰的方法不能被覆盖/重写。
 *      final修饰的变量不能被修改。
 * finally:
 *      finally和try联合使用。
 *      finally语句块中的代码是一定执行的。
 * finalize:
 *      是一个Object类中的方法。
 *      这个方法是由垃圾回收器GC负责调用的。
 */
public class ExceptionTest14 {
    public static void main(String[] args) {
        //final三个关键字。表示最终的。不变的。
        final int i = 100;
        //i = 200;

        //finally也是一个关键字,和try联合使用,使用在异常处理机制中。
        //在finally语句块中的代码是一定会执行的。
        try{

        }finally {
            System.out.println("finally...");
        }

        //finalize()是Object类中的一个方法。作为方法名出现
        //所以finalize是标识符。
        //finalize()方法是JVM的GC垃圾回收器负责调用。
        Object obj;

    }
}
如何自定义异常?

MyException类:

/**
 * 1、SUN提供的JDK内置的异常肯定是不够用的。在实际开发中,有很多业务,
 * 这些业务出现异常之后,JDK是没有的。和业务挂钩的。因此我们要会自定义异常。
 * 2、Java中怎么自定义异常呢?
 * 两步:
 *      第一步:编写一个异常类继承Exception或者RuntimeException
 *      第二步:提供两个构造方法,一个无参数的,一个有参数的。
 */
public class MyException extends Exception{//编译时异常

    public MyException() {//无参
    }

    public MyException(String message) {//有参
        super(message);
    }
}

/*
public class MyException extends RuntimeException{//运行时异常
}
*/

测试类:

public class ExceptionTest15 {
    public static void main(String[] args) {

        //创建异常对象(并没有抛出)
        MyException me = new MyException("用户名不能为空!!!");

        //打印异常堆栈信息
        me.printStackTrace();

        //获取异常简单描述信息
        String msg = me.getMessage();
        System.out.println(msg);
    }
}
自定义异常例子:(以栈的操作为例)重点!!!

自定义 栈操作异常类:

public class MyStackException extends Exception{
    public MyStackException() {
    }

    public MyStackException(String message) {
        super(message);
    }
}

栈类:

public class Stack {
    private int[] array = new int[5];
    private int length;

    public Stack() {
    }

    public Stack(int[] array, int length) {
        this.array = array;
        this.length = length;
    }

    public void push(int x) throws MyStackException {//入栈
        if (length==5){
            //System.out.println("栈满!!!");
            //return;
            throw new MyStackException("栈满");
        }
        array[length++]=x;
    }


    public int pop() throws MyStackException {
        if(length==0){
            //System.out.println("栈空!!!");
            //return;
            throw new MyStackException("栈空");
        }
        return array[--length];//出栈
    }
    public void printArray(){
        for (int i=0;i<length;i++){
            System.out.println(array[i]);
        }
    }

    public int[] getArray() {
        return array;
    }

    public void setArray(int[] array) {
        this.array = array;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    @Override
    public String toString() {
        return "Stack{" +
                "array=" + Arrays.toString(array) +
                ", length=" + length +
                '}';
    }
}

测试类:

/**
 * 测试改良后的Stack
 * 注意:最后这个例子,是异常最重要的案例,必须掌握!!!自定义异常在开发中的应用。
 */
public class ExceptionTest16 {
    public static void main(String[] args) {
        Stack stack = new Stack();
        try {
            stack.push(1);
            stack.push(2);
            stack.push(3);
            stack.push(4);
            stack.push(5);
            stack.printArray();
            //stack.push(6);//栈满
            System.out.println("========================================");
            stack.pop();
            stack.pop();
            stack.pop();
            stack.pop();
            stack.printArray();
            stack.pop();
            stack.pop();//栈空

        } catch (MyStackException e) {
            e.printStackTrace();
        }
    }
}
/*
运行结果:
1
2
3
4
5
========================================
1
com.chinasoft.javase.exception.MyStackException: 栈空
	at com.chinasoft.javase.exception.Stack.pop(Stack.java:29)
	at com.chinasoft.javase.exception.ExceptionTest16.main(ExceptionTest16.java:21)
*/

异常练习:
在这里插入图片描述

自定义异常类:

public class MyRegisterException extends Exception{
    public MyRegisterException() {
    }

    public MyRegisterException(String message) {
        super(message);
    }
}

UserService类:

public class UserService {
    private String userName;

    private String password;

    public UserService() {
    }

    public UserService(String userName, String password) {
        this.userName = userName;
        this.password = password;
    }

    public void register(String userName,String password) throws MyRegisterException {//注册
        if (userName==null||userName.length()<6||userName.length()>14){
            throw new MyRegisterException("用户名要求在[6,14]之间!!!");
        }
        System.out.println("注册成功!!");
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "UserService{" +
                "userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

测试类:

public class ExceptionTest18 {
    public static void main(String[] args) {
        java.util.Scanner sr = new java.util.Scanner(System.in);

        System.out.println("请输入用户名:");
        String userName = sr.next();
        System.out.println("请输入密码:");
        String password = sr.next();

        UserService us = new UserService(userName,password);
        try {
            us.register(userName,password );
        } catch (MyRegisterException e) {
            e.printStackTrace();
        }

    }
}
//运行结果1
/*
请输入用户名:
wl
请输入密码:
123
com.chinasoft.javase.exception.MyRegisterException: 用户名要求在[6,14]之间!!!
	at com.chinasoft.javase.exception.UserService.register(UserService.java:18)
	at com.chinasoft.javase.exception.ExceptionTest18.main(ExceptionTest18.java:14)
*/

//运行结果2
/*
请输入用户名:
wanglei
请输入密码:
123
注册成功!!

*/
武器数组练习

在这里插入图片描述

武器父类:

public class Weapon {//武器父类
    private String weaponName;

    public Weapon() {
    }

    public Weapon(String weaponName) {
        this.weaponName = weaponName;
    }

    public String getWeaponName() {
        return weaponName;
    }

    public void setWeaponName(String weaponName) {
        this.weaponName = weaponName;
    }

    @Override
    public String toString() {
        return "Weapon{" +
                "weaponName='" + weaponName + '\'' +
                '}';
    }
}

可移动的(接口):

public interface Moveable {//可以动的
    void move();
}

可攻击的(接口):

public interface Attainable {//可攻击的
    void attack();
}

异常类:

public class WeaponNumException extends Exception{//武器数量异常
    public WeaponNumException() {
    }

    public WeaponNumException(String message) {
        super(message);
    }
}

武器类:

public class Aircraft extends Weapon implements Moveable,Attainable{//飞机
    @Override
    public void attack() {
        System.out.println("飞机正在攻击!");
    }

    @Override
    public void move() {
        System.out.println("飞机正在移动。");
    }
}
public class Tank extends Weapon implements Moveable,Attainable{//坦克
    @Override
    public void attack() {
        System.out.println("坦克正在攻击!");
    }

    @Override
    public void move() {
        System.out.println("坦克正在移动。");
    }
}
public class Missiles extends Weapon implements Moveable,Attainable{//导弹
    @Override
    public void attack() {
        System.out.println("导弹正在攻击!!!");
    }

    @Override
    public void move() {
        System.out.println("导弹正在移动。");
    }
}

军队类:
注意:类在转换成某个接口的时候不需要有继承关系!!

public class Army {//军队
    private Weapon[] weapons;

    private int length;//已有武器数量

    private int maxSize;//数组最大容量

    public Army() {
    }

    public Army(int maxSize) {//初始化 创建maxSize大小的weapons数组
        this.weapons = new Weapon[maxSize];
        this.maxSize=maxSize;
    }

    public void addWeapon(Weapon weapon) throws WeaponNumException {//添加武器
        if (length==maxSize){//此时已满
            throw new WeaponNumException("军队武器已满!!!");
        }
        if (weapon!=null){
            weapons[length++]=weapon;
        }
    }

    public void moveAll(){//所有武器 移动
        for (int i=0;i<length;i++){//直接强转为接口类型,利用多态调用方法
            if (weapons[i] instanceof Moveable){
                Moveable m = (Moveable)weapons[i];
                m.move();
            }
        }
    }

    public void attackAll(){//所有武器 攻击
        for (int i=0;i<length;i++){//直接强转为接口类型,利用多态调用方法
            if (weapons[i] instanceof Attainable){
                Attainable a = (Attainable)weapons[i];
                a.attack();
            }
        }
    }

    public Weapon[] getWeapons() {
        return weapons;
    }

    public void setWeapons(Weapon[] weapons) {
        this.weapons = weapons;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public int getMaxSize() {
        return maxSize;
    }

    public void setMaxSize(int maxSize) {
        this.maxSize = maxSize;
    }

    @Override
    public String toString() {
        return "Army{" +
                "weapons=" + Arrays.toString(weapons) +
                ", length=" + length +
                ", maxSize=" + maxSize +
                '}';
    }
}

测试类:

public class ArrayTest01 {
    public static void main(String[] args) {
        java.util.Scanner sr =new java.util.Scanner(System.in);
        System.out.println("请输入军队需要存放的武器数量:");
        int size = sr.nextInt();
        Army army = new Army(size);//创建size个武器的军队

        army.addWeapon(new Tank());
        army.addWeapon(new Missiles());
        army.addWeapon(new Aircraft());
        army.addWeapon(new Aircraft());

        army.moveAll();
        System.out.println("======================");
        army.attackAll();
        
    }
}
/*
运行结果1:
请输入军队需要存放的武器数量:
5
坦克正在移动。
导弹正在移动。
飞机正在移动。
飞机正在移动。
======================
坦克正在攻击!
导弹正在攻击!!!
飞机正在攻击!
飞机正在攻击!

运行结果2:
请输入军队需要存放的武器数量:
3
com.chinasoft.javase.arrayCase.WeaponNumException: 军队武器已满!!!
	at com.chinasoft.javase.arrayCase.Army.addWeapon(Army.java:22)
	at com.chinasoft.javase.arrayCase.ArrayTest01.main(ArrayTest01.java:14)
坦克正在移动。
导弹正在移动。
飞机正在移动。
======================
坦克正在攻击!
导弹正在攻击!!!
飞机正在攻击!

*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值