Java中异常机制

异常机制

          • 概述
          • throws
          • catch语句块
          • 异常改进
          • finally
          • 注意
          • 自定义异常类

概述

/*
* 如果程序报错,会终止程序生命周期执行(错误代码后代码不再执行)
* 1 异常机制
*   异常时错误的一种说法
*   在Java中有一个模拟所有异常和错误的类(Throwable),所有异常都必须继承这个类
*   目的:增强程序的鲁棒性、健壮性
*
* 异常对于程序员在编程时,是对于某些高风险的操作规定了一个提醒机制,对于系统来说
*   就是处理异常的触发机制
*
* 2 异常的处理形式
*   try...catch... : 解决异常,一般用在客户端
*   throws : 抛出异常,一般用在类库端(服务端)
*   throw : 制造异常,异常源点,创建一个异常对象
*       throw new 异常类(错误信息);
*
* if..else.. 进行判断,可以解决大多数预知错误,对于无法预料情况,使用try...catch...解决
*
* 3 不同异常机制的选择
*   有些异常不想处理,或无法处理,或不知道如何处理,一般使用throws把异常抛出给调用处
*   知道怎么处理,直接使用try...catch...处理异常,一般在main方法中
*
* 4 finally语句块
*   必须执行的语句块
*
* 5 异常机制继承体系
*   最大异常类:Throwable
*       直接两个子类:Error(系统内部错误,一般无法解决,比如栈溢出)和
*           		Exception(所有子类除了RunTimeException,其它全是编译时异常)
*   常见运行时异常: 空指针、下标越界、类型转换、栈内存溢出
*
* 6 语法:
*   try{
*       高风险代码
*   }catch(异常类 变量){
*       处理措施
*   }
* */

public class _01_ExceptionB {

    public static void main(String[] args) {
        int a = 10;
        int b = 0;

        // java.lang.ArithmeticException: / by zero 除0异常 "divide by zero"
        if (b == 0){
            System.out.println("除数不能为0");
        }else {
            int c = a/b;
            System.out.println(c);
        }
        System.out.println("执行成功");
    }
}

/*
* try...catch...使用
* */
public class _02_ExceptionM1 {

    public static void main(String[] args) {
        test1();
        test2();
    }

    public static void test1(){
        // 简单异常。if也能解决
        int a = 10;
        int b = 0;
        // 如果try中出现异常,异常语句后剩余部分不再执行,执行catch
        try {
            int c = a/b;
            System.out.println(c);
        }catch (Exception e){ // 捕捉异常,需要使用异常对应的异常或其父类(多态)
            // 如果try中代码没有异常,则正常执行try,不会执行catch
            // 打印追踪栈帧
            e.printStackTrace(); // 编译能通过,属于运行时异常
            System.out.println("除数不能为0");
        }
        System.out.println("-------");
    }

    public static void test2(){
        // 检查型异常。if无法处理
        try {
            FileInputStream fileInputStream = new FileInputStream("xxxxxx");
        }catch (FileNotFoundException e){
            // 适合返回给编程人员
            e.printStackTrace();

            // 获取异常信息,适合返回给客户
            String message = e.getMessage();
            // xxxxxx (系统找不到指定的文件。)
            System.out.println(message);

            // 获取栈内存地址,不常用
            // [Ljava.lang.StackTraceElement;@74a14482
            System.out.println(e.getStackTrace());
        }
    }
}

throws

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/*
* throws:不处理异常,而是一种提醒,告诉调用处,这里有一个异常/可能有异常
*   异常而没有解决,需要调用处自行处理(抛出异常)
* 如果调用处收到提醒,要么这里也要提醒调用这里的地方,或者try处理掉
*
* throws 也可以同时抛出多个异常,使用逗号隔开
*   throws只是提醒,无所谓继承与顺序关系
* */
public class _02_ExceptionM2 {

