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>