廖雪峰java教程学习笔记——异常处理

本文详细介绍了Java的异常处理机制,包括基本语法、捕获多种异常、finally块、异常类型转换和自定义异常。同时,讨论了断言assert在调试中的作用,并对比了JDK Logging、Commons Logging、Log4j以及SLF4J和Logback的日志框架,阐述了它们的使用场景和特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基本语法

try catch throw

        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();  //打印异常栈
        }

如果不想在函数中捕获异常,可以加上throws把它转交给调用方处理;

最后一层层扔到了main,若连main也不想处理,可以继续throw,这就真的丢弃了。这时一旦出错直接程序崩溃。

public class Main {
    public static void main(String[] args) throws Exception {
        byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
    }

    static byte[] toGBK(String s) throws UnsupportedEncodingException {
        // 用指定编码转换String为byte[]:
        return s.getBytes("GBK");
    }
}

捕获多种异常

catch会依次执行,需要将子类放到前面,否则总会落到父类里去,正确做法如下:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {  //IOException的子类
        System.out.println("Bad encoding");
    } catch (IOException e) {
        System.out.println("IO error");
    }
}

若两种异常没有继承关系,可以用|连接:

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (IOException | NumberFormatException e) { // IOException或NumberFormatException
        System.out.println("Bad input");
    } catch (Exception e) {
        System.out.println("Unknown error");
    }
}

finally

try…catch…finally,finally总是最后执行,finally可以省略。

public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
    } catch (IOException e) {
        System.out.println("IO error");
    } finally {
        System.out.println("END");
    }
}

异常类型转换

public class Main {
    public static void main(String[] args) {
        try {
            process1();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {  //2. 抓到process2的异常
            throw new IllegalArgumentException(e);  //3.扔新的异常
        }
    }

    static void process2() {
        throw new NullPointerException();  //1. 手动抛个异常
    } 
}

打印:

java.lang.IllegalArgumentException: java.lang.NullPointerException
	at oop_package.src.com.itranswarp.sample.Main.process1(Main.java:16)
	at oop_package.src.com.itranswarp.sample.Main.main(Main.java:6)
Caused by: java.lang.NullPointerException
	at oop_package.src.com.itranswarp.sample.Main.process2(Main.java:21)
	at oop_package.src.com.itranswarp.sample.Main.process1(Main.java:14)
	... 1 more

Caused by: Xxx,指向了问题源头process2()的异常。

调试时Caused by很关键。

常用异常类型

Exception
│
├─ RuntimeException
│  │
│  ├─ NullPointerException
│  │
│  ├─ IndexOutOfBoundsException
│  │
│  ├─ SecurityException
│  │
│  └─ IllegalArgumentException
│     │
│     └─ NumberFormatException
│
├─ IOException
│  │
│  ├─ UnsupportedCharsetException
│  │
│  ├─ FileNotFoundException
│  │
│  └─ SocketException
│
├─ ParseException
│
├─ GeneralSecurityException
│
├─ SQLException
│
└─ TimeoutException

自定义异常

在大型项目中通常自己先定义一个BaseException作为“根异常”,通常建议从RuntimeException派生。然后再继续派生出各种业务类型的异常。

public class BaseException extends RuntimeException {
    public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

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

    public BaseException(Throwable cause) {
        super(cause);
    }
}

断言 assert

方便抛出异常并打印自定义消息,但程序会退出,所以只应用于调试阶段:

        int x = -1;
        assert x > 0 : "x <= 0!!!";
        System.out.println(x);

assert默认是不开启的,需要给jvm传递-enableassertions(可简写为-ea)参数启用断言:

$ java -ea Main.java
Exception in thread "main" java.lang.AssertionError
	at Main.main(Main.java:5)

实际开发很少用断言,一般采用单元测试。

JDK Logging

package oop_package.src.com.itranswarp.sample;

import java.util.logging.Logger;

public class Main{
    public static void main(String[] args) {
        Logger logger = Logger.getGlobal();
        logger.info("start process...");
        logger.warning("memory is running out...");
        logger.fine("ignored.");  //这条没被打印,info级别以上才打印
        logger.severe("process will be terminated...");
    }
}

打印:

May 26, 2021 4:46:33 PM oop_package.src.com.itranswarp.sample.Main main
INFO: start process...
May 26, 2021 4:46:33 PM oop_package.src.com.itranswarp.sample.Main main
WARNING: memory is running out...
May 26, 2021 4:46:33 PM oop_package.src.com.itranswarp.sample.Main main
SEVERE: process will be terminated...

logging级别:

SEVERE
WARNING
INFO
CONFIG
FINE
FINER
FINEST

默认级别是INFO,INFO以下的都忽略。

设置级别需要在JVM启动时传递参数-Djava.util.logging.config.file=<config-file-name>

Commons Logging

第三方日志库,可以连接不同日志系统,默认搜索使用Log4j,如果没有则用JDK Logging。

只需操作两个类:

  1. LogFactory
  2. Log
//需要导入专门jar包,不在标准库里
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Main {
    public static void main(String[] args) {
        Log log = LogFactory.getLog(Main.class);
        log.info("start...");
        log.warn("end.");
    }
}

通常在一个父类里定义一个常量:

  protected final Log log = LogFactory.getLog(getClass());

以后的子类继承后直接用log就行。

Log4j

流行的日志实现,可以作为上面Commons Logging接口的具体实现。

Log4j能自动通过不同的Appender把同一条日志输出到不同的目的地。例如:

  • console:输出到屏幕;
  • file:输出到文件;
  • socket:通过网络输出到远程计算机;
  • jdbc:输出到数据库

使用Log4j的时候,需要配置log4j2.xml的文件。

只要把相关Log4j的相关jar包放到classpath,Commons Logging就能自动发现它,其 log.info()等打印的信息就是由Log4j完成的了。

SLF4J和Logback

SLF4J类似于Commons Logging,也是一个日志接口;

Logback类似于Log4j,是一个日志的实现。

它们俩搭档起来,对Commons Logging 和 Log4j 做了一些改进,比如自带带占位符功能,不用自己拼接字符串了:

logger.info("Set score {} for Person {} ok.", score, p.getName());

从目前的趋势来看,越来越多的开源项目从Commons Logging加Log4j转向了SLF4J加Logback。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值