Java OOP 6 异常

Java OOP 6 异常

一、学习目标
  1. 熟练使用try-catch-finally进行异常处理
  2. 理解Java异常处理机制的执行过程
  3. 会使用throws声明异常
  4. 会使用throw抛出异常
  5. 了解异常的分类
  6. 会使用log4j2记录日志
二、生活中的异常

在这里插入图片描述

三、程序中的异常

丰收时节,星沐生态农场将采摘的水果平均分给每家果商,编程实现计算每家果商可以分到的蔬果重量

/**
 * 计算果商供应量
 */
import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("请输入果实采摘量(公斤):");
        int weight = input.nextInt();
        System.out.print("请输入果商数(家):");
        int num = input.nextInt();
        System.out.println("每家果商供应" + weight / num + "公斤水果。");
        System.out.println("欢迎再来,预祝生意兴隆!");
    }
}

接受的果商数值直接影响代码的运行结果

  • 正整数:程序正常运行

  • 零:程序出现异常

在这里插入图片描述

  • 非数字:程序出现异常

    在这里插入图片描述

因为疏漏造成程序运行出现异常,应该怎么解决?是否可以让程序自动判断异常和处理异常呢?

代码改造如下,尝试通过if-else语句解决

package com.aiden.exceptions.demo02;

import java.util.Scanner;

/**
 * 计算每家果商供应量(使用if-else处理异常)
 */
public class Test2 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("请输入果实采摘量(公斤):");
        int weight = 0;
        if (input.hasNextInt()) {
            weight = input.nextInt();
        } else {
            System.err.println("果实采摘量必须是整数!");
            System.exit(1);
        }
        System.out.print("请输入果商数(家):");
        int num = 0;
        if (input.hasNextInt()) {
            num = input.nextInt();
            if (num <= 0) {
                System.err.println("果商数必须大于零!");
                System.exit(1);
            }
        } else {
            System.err.println("果商数必须输入整数!");
            System.exit(1);
        }
        System.out.println("每家果商供应" + weight / num + "公斤水果。");
        System.out.println("欢迎再来,预祝生意兴隆!");
    }
}

使用if else的缺陷

  1. 无法穷举所有的异常情况
  2. 影响程序可读性,维护难度高

解决方案:使用异常处理机制

四、什么是异常

异常是指在程序的运行过程中所发生的不正常的事件,它会中断正在运行的程序

在这里插入图片描述

生活中遇到异常时的处理方案

生活中,根据不同的异常进行相应的处理,而不会就此中断我们的生活

在这里插入图片描述

五、程序中的异常处理

Java编程语言使用异常处理机制为程序提供了错误处理的能力

在这里插入图片描述

  1. Java中,所有的异常都定义为类
  2. 除了内置的异常类之外,Java也可以自定义异常类
  3. Java的异常处理机制也允许自行抛出异常
六、异常处理

Java的异常处理是通过5个关键字实现的

① try、② catch、 ③finally、④throw、⑤ throws

在这里插入图片描述

七、常用的异常处理结构

关键字try、catch、 finally 组成常用的异常处理结构

try {
    //有可能出现异常的语句
}[catch(异常类型 异常对象) {
    //异常处理语句
}] [finally {
   //一定会运行到的语句
}]

在这里插入图片描述

八、try-catch块

使用try-catch块捕获异常,分为三种情况

第一种情况(正常)

public static void main(String[] args) {
	try {
      // 代码段(此处不会产生异常)
	} catch (异常类型 ex) {
      // 对异常进行处理的代码段
	}
	// 代码段
}

在这里插入图片描述

第二种情况:出现异常

public static void main(String[] args) {
	try {
      // 代码段 1
      // 产生异常的代码段 2
      // 代码段 3
	} catch (异常类型 ex) {
      // 对异常进行处理的代码段4
	}
	// 代码段5
}

在这里插入图片描述

示例:使用try-catch语句处理接受果商数为0的异常

package com.aiden.exceptions.demo03;

