Java学习笔记---异常处理

2021.8.26

java的异常

继承树

 特点:

  • java的用异常来表示错误,可通过try.......catch来捕捉
  • 异常也是类,继承自Throwable
  • Error是无需捕获的严重错误,Exception,是可以处理的,
  • Runtime无需强制捕获,非RuntimeException(Checked Exception)需强制捕获,或者用throws声明

【Java规定】

  • 必须捕获的异常,包括Exception及其子类,但不包括RuntimeException及其子类,这种类型的异常称为Checked Exception。

  • 不需要捕获的异常,包括Error及其子类,RuntimeException及其子类。

【异常捕获】: try... catch

public class Main {
    public static void Main(String[] args){
        bytes[] bs = toGBK('中文');
        System.out.println(Array.toString(bs));
    }
    
    static byte[] toGBK(String s) {
        try {
            // 用指定编码转换String为byte[]:
            return s.getBytes("GBK");
        } catch (UnsupportedEncodingException e) {
            // 如果系统不支持GBK编码,会捕获到UnsupportedEncodingException:
            System.out.println(e); // 打印异常信息
            return s.getBytes(); // 尝试使用用默认编码
        } //不捕获会报错
    }
}

 像getBytes方法的定义,在方法定义的时候,使用throws Xxx表示该方法可能抛出的异常类型。调用方在调用的时候,必须强制捕获这些异常,否则编译器会报错。

public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
    ...
}

觉得麻烦,不想写try的代码,可以在main方法定义throws exception 

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】

  • 捕获多个异常时,要注意顺序,因为多个catch也只会执行一个,子类要放在前面,如果放在对应父类的后面,会永远不被执行
  • 在异常判断里,也可以用关系运算符
    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");
        }
    }

【final】

  • 无论是否捕捉到异常,都要执行某个字段,放在每个catch里面,显得太繁杂,所以java提供了fanal
    void process(String file) throws IOException {
        try {
            ...
        } finally {
            System.out.println("END");
        }
    }

抛出异常

【异常的传播】:如果当前出现的异常,没有被捕捉,会传到上一层调用方法,知道被捕

【printStackTrace()】:打印出方法的调用栈

抛出异常的方法:

  1. 先创建某个exception的实例
  2. 在throw 出来
    void process2(String s) {
        if (s==null) {
            NullPointerException e = new NullPointerException();
            throw e;
        }
    }
    //  实际会省略一些
    void process2(String s) {
        if (s==null) {
            throw new NullPointerException();
        }
    }

捕获到异常并再次抛出时,一定要留住原始异常,否则很难定位第一案发现场! 

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

    static void process1() {
        try {
            process2();
        } catch (NullPointerException e) {   
            throw new IllegalArgumentException(e); //传入参数,保留原异常的casuse
        }
    }

    static void process2() {
        throw new NullPointerException();
    }
}

【异常屏蔽】:catch的捕捉和抛出异常不影响final的输出,但是在final抛出异常之后,catch就不会抛出异常了,因为只能抛出一个异常,这种就叫做''被屏蔽''的异常,suppressed Exception

        如仍然需要抛出所有异常,我们可以用一个变量先保存,调用Throwable.addsuppresed。在final里面一起输出。

public class Main {
    public static void main(String[] args) throws Exception {
        Exception origin = null;
        try {
            System.out.println(Integer.parseInt("abc"));
        } catch (Exception e) {
            origin = e;
            throw e;
        } finally {
            Exception e = new IllegalArgumentException();
            if (origin != null) {
                e.addSuppressed(origin);
            }
            throw e;
        }
    }
}

 大多数不用final抛出异常

自定义异常

业务需要的时候,DIY异常

一般时从Exception中派送一个baseException,然后在从baseException派生出具体的异常

public class BaseException extends RuntimeException {
}
public class UserNotFoundException extends BaseException {
}

public class LoginFailedException extends BaseException {
}

 具体以后需要再看

NullPointerException空指针异常

null

NullPointerException是Java代码常见的逻辑错误,应当早暴露,早修复;

