七. (《Java核心技术》读书笔记+重点整理系列)异常处理、断言和日志

2023-05-16

目录

  • 异常分类
  • 抛出异常
  • 捕获异常
  • 断言
  • 记录日志
  • 调试技巧
  • PS

异常分类

Throwable
Error
Exception
IOException
Runtime Exception
  • Error:Java运行时系统的内部错误以及资源耗尽的错误。程序中基本不用处理,如果出现了该类错误,告知用户并安全退出即可。

  • RuntimeException:程序编写中的错误,不需要try…catch捕获,修改程序即可避免的错误。也叫uncheckedException,所谓的unchecked是指编译过程中,编译器无法检测出来的错误。比如:数组越界、访问空指针。

  • IOException:类中抛出的、需要try…catch捕获的大多是这类异常。对应的也成为checkedException,编译器会强制检查是否处理的异常。

抛出异常

  • 声明受查异常:在方法签名后,用关键字throws接异常类名。

  • 抛出异常:在方法代码中,用关键字throw接异常对象。

  • 自定义异常类:创建一个继承自Exception或Exception子类的自定义类,规范化的包含两个构造器,一个无参无内容构造器,一个含参数调用super(String)构造器。

public class Main{
    public class myException extends Exception{
        public myException(){}
        public myException(Integer i, String s){
            super("文件名长" + String.valueOf(i) + " 文件名为" + s + "  WEONG!");
        }
    }

    public void myPrint(String filename) throws myException {
        if (filename.length() > 5){
            throw new myException(filename.length(), filename);
        }
        //...
        return;
    }

    public static void main(String[] args){
        Main m = new Main();
        try {
            m.myPrint("abcdef");
        } catch (myException e) {
            System.out.println(e.getMessage());
            //e.printStackTrace();
        }
    }
}

捕获异常

