JAVA异常

    技术2022-07-11  80

    JAVA异常

    Throwable

    从继承关系可知:Throwable是异常体系的根,它继承自Object。 Throwable有两个体系:Error和Exception,Error表示严重的错误,程序对此一般无能为力 Error: OutOfMemoryError:内存耗尽 NoClassDefFoundError:无法加载某个Class StackOverflowError:栈溢出 Exception: RuntimeException以及它的子类; 非RuntimeException(包括IOException、ReflectiveOperationException等等) 编译器对RuntimeException及其子类不做强制捕获要求, 不是指应用程序本身不应该捕获并处理RuntimeException。是否需要捕获,具体问题具体分析。

    捕获异常

    所有异常都可以调用printStackTrace()方法打印异常栈,这是一个简单有用的快速打印异常的方法。

    抛出异常

    为了能追踪到完整的异常栈,在构造异常的时候,把原始的Exception实例传进去, 新的Exception就可以持有原始Exception信息 例子: 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); //这个地方一定要传进去 否则上层就找不到NullPointerException这个异常了 } } static void process2() { throw new NullPointerException(); } } finally catch -> finally -> 抛出异常 异常屏蔽 catch->finally(如果这个地方抛出了异常,就直接结束不会再抛出catch的异常)->抛出异常 怎么才能在finally把所有异常都抛出呢 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; } 通过Throwable.getSuppressed()可以获取所有的Suppressed Exception。 绝大多数情况下,在finally中不要抛出异常。因此,我们通常不需要关心Suppressed Exception

    自定义异常

    项目中抛出的一般都是自定义的异常 Exception │ ├─ RuntimeException │ │ │ ├─ NullPointerException │ │ │ ├─ IndexOutOfBoundsException │ │ │ ├─ SecurityException │ │ │ └─ IllegalArgumentException │ │ │ └─ NumberFormatException │ ├─ IOException │ │ │ ├─ UnsupportedCharsetException │ │ │ ├─ FileNotFoundException │ │ │ └─ SocketException │ ├─ ParseException │ ├─ GeneralSecurityException │ ├─ SQLException │ └─ TimeoutException BaseException需要从一个适合的Exception派生,通常建议从RuntimeException派生 public class BaseException extends RuntimeException { } public class UserNotFoundException extends BaseException { } public class LoginFailedException extends BaseException { } 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); } }

    NullPointerException

    尽量避免出现null,1.初始值的时候附上值 2. 逻辑判断的时候进行判断为null的处理 如果必须用null 使用Optional 例子: User user = new User(); Address address = new Address(); // House house = new House(); // house.setName("hah"); // address.setHouse(house); user.setAddress(address); Optional<User> userOptional = Optional.of(user); String name = userOptional.map(u->u.getAddress()).map(address1 -> address1.getHouse()).map(house1 -> house1.getName()).orElse("defff"); System.out.println(name); //这时这个不会抛出异常 返回默认值 当有值得时候返回 非常优雅 System.out.println(user.getAddress().getHouse().getName()); //这时这个会抛出异常 JAVA14 对NullPointerException进行了信息增强,能提示出哪个变量是null,但是默认是不开启的 如果要开启 需要配置JVM XX:+ShowCodeDetailsInExceptionMessages

    使用断言 (Assertion) 是一种调试程序的方式 ,在java中使用assert关键字来实现断言

    断言是一种调试方式,断言失败会抛出AssertionError,只能在开发和测试阶段启用断言 对可恢复的错误不能使用断言,而应该抛出异常 断言很少被使用,更好的方法是编写单元测试 开启断言的方法是 -enableassertions(可简写为-ea public static void main(String[] args) { int x = -1; assert x > 0: "抛出异常"; System.out.println(x); }

    JDK Logging

    Logger logger = Logger.getGlobal(); logger.info("start process..."); logger.warning("memory is running out..."); logger.fine("ignored."); logger.severe("process will be terminated..."); 缺点是:需要在JVM启动时传递参数-Djava.util.logging.config.file=<config-file-name> 所以更方便的是使用日志系统

    Commons Logging

    和Java标准库提供的日志不同,Commons Logging是一个第三方日志库,它是由Apache创建的日志模块,这是一个接口 首先会找Log4J,没有找到会使用JDK Logging 使用步骤: 1. 通过LogFactory获取Log类的实例 2. 使用Log实例的方法打日志 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."); } } 需要下载三方依赖然后打成jar包,执行测试命令 java -cp .;commons-logging-1.2.jar Main -cp是指定classpath Commons Logging定义了6个日志级别 FATAl ERROR WARNING INFO DEBUG TRACE 常用格式 // 在实例方法中引用Log: public class Person { protected final Log log = LogFactory.getLog(getClass()); void foo() { log.info("foo"); } } log.error(String, Throwable)

    Log4j

    Common Logging 是日志接口 日志实现 是Log4j Log4j 有几种记录日志的方式 console 输出到控制台 file 输出到文件里 socket 通过网络输出到远程计算机 jdbc 输出到数据库 Log4j配置文件名为log4j2.xml 依赖jar包为log4j-api-2.x.jar log4j-core-2.x.jar log4j-jcl-2.x.jar <?xml version="1.0" encoding="UTF-8"?> <Configuration> <Properties> <!-- 定义日志格式 --> <Property name="log.pattern">%d{MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36}%n%msg%n%n</Property> <!-- 定义文件名变量 --> <Property name="file.err.filename">log/err.log</Property> <Property name="file.err.pattern">log/err.%i.log.gz</Property> </Properties> <!-- 定义Appender,即目的地 --> <Appenders> <!-- 定义输出到屏幕 --> <Console name="console" target="SYSTEM_OUT"> <!-- 日志格式引用上面定义的log.pattern --> <PatternLayout pattern="${log.pattern}" /> </Console> <!-- 定义输出到文件,文件名引用上面定义的file.err.filename --> <RollingFile name="err" bufferedIO="true" fileName="${file.err.filename}" filePattern="${file.err.pattern}"> <PatternLayout pattern="${log.pattern}" /> <Policies> <!-- 根据文件大小自动切割日志 --> <SizeBasedTriggeringPolicy size="1 MB" /> </Policies> <!-- 保留最近10份 --> <DefaultRolloverStrategy max="10" /> </RollingFile> </Appenders> <Loggers> <Root level="info"> <!-- 对info级别的日志,输出到console --> <AppenderRef ref="console" level="info" /> <!-- 对error级别的日志,输出到err,即上面定义的RollingFile --> <AppenderRef ref="err" level="error" /> </Root> </Loggers> </Configuration>

    SLF4J和Logback

    SLF4j 相对Commons logging commons logging log.info("Set score " + score + " for Person " + p.getName() + " ok."); SLF4j这么用 logger.info("Set score {} for Person {} ok.", score, p.getName()); Commons Logging SLF4J org.apache.commons.logging.Log org.slf4j.Logger org.apache.commons.logging.LogFactory org.slf4j.LoggerFactory 不同之处就是Log变成了Logger,LogFactory变成了LoggerFactory。 jar包:slf4j-api-1.7.x.jar logback-classic-1.2.x.jar logback-core-1.2.x.jar 把logback.xml放到classpath下 <?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> <charset>utf-8</charset> </encoder> <file>log/output.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"> <fileNamePattern>log/output.log.%i</fileNamePattern> </rollingPolicy> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>1MB</MaxFileSize> </triggeringPolicy> </appender> <root level="INFO"> <appender-ref ref="CONSOLE" /> <appender-ref ref="FILE" /> </root> </configuration> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-classic --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <!-- https://mvnrepository.com/artifact/ch.qos.logback/logback-core --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency>
    Processed: 0.017, SQL: 9