如果一个语句  a.b.c.d(),不知道时哪个出现了nul,就可以启用Java 14的增强异常信息来查看NullPointerException的详细错误信息。这种增强的NullPointerException详细信息是Java 14新增的功能,但默认是关闭的,我们可以给JVM添加一个-XX:+ShowCodeDetailsInExceptionMessages参数启用它:

java -XX:+ShowCodeDetailsInExceptionMessages Main.java

使用断言

【assert】:成立时,正常,不成立时抛出AssertionError

断言失败时,会使程序结束推出,因此一般不用于可回复的异常,比如NPE空指针异常。

public static void main(String[] args) {
    double x = Math.abs(-123.45);
    assert x >= 0 : "x must >= 0";
    System.out.println(x);
}

 要使用断言时,要在JVM下执行一下语句

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

使用JDK Logging

【java.util.logging】做程序调试的时候,使用log方便

import java.util.logging.Level;
import java.util.logging.Logger;
public class Hello {
    public static void main(String[] args) {
        Logger logger = Logger.getGlobal();
        logger.info("start process...");
        logger.warning("memory is running out...");
        logger.fine("ignored.");
        logger.severe("process will be terminated...");
    }
}
---------------------------
Mar 02, 2019 6:32:13 PM Hello main
INFO: start process...
Mar 02, 2019 6:32:13 PM Hello main
WARNING: memory is running out...
Mar 02, 2019 6:32:13 PM Hello main
SEVERE: process will be terminated...
没有输出fine severe

JDK的Logging定义了7个日志级别,从严重到普通:

  • SEVERE
  • WARNING
  • INFO
  • CONFIG
  • FINE
  • FINER
  • FINEST

默认等级时INFO,INFO以下的不会被打印

使用Commons Logging

 JDK logging用的比较少,一般是用第三方库Commons Logging,一般只用连个类log,logFactory

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.");
    }
}

编译的时候,还需要制定classpath,具体要自己另外观看。

先把具体的包放在同一个package下,然后编译命令

javac -cp commons-logging-1.2.jar Main.java

 成功的话会有一个.class文件,然后就可以执行这个class了,注意也要制定classpath

java -cp .;commons-logging-1.2.jar Main

 Commons logging里级别有6个

  • FATAL
  • ERROR
  • WARNING
  • INFO
  • DEBUG
  • TRACE

默认级别是INFO。

如果在静态方法中使用log,通常直接定义一个静态类型变量:

// 在静态方法中引用Log:
public class Main {
    static final Log log = LogFactory.getLog(Main.class);

    static void foo() {
        log.info("foo");
    }
}

实例方法,就定义一个实例变量 

// 在实例方法中引用Log:
public class Person {
    protected final Log log = LogFactory.getLog(getClass());

    void foo() {
        log.info("foo");
    }
}
//注意到实例变量log的获取方式是LogFactory.getLog(getClass())
//,虽然也可以用LogFactory.getLog(Person.class),
//但是前一种方式有个非常大的好处,就是子类可以直接使用该log实例。

如果student继承Person,其log实际等于 LogFactory.getLog(Student.class)

        Commons Logging的日志方法,例如info(),除了标准的info(String)外,还提供了一个非常有用的重载方法:info(String, Throwable),这使得记录异常更加简单:

try {
    ...
} catch (Exception e) {
    log.error("got exception!", e);  
}

使用Log4j

  • 和Commons Logging是两兄弟,Commons Logging作为API写入信息,Log4j作为底层配置。
  • 通过Commons Logging实现日志,不需要修改代码即可使用Log4j
  • 一开始要配置一些内容,比如XML的一些配置,根据配置分配信息

使用SLF4J和logback

SLF4J和Commons log的不同

//Commons log
int score = 99;
p.setScore(score);
log.info("Set score " + score + " for Person " + p.getName() + " ok.");

//SLF4J
int score = 99;
p.setScore(score);
logger.info("Set score {} for Person {} ok.", score, p.getName());
Commons LoggingSLF4J
org.apache.commons.logging.Logorg.slf4j.Logger
org.apache.commons.logging.LogFactoryorg.slf4j.LoggerFactory

 logBack和SLF4J是两兄弟,logBack也是用来配置文件的,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值