//try...catch
public static void main(String[] args) {
    File f = new File("a.txt");
    try {
        FileInputStream fin = new FileInputStream(f);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}
//IDEA整体缩进快捷键:Shift + Tab

两种执行结果:
try块中没有异常,跳过catch块;
try块中遇到异常,跳过剩余try块语句,执行catch块。

应用程序中,不可避免的会用到文件操作IO流或者是数据库连接,诸如此类开销比较大的资源。这个时候就需要在使用完后,手动进行资源的及时销毁,避免内存泄露。try…catch有三种写法可以完成上述功能。

//try...catch
public static void main(String[] args) {
    File f = new File("a.txt");
    try {
        FileInputStream fin = new FileInputStream(f);
        fin.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e){
        e.printStackTrace();
    }
}

但如果在对象创建之后,遇到了异常跳入某个catch块中,这样close的代码就执行不到。由此引出了finally块,即无论try…catch如何执行,最后一定会执行finally块。

//try...catch...finally
public static void main(String[] args) {
    File f = new File("a.txt");
    FileInputStream fin = null;
    try {
        fin = new FileInputStream(f);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        try {
            fin.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

试想如果变量在创建的时候就报异常,导致变量没有创建仍是空值,就会在finally块中报空值引用错误。而且这种写法,在finally块中遇到异常时,还需要再使用一次try…catch,略显臃肿。

//try-with-resources
public static void main(String[] args) {
    File f = new File("a.txt");
    try(FileInputStream fin =new FileInputStream(f)) {
        ...
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e){
        e.printStackTrace();
    }
}

try-with-resources写法的原理,会在try块退出的时候执行一次资源关闭。实测不会出现空指针情况,而且写法简洁,最为推荐。

  • 异常链:在实际的开发背景中,还会在catch块中再次抛出异常。用来改变异常类型,或者使用自定的详述的异常类型。也可以用Java提供的initCause(e)和getCause(),来构建一个因果的异常链。

  • 堆栈轨迹元素:常用的e.printStackTrace()方法打印方法调用过程,也可以使用e.getStackTrace()返回一个StackTraceElement[]数组,用来逐个分析异常文件名和代码行数。

断言

断言是在开发测试阶段使用,用来确定每一部分的前置条件是否满足,并以此来确定程序错误出现的区间,以便快速定位BUG。相较于异常的优势在于,可以在运行的时候选择是否执行断言,并且不用重新编译。

public static void main(String[] args) {
    File f = null;
    assert f != null:f;
    System.out.println(f.getName());
}
//定位到错误在File对象没有初始化

assert有两种形式:
assert 条件;
assert 条件 : 表达式;

//含有的表达式会在输出中显示值,核心就是报一个AssertionError
Exception in thread "main" java.lang.AssertionError: null
	at Main.main(Main.java:23)
  • 启用断言:在IDEA中修改运行配置,加入-ea的参数即可。我开始还以为在程序参数中加,但是发现程序参数是主函数的args形参。应该调出VM参数,加入-ea。同时可以用-ea:或者-da:启用或抑制某个包中的断言。还有就是在程序中使用ClassLoader类加载器的时候,手动启用或是移除断言。

记录日志

在JDK1.3以前,日志都是直接print到STDOUT流中。后来出现了log4j,以及性能更优的slf4j框架。

  • 基本日志:日志记录器有7个级别,分别为:SEVERE>WARNING>INFO>CONFIG>FINE>FINER>FINEST。
//全局日志记录器
public static void main(String[] args) {
    int a = 1;
    //Logger.getGlobal().setLevel(Level.WARNING);
    //设置最低显示级别
    Logger.getGlobal().info("赋值a为1。");
}
  • 高级日志:也就是使用自定义的日志记录器。还可以有不同类型的日志,比如entering函数步入以及exiting函数退出。
public class Main{
    private static final Logger mylog = Logger.getLogger("com.mylog");
    //用静态变量存储日志记录器,防止没有使用而被垃圾回收器回收。
    public static void main(String[] args) {
        int a = 1;
        mylog.warning("a赋值为1");
    }
}
public class Main{
    private static final Logger mylog = Logger.getLogger("com.mylog");
    public static void main(String[] args) {
        mylog.setLevel(Level.FINER);
        mylog.setUseParentHandlers(false);
        Handler handler = new ConsoleHandler();
        handler.setLevel(Level.FINER);
        mylog.addHandler(handler);
        mylog.entering("com", "main");
        int a = 1;
        mylog.exiting("com", "read", a);
    }
}
//这里涉及到了处理器的内容,之后会详述。
  • 处理器:日志记录器会将记录发送到日志处理器中,由处理器决定如何输出日志。默认情况下,会发送到ConsoleHandler中,输出到System.err中。(关于System.err与System.out相似,为标准错误输出,体现在编译器中就是输出内容为红色)值得注意的是,自定义处理器时,会默认将日志再传一份给命名为“”的父处理器,这也是为什么要设置setUseParentHandlers为false,目的是只有一遍输出即可。记录器有记录级别,同时处理器也有处理级别,所以能显示的日志为两者的最小阈值。类似的,还可以将日志输出到文件或者流数据中,分别对应了FileHandler和StreamHandler。
public class Main{
    private static final Logger mylog = Logger.getLogger("com.mylog");
    public static void main(String[] args) {
        try {
            Handler handler = new FileHandler("src/1.log");
            //默认路径在C盘用户里
            mylog.addHandler(handler);
            mylog.setLevel(Level.WARNING);
            mylog.warning("BUG!!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
文件处理器配置参数(java.util.logging.Handler)作用
append追加文件末尾或是每个程序打开一个文件
limit文件最大允许大小,超过则新建
pattern文件路径以及文件名模式(如%g循环数值)
count文件名循环数(1不循环)
  • 过滤器:每个记录器和处理器都可以有一个过滤器,默认情况下是根据日志的级别过滤。如果想要自定义的话,可以实现Filter接口,重写isLoggable方法,使用setFilter绑定。

  • 格式化器:同样的,自定义时需要实现Formatter接口,用setFormatter绑定。

  • 修改配置文件:文件路径在jdk/conf/logging.properties中,修改相应字段,程序中的修改优先级更高。可以指定自定义部件属性值,也可以直接修改全局值。

//补充配置文件相关,实质上Properties抽象出String类型的键值对
try {
    Properties properties = new Properties();
    FileInputStream fileInputStream = new FileInputStream("1.txt");
    properties.load(fileInputStream);
    //还可以通过store存储
    String name = properties.getProperty("name");
    System.out.println(name);
} catch (Exception e) {
    e.printStackTrace();
}
  • 本地化:使用logmessages资源包,显示信息必须用包中定义的,可以完成不同语言的映射,适用于全球化编程。

本文中的日志均使用的是java.util.logging自带的框架,可以使用功能更为强大的log4j或者是commons-logging。

调试技巧

  • 日志代理:new对象的时候,使用匿名类重写要使用的方法,调用super方法,加入日志信息。

  • -verbose启动VM,可以看到类的加载路径。-Xlint对常见错误的检查。

  • Windows下打开bin下的jconsole监控内存、线程、类加载等情况。

  • jmap工具查看堆中变量值。(使用jps命令查看运行的所有java程序及其虚拟标识符)

PS

  • print中如果出现多个变量的字符串拼接,最好使用printf格式化输出,否则会创建很多的String变量。

  • 打开文件的路径框中输入cmd,可以直接在目标路径下打开命令行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

七. (《Java核心技术》读书笔记+重点整理系列)异常处理、断言和日志 的相关文章

随机推荐

  • HTML 解决css缓存

    span class token operator lt span link rel span class token operator 61 span span class token string 34 stylesheet 34 sp
  • Ubuntu18.04安装Nvidia显卡驱动教程

    0 前期准备 禁用BIOS的secure boot xff0c 即disable它 xff0c 如果不关闭 xff0c 使用第三方源安装显卡驱动会安装后不能使用 1 禁用nouveau 1 创建文件 xff0c 如果没有下载vim编辑器 x
  • VINS之estimator节点小结

    VINS的核心节点 xff0c 包括VIO的初始化过程 紧耦合的非线性化过程 边缘化处理过程 主要流程步骤 1 主函数线程 订阅了四个topic xff0c 分别调用回调函数 xff1b 创建了13个话题发布器 xff1b 开辟了一个VIO
  • 基于布谷鸟搜索算法的函数寻优算法

    文章目录 一 理论基础1 算法原理2 算法流程图 二 Matlab代码三 参考文献 一 理论基础 1 算法原理 布谷鸟采用一种特殊的寄生宿主巢穴的方式孕育繁殖 它将孵育的蛋置入寄生宿主的巢穴 xff0c 让寄生宿主孵化布谷鸟蛋 由于布谷鸟幼
  • 基于逐维反向学习的动态适应布谷鸟算法

    文章目录 一 理论基础1 布谷鸟搜索算法2 DA DOCS算法 xff08 1 xff09 逐维反向学习策略 xff08 2 xff09 动态适应 xff08 3 xff09 DA DOCS算法流程 二 实验与结果分析三 参考文献 一 理论
  • SMPL学习笔记

    文章目录 前言一 SMPL概述1 形状参数 beta 2 姿态参数
  • 多协议BGP-----MPBGP

    MPBGP是在BGP 4 基础上的扩展 xff0c 分为三种 xff1a ipv4 ipv4 ipv6 ipv6 ipv6 ipv4 ipv4 ipv6 本文主要介绍 xff1a ipv6 ipv4 xff08 在 建立ipv6 的BGP邻
  • __asm void MSR_MSP(uint32_t addr) 提示:error:expected '(' after 'asm'

    SYSTEM sys sys c 33 7 error expected 39 39 before 39 void 39 ASM void MSR MSP u32 addr 在STM32中的sys c文件编译报出这个错误时 xff1a AS
  • LTL线性时序逻辑

    https blog csdn net yuniruchujian article details 106213848https www docin com p 506137477 html
  • 强化学习资料

    强化学习资料 莫烦学习资料 莫烦学习资料 https mofanpy com bilibili视频资料 xff1a https www bilibili com video BV13W411Y75P from 61 search amp s
  • apollo学习

    知乎王方浩 https zhuanlan zhihu com p 52521739 csdn https blog csdn net u013914471 type 61 blog bilibili 忠厚老实的老王 https space
  • 求解离散黎卡提矩阵代数方程

    离散代数黎卡提方程求解 1 黎卡提方程 在LQR最优控制中 xff0c 有连续时间最优控制 xff0c 即LQR xff0c 也有离散时间最优控制DLQR xff0c 则在求解中一定会遇到解连续时间黎卡提方程和离散时间黎卡提方程的问题 xf
  • 基于运动学模型的无人机模型预测控制(MPC)-2

    基于无人机自身模型的模型预测控制 无约束情况 1 模型建立 无人机运动学模型 xff1a x
  • 一阶低通滤波器-连续转离散

    一阶低通滤波器 1 一阶连续低通滤波器 y s r
  • 汽车动力学模型

    1 动力学模型 在纵向时 xff0c 可能还会受到纵向空气阻力 xff0c 前轮滚动阻力 xff0c 后轮滚动阻力 xff0c 坡道重力分量等
  • PX4飞控源码及解析

    源码地址 xff1a https github com 987419640 Firmware 解析 xff1a https dev px4 io zh concept architecture html
  • Hadoop:简介和安装

    Hadoop简介 Hadoop项目由多个子项目组成 与其他项目不同 xff0c 这个项目更像一个生态系统 其中 xff0c 核心项目包括HDFS MapReduce框架 YARN和ZooKeeper HDFS是一个符合Hadoop要求的分布
  • centos6.x如何安装docker

    1 curl Lks https yum spaceduck org kernel ml aufs kernel ml aufs repo gt etc yum repos d kernel ml aufs repo 2 yum remov
  • c#开发Windows桌面程序,支持触摸屏

    这是一段由new bing聊天机器人提供的代码 xff0c 我没有测试是否能正常运行 xff0c 请谨慎使用 我是这样提问的 xff1a 我想用c 开发一款Windows桌面程序 xff0c 这个程序支持触摸屏 xff0c 这个程序打开后要
  • 七. (《Java核心技术》读书笔记+重点整理系列)异常处理、断言和日志

    目录 异常分类抛出异常捕获异常断言记录日志调试技巧PS 异常分类