import java.util.Scanner;

/**
 * 使用try-catch语句处理异常
 */
public class Test3 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.print("请输入果实采摘量(公斤):");
            int weight = input.nextInt();
            System.out.print("请输入果商数(家):");
            int num = input.nextInt();
            System.out.println("每家果商供应" + weight / num + "公斤水果。");
            System.out.println("欢迎再来,预祝生意兴隆!");
        } catch (Exception ex) {//捕捉异常,提示出错信息
            System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
        }
    }
}

运行结果:
在这里插入图片描述

在catch块中处理异常

加入用户自定义处理信息

System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零! ");

调用方法输出异常信息

e.printStackTrace();

异常对象常用的方法

方法名说 明
void printStackTrace()输出异常的堆栈信息
String getMessage()返回异常信息描述字符串, 是printStackTrace()输出信息的一部分

printStackTrace()的堆栈跟踪功能显示出程序运行到当前类的执行流程

 try {
     //省略代码
 } catch (Exception ex) {
     System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
     ex.printStackTrace();  //输出完整异常信息
 }

在这里插入图片描述

第三种情况:异常类型不匹配

public static void main(String[] args) {
	try {
    	// 代码段 1
    	// 产生异常的代码段 2
    	// 代码段 3
	} catch (异常类型 ex) {
    	// 对异常进行处理的代码段4
	}
	// 代码段5
}

在这里插入图片描述

九、try-catch-finally

在try-catch块后加入finally块
是否发生异常都执行

在这里插入图片描述

示例:使用try-catch-finally语句处理果商数为0的异常

package com.aiden.exceptions.demo05;
/**
 * 使用try-catch-finally语句处理异常(输出完整异常信息)
 */

import java.util.Scanner;

public class Test5 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.print("请输入果实采摘量(公斤):");
            int weight = input.nextInt();
            System.out.print("请输入果商数(家):");
            int num = input.nextInt();
            System.out.println("每家果商供应" + weight / num + "公斤水果。");
        } catch (Exception ex) {
            System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
            ex.printStackTrace();
        } finally {//无论是否出现异常,都会执行finally块中代码
            System.out.println("欢迎再来,预祝生意兴隆!");
        }
    }
}

运行结果:

在这里插入图片描述

在try-catch块后加入finally块,当存在 return的try-catch-finally块

在这里插入图片描述

示例代码: try 和 finally语句块中存在return语句

package com.aiden.exceptions.demo06;

import java.util.Scanner;

/**
 * 使用try-catch-finally语句处理异常(异常处理中使用return)
 */
public class Test6 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.print("请输入果实采摘量(公斤):");
            int weight = input.nextInt();
            System.out.print("请输入果商数(家):");
            int num = input.nextInt();
            System.out.println("每家果商供应" + weight / num + "公斤水果。");
            return;//不影响finally块执行
        } catch (Exception ex) {
            System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
            ex.printStackTrace();
            return;//不影响finally块执行
        } finally {
            System.out.println("欢迎再来,预祝生意兴隆!");
        }
    }
}

运行结果

在这里插入图片描述

finally唯一不执行的情况

在这里插入图片描述

示例代码

package com.aiden.exceptions.demo07;

/**
 * 使用try-catch-finally语句处理异常(finally不被执行的情况)
 */

import java.util.Scanner;

public class Test7 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.print("请输入果实采摘量(公斤):");
            int weight = input.nextInt();
            System.out.print("请输入果商数(家):");
            int num = input.nextInt();
            System.out.println("每家果商供应" + weight / num + "公斤水果。");
        } catch (Exception ex) {
            System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
            ex.printStackTrace();
            System.exit(1);  //finally语句块不被执行的唯一条件
        } finally {
            System.out.println("欢迎再来,预祝生意兴隆!");
        }
    }
}

运行结果

在这里插入图片描述

十、多重catch块

程序中可能存在多种异常类型不同的异常类型有不同的处理方式

