OOP18-Java异常与异常处理

异常和处理异常

正常的程序运行状态符合程序设计人员和用户的希望.
程序执行的状态进入到不希望的状态, 称为异常状态. 程序运行状态异常的发现, 报告和处理在Java中采用try-catch-finally的结构. 通过创建并抛出代表异常的对象, 发现并报告异常; 通过catch捕捉发生的异常.

现象

正常的程序

表示账户的类Account.java文件内容如下:

public class Account {

    private final String accountID;
    private int balance;

    public void deposit(int amount) {
        this.balance += amount;
    }

    public void withdraw(int amount) {
        this.balance -= amount;
    }

    public Account(String accountID, int balance) {
        this.accountID = accountID;
        this.balance = balance;
    }

    public int getBalance() {
        return balance;
    }

    public String getAccountID() {
        return accountID;
    }
}

对一个账户进行大量的存款. 存款20次, 每次100_000_000. 结果正常.

public class BalanceNormal {
    public static void main(String[] args) {
        Account account1 = new Account("620001", 10);
        for (int j = 0; j < 20; j++) {
            account1.deposit(100_000_000);
        }
        System.out.println("account1.getBalance()=" + account1.getBalance());
    }
}

运行结果:

account1.getBalance()=2000000010

不正常的程序结果

对一个账户进行大量的存款. 存款22次, 每次100_000_000. 结果不正常.

public class BalanceAbnormal {
    public static void main(String[] args) {
        Account account1 = new Account("620001", 10);
        for (int j = 0; j < 22; j++) {
            account1.deposit(100_000_000);
        }
        System.out.println("account1.getBalance()=" + account1.getBalance());
    }
}

运行结果:

account1.getBalance()=-2094967286

问题分析

初始的账户余额为10, 存款22次, 每次100_000_000, 应当得到是2200000010, 而不是-2094967286. 由于balance定义为int类型的变量, 2200000010超出32位有符号整数能够表达的范围, 因此溢出到符号位, 使整体变成了复数.

直接问题

  • 如何发现溢出事件?
  • 发生溢出时应当如何处理?
    – 减少/消除溢出造成的影响.
    – 程序恢复到正常状态.

根源问题

  1. 如何发现程序运行的状态不正常?
  2. 如何进行处理不正常的结果(状态), 以免问题继续扩大?
    • 减小/消除不正常状态造成的影响
    • 程序恢复到正常状态
  3. 如果当前部分的程序不能处理发生的不正常状态, 如何报告发生了不正常的结果?

方案

返回值

每个动作执行都检查执行的结果, 每次方法调用后都检查返回值. 用返回值代表是否发生了异常, 以及发生了什么异常.
按照这个思路修订的deposit方法.

    /**
     * deposit 存款
     * @param amount 存款数量, 单位人民币元
     * @return true表示存款正常; false表示存款不正常, 没有执行存款操作
     */
    public boolean deposit(int amount) {
        if(this.balance + amount < this.balance)
            return false;
        this.balance += amount;
        return true; 
    }

每执行一条语句都检查程序的状态是否正常.
用if判断程序执行的状态的发生, 如果不正常, 则退出循环.

public class BalancAbnormal {

    public static void main(String[] args) {
        Account account1 = new Account("620001", 10);
        for (int j = 0; j < 22; j++) {
            if(account1.deposit(100_000_000)!=true)
                break;
        }
        System.out.println("account1.getBalance()=" + account1.getBalance());
    }
}

运行结果:

account1.getBalance()=2100000010

用方法的返回值表达方法执行是否成功, 在上面的例子中看起来是一个很好的解决方案. C语言的函数就是这么处理的.
考虑另一个功能, 从文件中读取一个字节. 方法的返回值必须是一个字节, 没有办法携带读取文件中的一个字节是否成功的标志. 用byte作为返回值, 没办法表达读取成功与否; 用更大范围的数据类型如int作为返回值, 不符合读取一个字节的语义.
这是需要一种新的表达操作成功与否的机制, 这种机制称为异常处理.

异常

不占用返回值, 表达程序运行的不正常状态.
正常执行的程序和异常处理程序处于不同的程序块.

Java的Exception类作为各种异常情况的超类.

定义异常类BalanceException

public class BalanceException extends Exception {
    String message = "balance should be positive.";
    @Override
    public String getMessage() {
        return message + super.getMessage();
    }
}

在deposit方法中, 如果balance变量的状态不正常, 则创建并抛出异常BalanceException 对象.

    /**
     * deposit 存款
     * @param amount 存款数量, 单位人民币元
     * @throws BalanceException
     */
    public void deposit(int amount) throws BalanceException {
        if(this.balance + amount < this.balance)
            throw new BalanceException();
        this.balance += amount;
    }

声明main()函数中可能抛出BalanceException类型的异常对象

public class BalancAbnormal {

    public static void main(String[] args) throws BalanceException {
        Account account1 = new Account("620001", 10);
        for (int j = 0; j < 22; j++) {
            account1.deposit(100_000_000);
        }
        System.out.println("account1.getBalance()=" + account1.getBalance());
    }
}

运行结果:

Exception in thread "main" oop08.BalanceException: balance should be positive.null
at oop08.Account.deposit(Account.java:24)
at oop08.BalancAbnormal.main(BalancAbnormal.java:13)

从安静的继续错下去, 到报错了. 抛出异常对象, 打断程序的正常执行. 不会让不正常的状态继续执行. 语句

System.out.println("account1.getBalance()=" + account1.getBalance());

没有执行.

异常处理

增加catch语句块处理异常

public class BalancAbnormal {

    public static void main(String[] args)  {
        Account account1 = new Account("620001", 10);
        for (int j = 0; j < 22; j++) {
            try {
                account1.deposit(100_000_000);
            } catch (BalanceException ex) {
                System.out.println(ex.getMessage());
                System.out.println("第"+j+"次试图存入"+100_000_000+"存款失败");
            }
        }
        System.out.println("account1.getBalance()=" + account1.getBalance());
    }
}

运行结果:

balance should be positive.
第21次试图存入100000000存款失败
account1.getBalance()=2100000010

资源使用

资源的申请, 使用, 释放.
例如文件读取

public class FileTool {
    public static String read(String fileName) {
        //在try语句块外声明变量, 因为在finally语句块中也要用
        FileInputStream fileInput = null;
        StringBuilder content = new StringBuilder();
        try {
            fileInput = new FileInputStream(fileName); //申请资源
           //下面是正常使用资源
            final InputStreamReader inputStreamReader = new InputStreamReader(fileInput);
            while (inputStreamReader.ready()) {
                content.append((char) inputStreamReader.read());
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally { //无论是否抛出异常, 都必须执行finally语句块.
            try {
                fileInput.close(); //释放资源
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
        return content.toString();
    }
}

简化的资源自动释放

try(资源分配语句;){
    资源使用语句;
}//自动释放使用的资源;

例如文件读取, 可以简化成如下的代码

public class FileTool {
    public static String read(String fileName) {
        StringBuilder content = new StringBuilder();
        try (FileInputStream fileInput = new FileInputStream(fileName);
                InputStreamReader inputStreamReader = new InputStreamReader(fileInput);) {
            while (inputStreamReader.ready()) {
                content.append((char) inputStreamReader.read());
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return content.toString();
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值