    public static void main(String[] args) {
        try {
            test1();
            // 可使用FileNotFoundException进行异常捕捉,也可以使用FileNotFoundException的父类
            // IOException、Exception
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

    // 抛出多个异常
    public static void test1() throws FileNotFoundException,
            Exception,IOException,FileNotFoundException{
        test2();
    }
    public static void test2() throws FileNotFoundException{
        test3();
    }
    public static void test3() throws FileNotFoundException {
        FileInputStream fileInputStream = new FileInputStream("F:/123.txt");
    }
}

catch语句块

/*
* try{
*       高风险代码
*   }catch(异常类 变量){
*       处理措施
*   }catch(异常类 变量){
*       处理措施
*   }
* 1 catch语句块可以根据代码返回的异常编写多个
* 2 从上往下,必须是子类到父类,否则根据多态特性,父类会把子类异常对象捕获
* 3 如果从上往下没有继承关系,则不用考虑顺序问题
* 4 多个异常,只会有一个执行,在产生异常代码语句只后,try中往后代码不会执行
*
* 如果 多个异常中,处理措施一样,可以直接写Exception,否则分开捕获
* */

public class _02_ExceptionM3 {

    public static void main(String[] args) {
        try {
            // 可能存在FileNotFoundException
            new FileInputStream("xxx");

            // 可能空指针异常
            String s = null;
            s.equals("");
        }catch (FileNotFoundException e){
            System.out.println("找不到指定文件");
        }catch (NullPointerException e1){
            System.out.println("空指针");
        }catch (Exception e3){
            // 不能把这个异常放第一个catch块,Exception可以捕获大部分异常,包括空指针和FileNotFoundException等
            // 若其在第一个catch,可能导致另外两个异常永远捕获不到(多态),父类把异常都捕获了
            System.out.println("其他异常");
        }
    }
}

异常改进

/*
* java1.7开始,异常改进
*   1 可以一次捕获多个异常
*       但异常直接不能有继承关系,若存在继承关系,直接写父类
*       多个异常使用 | 隔开
*           异常类型 | 异常类型
*   2 自动关闭资源,多条开启资源语句以分号分割
*       try(开启资源语句){
*           高风险代码;
*       }catch(异常类 变量){
*           处理措施;
*       }
* */

public class _02_ExceptionM4 {

    public static void main(String[] args) {
        test_1();
        test_2();
    }

    public static void test_1(){
        try {
            // 可能存在FileNotFoundException
            new FileInputStream("xxx");

            // 可能空指针异常
            String s = null;
            s.equals("");
            // 不能有继承关系,否 写父类异常
        }catch (FileNotFoundException | NullPointerException e){
            System.out.println("找不到文件");
        }
    }


    public static void test_2(){
        // 保存打开的资源对象,测试是否关闭
        FileInputStream fisc = null;
        // 可自动关闭资源
        try (
                FileInputStream fis1 = new FileInputStream("D:/111.txt");
                FileInputStream fis2 = new FileInputStream("D:/111.txt");
                ){
            // 操作,读取文件中数据
            System.out.println(fis1.read());
            System.out.println(fis2.read());
            // fis2对象赋值非测试对象fisc
            fisc = fis2;
        } catch (FileNotFoundException e) {
            System.out.println("找不到");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 测试
        try {
            // java.io.IOException: Stream Closed 文件流关闭
            System.out.println(fisc.read());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

finally

/*
* finally: 必须执行的语句块
*   在编程中,会有出错情况,但某些情况下,尽管出错,一些代码任然需要执行
*       比如资源关闭语句
* 1 finally不能单独使用
* 2 finally可以直接和 try 一起使用:try...finally...
* 3 finally可以直接和 try...catch... 一起使用:try...catch...finally...
* 4 finally不执行情况
*   System.exit():关闭虚拟机
* 5 finally 中return语句情况
*
* 应用场景
*   由于finally必须执行,所以一般用于关闭资源
*
* 面试题:谈谈对final的理解
*   1 final是什么,怎么用?
*   2 finally是什么,怎么用?
*   3 finalize是什么,怎么用?
* */

public class _02_ExceptionM5 {

    int i;

    public static void main(String[] args) {

        _02_ExceptionM5 exceptionM5 = new _02_ExceptionM5();
        System.out.println(exceptionM5.i); // 成员变量有默认初始值

        testFinally_01();
        System.out.println("--------分割线");
        int i = testFinally_02();
        System.out.println("返回值:"+i);
        System.out.println("--------分割线");
        testFinally_03();
    }
    // finally基本情况
    public static void testFinally_01(){
        int a = 10;
        int b = 0;
        try {
            int c = a/b; // 终止生命周期
            System.out.println(c);
        }catch (Exception e){
            e.printStackTrace(); // 编译能通过,属于运行时异常
            System.out.println("除数不能为0");
        }finally {
            // 依然执行
            System.out.println("必须执行");
        }
        // 如果没有catch,没办法捕获并处理异常,依旧导致终止程序生命周期,故不会执行下面输出语句
        // 如果catch处理了异常,下面输出语句正常执行
        System.out.println("main方法执行");
    }

    // finally中return语句情况
    public static int testFinally_02(){
        int i = 10;
        try {
            // 这里return语句也会执行,后面finally语句块优先级高,把它覆盖了
            // 若返回数据,需要先执行运算操作(i++)。再返回
            // 当运算操作执行完,要执行return语句,发现finally优先级更高,放弃return,执行finally
            return i++;
        }finally {
            System.out.println("我先执行");
            // 当执行到这里,try里面i++已经执行,i=11,return i=11
            return i;
        }
    }

    // 应用场景
    public static void testFinally_03(){
        // 局部变量没有默认初始值,必须赋值
        FileInputStream fileInputStream = null;
        int i; // 必须赋值
        // 输出报错
        // System.out.println(i);

        try {
            fileInputStream = new FileInputStream("F:111.txt");
        }catch (FileNotFoundException e){
            System.out.println("找不到指定文件");
        }finally {
            // fileInputStream.close();可能会抛出异常
            try {
                // 不确保 fileInputStream != null 的话,会出现空指针异常
                // 如果try中文件没有找到,说明fileInputStream没有创建成功,则fileInputStream为空指针
                if (fileInputStream != null){
                    fileInputStream.close(); // 文件流关闭
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
注意

/*
* 子类重写父类方法,不能有更宽泛的异常
*   子类方法中抛出的异常,必须是父类方法抛出的异常类,或者其子类
*   子类覆写后异常 <= 父类方法异常
* */
public class _03_ExceptionA {

    public static void main(String[] args) {
        B b = new B();
        try {
            b.test();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class A{
    public void test() throws IOException{
        System.out.println("A方法");
    }
}

class B extends A{
    @Override
//     public void test() throws Exception {  // 错误,更高异常
//     public void test() throws IOException { // 正确
    public void test() throws FileNotFoundException{ // 正确
        System.out.println("B方法");
    }
}

自定义异常类

/*
* 自定义异常类
* 1 继承一个已有的异常,基本都是继承Exception类,若是运行时异常,则继承RunTimeException
* 2 无参构造
* 3 有参构造,传入错误信息,并把信息传递给父类 super(xxx)
* */
public class UserException extends Exception{

    public UserException() {
    }

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

/*
* 业务类
* */
public class UserService {

    public void login(User user) throws UserException {
        if ("admin".equals(user.getUsername())){
            // 若用户名数admin,则比较密码
            if ("root".equals(user.getPassword())){
                // 若密码正确
                System.out.println("登录成功");
            }else {
                // 密码不正确,抛出异常 并提示 密码错误
                throw new UserException("密码错误");
            }
        }else {
            // 用户名不存在,抛出异常 并提示 用户名不存在
            // throw : 制造异常,异常源点,创建一个异常对象
            throw new UserException("用户名不存在");
        }
    }
}

/*
* 用户类
* */
public class User {

    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    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;
    }
}

/*
* 需求:完成用户登录
*   1 如果用户名不是admin 则提示用户不存在
*   2如果密码不是root,提示密码错误
* 客户端类
* */
public class Client {

    public static void main(String[] args) {
        System.out.println("请输入用户名和密码:");
        Scanner scanner = new Scanner(System.in);

        // 获取用户输入
        String username = scanner.nextLine();
        String password = scanner.nextLine();

        // 封装对象
        User user = new User(username,password);
        // 创建业务类对象
        UserService userService = new UserService();

        // 登录
        try {
            userService.login(user);
            System.out.println("跳转到主页面");
        } catch (UserException e) { // 处理自定义异常
            // com.tianl.Exception.ThrowException.UserException: 密码错误
            // 不必打印追踪栈帧,只需把错误提醒信息 展示给页面即可
            // 因为编程人员知道异常如何发生
            // e.printStackTrace();

            // 获取错误信息
            String msg = e.getMessage();
            // 错误信息传递到页面
            System.out.println(msg);
        }
    }
}

你可能感兴趣的:(JavaSE,java,exception)