在这里插入图片描述

引发多种类型的异常

  1. 排列catch 语句的顺序:先子类后父类
  2. 发生异常时按顺序逐个匹配
  3. 只执行第一个与异常类型匹配的catch语句

在这里插入图片描述

示例代码

package com.aiden.exceptions.demo08;

/**
 * 使多重catch语句
 */

import java.util.InputMismatchException;
import java.util.Scanner;

public class Test8 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.print("请输入果实采摘量(公斤):");
            int weight = input.nextInt();
            System.out.print("请输入果商数(家):");
            int num = input.nextInt();
            System.out.println("每家果商供应" + weight / num + "公斤水果。");
        } catch (ArithmeticException ex) {
            System.err.println("出现错误:果商数应大于零!");
            ex.printStackTrace();
        } catch (InputMismatchException ex) {
            System.err.println("出现错误:果实采摘量和果商数应为整数!");
            ex.printStackTrace();
        } catch (Exception ex) {
            System.err.println("其他未知错误!");
            ex.printStackTrace();
        } finally {
            System.out.println("欢迎再来,预祝生意兴隆!");
        }
    }
}
十一、异常的分类

在这里插入图片描述

十二、运行时异常
  • 是可以在程序中避免的异常
  • 当程序进行时发生异常,会输出异常堆栈信息并终止程序运行
  • 可以使用try-catch语句捕获
  • 常见的异常类型
异常类型说明
ArithmeticException当出现算术错误时,抛出此异常 如:一个整数“除以零”时,抛出此异常
ArrayIndexOutOfBoundsException非法索引访问数组时抛出的异常 如索引为负或大于等于数组长度
ClassCastException当试图将对象强制转换为非本对象类型的子类时,抛出该异常
IllegalArgumentException表明向方法传递了一个不合法或不正确的参数
InputMismatchException欲得到的数据类型与实际输入的类型不匹配
NullPointerException当应用程序试图在需要对象的地方使用null时,抛出该异常
NumberFormatException当试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常 如:把“ABC”转成数字
十三、Checked异常
  • 是指运行时异常以外的异常
  • 是用户错误或问题引起的异常
  • 程序员无法预见
  • 编译器会提示
  • 如果不进行捕获,则会出现编译错误
  • 常见的异常类型
    • FileNotFoundException异常
    • SQLException异常
十四、Checked异常处理示例

创建文件流

/**
 * 演示Check异常
*/
public class Test9 {
 public static void main(String[] args) {
     FileInputStream fis = null; //创建一个文件流对象
     File file = new File("d:\\test.txt");
     fis = new FileInputStream(file);//初始化文件流入对象
     fis.close();
 }
}

在这里插入图片描述

处理checked异常

package com.aiden.exceptions.demo09;

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

