詳解Java如何優(yōu)雅的實(shí)現(xiàn)異常捕獲
在一個(gè)優(yōu)秀的項(xiàng)目中一定少不了對(duì)程序流程良好的異常捕獲與日志打印,通過二者結(jié)合可實(shí)現(xiàn)異常程序的快速定位,本片文章將詳細(xì)介紹如何優(yōu)雅的實(shí)現(xiàn)異常捕獲與日志打印輸出。
話不多說,下面我們就直奔主題開始介紹相關(guān)知識(shí)吧。
一、異常捕獲
1. 處理方式
程序異常是開發(fā)時(shí)不可避免的,很多時(shí)候我們需要針對(duì)不同異常進(jìn)行不同的處理,最常用的就是try{} catch(){}
語句,這里主要說明一下兩種常見異常處理的異同。
堆棧打印
當(dāng)用 printStackTrace()
處理異常時(shí),當(dāng)程序出現(xiàn)異常將會(huì)在控制臺(tái)打印異常信息,然后繼續(xù)執(zhí)行之后的代碼。
public void Exception1Demo() { try { Integer.parseInt("abc"); } catch (Exception e) { e.printStackTrace() } // 打印正常輸出 System.out.println("This is will show."); }
異常拋出
通過 throw new xxxException()
則會(huì)將異常信息根據(jù)調(diào)用層級(jí)逐層向上拋出,程序?qū)⒃诋惓L幹袛?,不?huì)繼續(xù)執(zhí)行后續(xù)代碼。
public void Exception2Demo() { try { Integer.parseInt("abc"); } catch (Exception e) { throw new IllegalArgumentException(); } // 打印不會(huì)被輸出 System.out.println("This is will not show."); }
2. 捕獲示例
在上一點(diǎn)中介紹了兩種捕獲異常的處理方式,那在實(shí)際的程序開發(fā)中應(yīng)該如何進(jìn)行選擇呢?
最常見的一種規(guī)范即底層異常永遠(yuǎn)向上拋出,由最頂層統(tǒng)一處理。如下示例中 demo()
調(diào)用了 task()
方法,相對(duì)而言 task()
更為底層因此其捕獲異常時(shí)通過 throw
關(guān)鍵字向上拋出,而 demo()
為最頂層則可以通過 printStackTrace()
打印異常堆棧,當(dāng)然也可以選擇繼續(xù)拋出由系統(tǒng)處理異常。
@Test public void demo() { try { task(); } catch (Exception e) { e.printStackTrace(); } } private void task() { try { Integer.parseInt("abc"); } catch (Exception e) { throw new RuntimeException(e); } }
但如果將上述示例的 task()
方法中捕獲異常替換為 e.printStackTrace();
則 demo()
在調(diào)用 task()
時(shí)將無法捕獲到程序異常,從而導(dǎo)致代碼的執(zhí)行順序可能將與我們?cè)O(shè)想的有所偏差。
當(dāng)然還有一種情景是需要執(zhí)行批量操作,但是我們又想每一批之間可以互不影響,此時(shí)底層模塊也可以選擇不向上拋出異常。
稍微修改一下上述的 task()
方法為如下,這里通過 continue
跳過,替換為 e.printStackTrace()
效果一致。
private void task() { for(int i = 0; i < 5; i++) { try { Integer.parseInt("abc"); } catch (Exception e) { // 不拋出異常,繼續(xù)下一循環(huán) continue; } } }
3. 自定義異常
自定義異常類十分簡單,只需繼承 RuntimeException
,并編寫相應(yīng)的構(gòu)造方法即可,使用方法同上。
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); } }
4. 斷言處理
在 JDK 1.4
中引入斷言語法更簡潔的實(shí)現(xiàn)異常拋出,在 try catch
中是無法預(yù)判異常環(huán)節(jié)從而用其實(shí)現(xiàn)捕獲,斷言則更多的用于條件判斷。
即通過斷言用于判斷是否滿足先決條件,若否則在該處拋出異常,若是則正常執(zhí)行后續(xù)代碼,下面通過示例說明。
assert
通過 assert
可實(shí)現(xiàn)更便捷的數(shù)據(jù)合法性驗(yàn)證,其基本語法如下,當(dāng)表達(dá)式 <expression>
返回值為 false
時(shí)將拋出一個(gè)異常,通過 <message>
定義異常信息提示。
assert <expression> : <message>;
下面通過一個(gè)具體示例演示效果,兩個(gè)示例的作用效果相同,除了拋出的異常類型不同。
public void AssertDemo() { int y = -1; assert y > 0 : "The value of y is lower then zero"; } public void AssertDemo() { int y = -1; if(y < 0) { throw new RuntimeException("The value of y is lower then zero"); } }
Assert
更簡潔的語法規(guī)則,效果同上,當(dāng)捕獲到異常后將中斷程序,不會(huì)繼續(xù)執(zhí)行后續(xù)內(nèi)容。
public void Assert2Demo() { String msg = ""; // 打印異常,效果等同 printStackTrace() Assert.hasLength(msg, "不允許為空"); System.out.println("1111"); }
二、日志監(jiān)控
在開發(fā)時(shí)如果需要查看某一處代碼信息時(shí)我們可以直接使用 println()
進(jìn)行打印輸出,但生成環(huán)境下控制臺(tái)信息顯然變得沒有意義,此時(shí)我們就需要通過日志進(jìn)行信息打印。
1. 日志打印
Java
提供原生日志工具類,導(dǎo)入包即可使用。
import java.util.logging.Logger; public void LogDemo() { Logger logger = Logger.getGlobal(); logger.info("start process..."); logger.warning("memory is running out..."); logger.fine("ignored."); logger.severe("process will be terminated..."); }
2. Log4j框架
除了自帶的日志框架,Log4j
是當(dāng)下較為流行的日志插件,在項(xiàng)目工程中引入下列依賴,其中 slf4j
指的是日志規(guī)范。
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>2.0.7</version> </dependency>
其提供了一下兩種初始化方式,區(qū)別在于使用 getClass()
定義的實(shí)例對(duì)象其子類仍可使用。
// 只能當(dāng)前類可用 Logger logger = LoggerFactory.getLogger(LogTest.class); // 當(dāng)前類與其子類都可用 Logger logger = LoggerFactory.getLogger(getClass());
Slf4j
規(guī)范針對(duì)不同級(jí)別的日志提供不同的接口方法如:info()
、 warn()
、 debug()
、 error()
, 基本使用示例如下:
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LogTest{ Logger logger = LoggerFactory.getLogger(LogTest.class); public static void main(String[] args) { logger.info("info ..."); logger.warn("warn ..."); logger.debug("debug ..."); logger.error("error ..."); } }
三、Logbak配置
1. 項(xiàng)目配置
在工程的 application.yml
添加 logging.config
用于指定日志配置文件路徑。
# 日志配置文件 logging: config: classpath:logback-spring.xml
2. 日志級(jí)別
這里單獨(dú)介紹一下 logger
標(biāo)簽的作用,它可以用于控制指定包路徑下的日志是否輸出。
如配置了 <logger name="xyz.ibudai.slf4j.error" level="ERROR"/>
則 xyz.ibudai.slf4j.error
包路徑下的 info, debug, warn
將不會(huì)出現(xiàn)在配置的輸出日志文件中。
logger
標(biāo)簽中不同的 level
配置輸出的信息如下:
- INFO:輸出
info, warn, error
級(jí)別日志。 - DEBUG:輸出
info, debug, warn, error
級(jí)別日志。 - WARN:只輸出
error
級(jí)別日志。 - ERROR:只輸出
error
級(jí)別日志。
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!-- 指定包路徑的日志輸出級(jí)別, 過濾低級(jí)別日志 --> <logger name="xyz.ibudai.slf4j.info" level="INFO"/> <logger name="xyz.ibudai.slf4j.debug" level="DEBUG"/> <logger name="xyz.ibudai.slf4j.warn" level="WARN"/> <logger name="xyz.ibudai.slf4j.error" level="ERROR"/> </configuration>
3. 配置格式
在 resources
目錄下新建 logback-spring.xml
配置文件,常見標(biāo)簽參考下表。
標(biāo)簽 | 作用 |
---|---|
property | 定義全局變量,可通過 ${} 表達(dá)式獲取。 |
appender | 搭配 appender-ref 可為不同級(jí)別日志設(shè)置文件輸出配置。 |
logger | 用于控制指定包下文件的日志輸出。 |
如下配置示例中即分別為 INFO, DEBUG, ERROR
三種日志級(jí)別配置了日志內(nèi)容文件輸出,具體作用參考備注信息。
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!-- 設(shè)置日志存儲(chǔ)路徑 --> <property name="LOG_HOME" value="./logs"/> <!-- 指定基礎(chǔ)的日志輸出級(jí)別 --> <root level="INFO"> <!-- appender 將會(huì)添加到這個(gè) logger --> <appender-ref ref="CONSOLE"/> <appender-ref ref="INFO"/> <appender-ref ref="DEBUG"/> <appender-ref ref="ERROR"/> </root> <!-- 控制臺(tái)日志輸出 --> <!-- 設(shè)置彩色輸出: -Dlog4j.skipJansi=false --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <!-- 設(shè)置輸出格式 --> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!-- 格式化輸出 --> <!-- (1) %d: 表示日期 --> <!-- (2) %thread: 表示線程名 --> <!-- (3) %-5level: 日志級(jí)別, 從左顯示 4 個(gè)字符寬度 --> <!-- (4) %msg: 表示日志消息 --> <!-- (5) %n: 表示換行符 --> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-4level) %cyan(%logger{50}:%L) - %msg%n</pattern> <!-- 設(shè)置編碼 --> <charset>UTF-8</charset> </encoder> </appender> <!-- 按照 INFO 每天生成日志文件 --> <appender name="INFO" class="ch.qos.logback.core.rolling.RollingFileAppender"> <!-- 日志名, 指定最新的文件名, 其他文件名使用 FileNamePattern --> <file>${LOG_HOME}/info.log</file> <!-- 文件滾動(dòng)模式 --> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 日志輸出的文件名 --> <!-- (1) %i: 表示序號(hào), 當(dāng)日文件多份時(shí)用于區(qū)分 --> <!-- (1) gz: 文件類型, 開啟文件壓縮 --> <FileNamePattern>${LOG_HOME}/bak/info.log.%d{yyyy-MM-dd}.%i.log.gz</FileNamePattern> <!-- 日志文件保留天數(shù) --> <MaxHistory>7</MaxHistory> <!-- 按大小分割同一天的 --> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>128MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <!-- 日志級(jí)別過濾, 過濾低級(jí)別日志 --> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>INFO</level> </filter> <!-- 日志內(nèi)容輸出格式 --> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %thread %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- 按照 DEBUG 每天生成日志文件 --> <appender name="DEBUG" class="ch.qos.logback.core.rolling.RollingFileAppender"> <File>${LOG_HOME}/debug.log</File> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${LOG_HOME}/bak/info.%d{yyyy-MM-dd}.%i.log.gz</FileNamePattern> <MaxHistory>7</MaxHistory> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>128MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>DEBUG</level> </filter> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- 按照 ERROR 每天生成日志文件 --> <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/error.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${LOG_HOME}/bak/error.log.%d{yyyy-MM-dd}.%i.log.gz</FileNamePattern> <MaxHistory>7</MaxHistory> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>128MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %thread %-5level %logger{50} - %msg%n</pattern> <charset>UTF-8</charset> </encoder> </appender> <!-- MyBatis log configure --> <logger name="com.apache.ibatis" level="INFO"/> <logger name="java.sql.Connection" level="ERROR"/> <logger name="java.sql.Statement" level="ERROR"/> <logger name="java.sql.PreparedStatement" level="ERROR"/> </configuration>
到此這篇關(guān)于詳解Java如何優(yōu)雅的實(shí)現(xiàn)異常捕獲的文章就介紹到這了,更多相關(guān)Java異常捕獲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實(shí)踐
本文主要介紹了Java 遞歸查詢部門樹形結(jié)構(gòu)數(shù)據(jù)的實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09Spring實(shí)戰(zhàn)之使用Resource作為屬性操作示例
這篇文章主要介紹了Spring實(shí)戰(zhàn)之使用Resource作為屬性,結(jié)合實(shí)例形式分析了spring載人Resource作為屬性相關(guān)配置與使用技巧,需要的朋友可以參考下2020-01-01Java?8?的異步編程利器?CompletableFuture的實(shí)例詳解
這篇文章主要介紹了Java?8?的異步編程利器?CompletableFuture?詳解,本文通過一個(gè)例子給大家介紹下Java?8??CompletableFuture異步編程的相關(guān)知識(shí),需要的朋友可以參考下2022-03-03詳解Spring cloud使用Ribbon進(jìn)行Restful請(qǐng)求
這篇文章主要介紹了詳解Spring cloud使用Ribbon進(jìn)行Restful請(qǐng)求,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-04-04Logback配置文件這么寫,還說你不會(huì)整理日志?
logback框架會(huì)默認(rèn)加載classpath下命名為logback-spring.xml或logback.xml的配置文件。這篇文章主要介紹了Logback配置文件寫法,需要的朋友可以參考下2020-07-07Java使用poi實(shí)現(xiàn)excel的導(dǎo)入操作指南
使用Apache Poi是一種流行且廣泛使用的方式,可以幫助開發(fā)人員直接從Java代碼中讀取、寫入和處理Excel文件,因此在這篇文章我們將著重介紹如何實(shí)現(xiàn)excel的導(dǎo)入,感興趣的朋友可以跟著小編一起來學(xué)習(xí)2023-06-06