/**
 * 演示Check异常
*/
public class Test9 {
 public static void main(String[] args) {
     FileInputStream fis = null; //创建一个文件流对象
     File file = new File("d:\\test.txt");
     try {
         fis = new FileInputStream(file);//初始化文件流入对象
     } catch (FileNotFoundException e) {
         e.printStackTrace();
     }finally {
         try {
             fis.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
 }
}

在这里插入图片描述

十五、异常处理的完整流程

在这里插入图片描述

对于异常处理,不同项目的要求并不相同
由项目开发标准决定异常是统一使用Exception类型接收处理还是分开处理
如果开发要求严格,会对每一种异常分别进行处理,详细记录异常信息,产生一定工作量

十六、声明异常

问题

如果在一个方法体中抛出了异常,如何通知调用者?

方案

使用关键字throws声明某个方法可能抛出的各种异常

语法

public void 方法名()  throws 异常类型[,异常类型] {
   //方法体
}

经验

声明异常,多个异常用逗号隔开

示例: 使用关键字throw声明异常,并处理异常

package com.aiden.exceptions.demo10;

/**
 * throws声明异常
 */
import java.util.InputMismatchException;
import java.util.Scanner;

public class Test10 {
    
    //实际开发中,main()方法不建议声明异常,因为如果程序出现了错误,会导致程序中断执行。
    //main()方法声明的异常,由Java虚拟机处理
    //public static void main(String[] args) throws Exception{
    //    calculation();
    //}
    
    public static void main(String[] args) {
        try {
            calculation();
        } catch (Exception ex) {
            System.err.println("出现错误:采摘量和果商数应为整数,果商数应大于零!");
            ex.printStackTrace();
        } finally {
            System.out.println("欢迎再来,预祝生意兴隆!");
        }
    }
    
    /**
     * 计算每家果商供应量
     * @throws Exception
     */
    public static void calculation() throws ArithmeticException, InputMismatchException {
        Scanner input = new Scanner(System.in);
        System.out.print("请输入果实采摘量(公斤):");
        int weight = input.nextInt();
        System.out.print("请输入果商数(家):");
        int num = input.nextInt();
        System.out.println("每家果商供应" + weight / num + "公斤水果。");
    }
}
十七、抛出异常

除了系统自动抛出异常外,有些问题需要程序员自行抛出异常

  • 根据程序逻辑自定义的异常类,在Java异常体系中并未提供,不能抛出
  • 根据业务需要自行选择异常抛出时机,或自定义异常处理逻辑

语法

throw new 异常名([参数列表]; //创建异常类的对象通过throw抛出

示例

throw new Exception();

注意

throw抛出的只能是Throwable类或其子类的对象

抛出异常示例

-某影院3~6岁儿童及60岁以上老人可以买半票25元/张,其他年龄观众需买全价票50元/张,在购票系统中,输入正常范围年龄,可提示正确票价信息;否则,输出错误提示。

package com.aiden.exceptions.demo11;

import java.util.Scanner;

/**
 * throw抛出异常
 */
public class Test11 {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("请录入您的年龄:");
        int age = input.nextInt();
        try {
            Test11 test = new Test11();
            System.out.println(test.ShowTicketPrice(age));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    /**
     *根据年龄显示票价信息
     */
    public String ShowTicketPrice(int age) throws Exception {
        if (age < 3) {
            throw new Exception("您录入的年龄有误!");
        } else if (age >= 60 || age <= 6) {
            return "您可以购买半价票25元!";
        } else {
            return "您需要购买全价票50元!";
        }
    }
}

运行结果

在这里插入图片描述

十八、自定义异常

当Java异常体系中提供的异常类型不能满足程序的需要时,可以自定义异常类
步骤

  1. 定义异常类,继承Exception类或者RuntimeException类
  2. 编写异常类的构造方法,并继承父类的实现
  3. 常见的构造方法
public Exception() { //构造方法1
    super();
}
public Exception(String message) { //构造方法2
    super(message);
}
public Exception(String message, Throwable cause) {  //构造方法3
    super(message, cause);
}
public Exception(Throwable cause) {  //构造方法4
    super(cause);
}

实例化自定义异常对象,并使用throw关键字抛出

自定义异常类AgeException,捕捉年龄在正常范围外的数值

package com.aiden.exceptions.demo12;

/**
 * 年龄异常
 */
public class AgeException extends Exception {
    public AgeException() {
        super();
    }

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

测试

package com.aiden.exceptions.demo12;

import java.util.Scanner;

/**
 * 测试自定义异常
 */
public class DefineExcepTest {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        System.out.print("请录入您的年龄:");
        int age = input.nextInt();
        try {
            ShowTicketPrice(age);
        } catch (AgeException ex) {
            ex.printStackTrace();
        }
    }

    /*
      根据年龄显示票价信息
     */
    public static void ShowTicketPrice(int age) throws AgeException {
        if (age < 3) {
            throw new AgeException("您录入的年龄有误!");
        } else if (age >= 60 || age <= 6) {
            System.out.println("您可以购买半价票25元!");
        } else {
            System.out.println("您需要购买全价票50元!");
        }
    }
}
十九、自定义异常的应用场景

项目中因业务逻辑错误需要抛出异常,但Java中不存在这类异常例如,年龄异常、性别异常等
项目开发一般是由团队成员共同完成,为统一对外异常展示的方式,可以使用自定义异常

二十、日志记录框架

如何实现以文件形式记录异常信息?

  • 使用日志框架

日志(log)

  • 主要用来记录系统运行中一些重要操作信息
  • 便于监视系统运行情况,帮助用户提前发现和避开可能出现的问题,或者出现问题后根据日志找到原因

日志分类

  • SQL日志、异常日志、业务日志

日志的主要用途

  1. 问题追踪
  2. 状态监控
  3. 安全审计

日志框架log4j2

  • 一款非常优秀的日志框架
  • 控制日志的输出级别
  • 控制日志信息输送的目的地是控制台、文件等
  • 控制每一条日志的输出格式

使用log4j2记录日志
编写配置文件

  • 文件后缀可为.xml、.json或者.jsn

定义日志记录器Logger

  • 获取日志记录器的方式

记录日志

  1. Logger类可以替代System.out或者System.err,供开发者记录日志信息
  2. Logger类常用方法

在这里插入图片描述

日志记录器的日志级别

  • all:最低等级,用于打开所有日志记录
  • trace:用于程序追踪输出
  • debug:指出细粒度信息事件,对高度应用程序是非常有帮助的
  • info:在粗粒度级别上指明消息,强调应用程序的运行过程
  • warn:表示警告信息,即可能会出现的潜在错误
  • error:指出虽然发生错误事件,但仍然不影响系统的继续运行
  • fatal:指出将会严重的错误事件将会导致应用程序退出
  • OFF:最高等级的,用于关闭所有日志记录

示例

  1. 控制台和文件中同时记录日志信息
  2. 信息录入以及正常执行的情况记录为debug级别日志
  3. 发生异常的信息记录为error级别日志

格式要求

  1. 控制台输出内容:执行位置、记录日志级别及输出信息
  2. 文本日志记录内容:日期和时间(精确到秒)、执行位置、记录日志级别及输出信息

分析

实现步骤

  • 编写配置文件log4j2.xml

  • 定义日志记录器Logger

  • 记录日志

配置log4j2.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration staus="OFF">
    <appenders>
        <!--输出日志到控制台的配置-->
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%l] %-5level - %msg%n"/>
        </Console>
        <!--输出日志到文件的配置-->
        <File name="log" fileName="log/test.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%l] %-5level - %msg%n"/>
        </File>
    </appenders>
    <loggers>
        <root level="all">
            <appender-ref ref="Console"/>
            <appender-ref ref="log"/>
        </root>
    </loggers>
</configuration>

定义日志记录器Logger,记录日志

package com.aiden.exceptions.demo13;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * 使用Log4j2记录日志
 */
public class Test13 {
    //获取Logger对象
    private static Logger logger = LogManager.getLogger(Test13.class.getName());

    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.print("请输入果实采摘量(公斤):");
            int weight = input.nextInt();
            logger.debug("采摘量:" + weight);
            System.out.print("请输入果商数(家):");
            int num = input.nextInt();
            logger.debug("果商数:" + num);//向日志中写入Debug信息
            System.out.println("每家果商供应" + weight / num + "公斤水果。");
            logger.debug("输出结果:" + String.format("%d/%d=%d", weight, num, weight / num));
        } catch (ArithmeticException ex) {
            logger.error("输入有误,果商数应大于零!", ex);//记录日志
        } catch (InputMismatchException ex) {
            logger.error("输入有误,果实采摘量和果商数应为整数!", ex);
        } catch (Exception ex) {
            logger.error(ex.getMessage());
        } finally {
            System.out.println("欢迎再来,预祝生意兴隆!");
        }
    }
}

运行结果:

在这里插入图片描述

二十一、本章总结

在这里插入图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

众生云海,一念初见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值