Mastering Java Exception Handling: From Basics to Advanced Practices
This article explores Java's exception hierarchy, demonstrates proper use of try‑catch‑finally and return statements, shows how to define custom system, business, and third‑party exceptions, implements global handling with @RestControllerAdvice, and discusses monitoring and alerting strategies for robust backend development.
1. Introduction
During program development, exception handling is a complex dimension that developers of all levels must face; exceptions reflect system defects and optimization points, and cannot be completely avoided. Proper handling and reduction of exception frequency are fundamental to system quality.
2. Exception Hierarchy
Java's exception hierarchy includes:
Throwable : Superclass of all errors and exceptions.
Error : Typically unrecoverable, serious errors that cause the JVM to terminate the thread.
Exception : Exceptions that can be caught and handled by the program.
Exceptions are divided into checked and unchecked exceptions.
Checked Exception : Detected at compile time; must be caught or declared, otherwise compilation fails.
Unchecked Exception : Detected at runtime; may be thrown during execution.
3. Exception Usage
3.1 Details
Java exception handling keywords are:
try(code block that may throw),
catch(captures exceptions),
finally(always executed),
throw(throws a specific exception), and
throws(declares thrown exceptions).
<code>public class UseExe01 {
public static void main(String[] args) {
try {
strStm();
ioStm();
} catch (NullPointerException e) {
System.out.println("空指针异常:" + e.getMessage());
e.printStackTrace();
} catch (IOException e) {
System.out.println("IO流异常:" + e.getMessage());
e.printStackTrace();
} catch (Exception e) {
System.out.println("异常:" + e.getMessage());
e.printStackTrace();
} finally {
System.out.println("execute...finally");
}
}
public static void ioStm() throws FileNotFoundException {
new FileInputStream(new File("file_path"));
}
public static String strStm() throws NullPointerException {
Object object = new Object();
return object.getClass().getName();
}
}
</code>3.2 Return Value Issues
When
returnappears in
try,
catch, or
finally, the actual returned value depends on the execution flow.
3.2.1 Primitive Types
<code>public class UseExe02 {
// Returns 2
public static int getInt1() {
try {
int i = 1 / 0;
} catch (ArithmeticException e) {
e.printStackTrace();
return 1;
} finally {
System.out.println("execute...finally");
return 2;
}
}
// Returns 1
public static int getInt2() {
int a = 1;
try {
int i = 1 / 0;
return a;
} catch (ArithmeticException e) {
e.printStackTrace();
return a;
} finally {
++a;
System.out.println("execute...finally");
}
}
// Returns 3
public static int getInt3() {
int a = 1;
try {
int i = 1 / 0;
a++;
return a;
} catch (ArithmeticException e) {
a++;
e.printStackTrace();
} finally {
a++;
System.out.println("execute...finally");
}
return a;
}
}
</code>3.2.2 Reference Types
<code>public class UseExe03 {
// Returns 张三
public static String getStr1() {
String var;
try {
var = new String("张三");
return var;
} catch (ArithmeticException e) {
e.printStackTrace();
} finally {
var = new String("李四");
System.out.println("execute...finally:" + var);
}
return var;
}
// Returns 李四
public static String getStr2() {
String var;
try {
int i = 1 / 0;
var = new String("张三");
return var;
} catch (ArithmeticException e) {
e.printStackTrace();
var = new String("李四");
return var;
} finally {
var = new String("王五");
System.out.println("execute...finally:" + var);
}
}
// Returns 王五
public static String getStr3() {
String var;
try {
int i = 1 / 0;
var = new String("张三");
return var;
} catch (ArithmeticException e) {
var = new String("李四");
e.printStackTrace();
} finally {
var = new String("王五");
System.out.println("execute...finally:" + var);
}
return var;
}
}
</code>If only the
tryblock contains a
return, the value from
tryis returned.
If
trythrows and
catchexecutes a
return, that value is returned.
If the
finallyblock contains a
return, its value overrides previous returns when
finallyexecutes normally.
From a design perspective, using
returninside
finallyis discouraged because it can prematurely terminate the method.
4. Project Practice
4.1 Exception Definition
In complex distributed systems, capturing exception information is crucial for rapid diagnosis and resolution. Two core dimensions are considered: capturing and solving exceptions, and propagating exception information to the client.
System Exception : e.g., timeout or service-level failures that require developer intervention.
Business Exception : User‑recoverable issues such as parameter validation or authorization problems.
Third‑Party Exception : Errors arising from interactions with external systems, encapsulated with unified response codes.
4.2 Exception Encapsulation
Define a base exception class extending
RuntimeException:
<code>public class BaseExe extends RuntimeException {
private String code;
public BaseExe(String code, String msg) {
super(msg);
this.code = code;
}
public BaseExe(String message, Throwable cause) {
super(message, cause);
}
// other constructors omitted
}
</code>System exception and enum:
<code>public enum SysExeCode {
SYSTEM_EXE("S00000", "系统异常");
}
public class SysException extends BaseExe {
public SysException(String code, String msg) {
super(code, msg);
}
public SysException(SysExeCode sysExeCode) {
super(sysExeCode.getCode(), sysExeCode.getMsg());
}
}
</code>Business exception and enum:
<code>public enum BizExeCode {
BIZ_EXE("B00000", "业务异常");
}
public class BizException extends BaseExe {
public BizException(String code, String msg) {
super(code, msg);
}
public BizException(BizExeCode bizExeCode) {
super(bizExeCode.getCode(), bizExeCode.getMsg());
}
}
</code>Third‑party exception and enum:
<code>public enum ThirdExeCode {
THIRD_EXE("T00000", "第三方异常");
}
public class ThirdException extends BaseExe {
private String thirdCode;
private String thirdMsg;
public ThirdException(String code, String msg) {
super(code, msg);
}
public ThirdException(String code, String msg, String thirdCode, String thirdMsg) {
super(code, msg);
this.thirdCode = thirdCode;
this.thirdMsg = thirdMsg;
}
public ThirdException(ThirdExeCode thirdExeCode, String thirdCode, String thirdMsg) {
super(thirdExeCode.getCode(), thirdExeCode.getMsg());
this.thirdCode = thirdCode;
this.thirdMsg = thirdMsg;
}
}
</code>4.3 Exception Handling
4.3.1 Response Method
Use
@RestControllerAdviceand
@ExceptionHandlerfor global exception handling:
<code>@RestControllerAdvice
public class ExeHandler {
/** Default exception */
@ExceptionHandler(value = Exception.class)
public void defaultException(Exception e) {
// unified response
}
/** System exception */
@ExceptionHandler(value = SysException.class)
public void sysException(SysException e) {
// unified response
}
/** Business exception */
@ExceptionHandler(value = BizException.class)
public void bizException(BizException e) {
// unified response
}
/** Third‑party exception */
@ExceptionHandler(value = ThirdException.class)
public void thirdException(ThirdException e) {
// unified response
}
}
</code>4.3.2 Recording Method
Custom annotation + AOP is used to record logs, especially exception logs:
<code>@Component
@Aspect
public class LogAop {
/** Log pointcut */
@Pointcut("@annotation(com.defined.log.annotation.DefinedLog)")
public void logPointCut() {}
/** Around advice */
@Around("logPointCut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) {
try {
// execute method
Object result = proceedingJoinPoint.proceed();
return result;
} catch (SysException e) {
// system exception handling
} catch (BizException e) {
// business exception handling
} catch (ThirdException e) {
// third‑party exception handling
} catch (Exception e) {
// default exception handling
} finally {
// information processing
}
return null;
}
}
</code>4.4 Exception Notification
System and third‑party exceptions trigger immediate alerts to developers via messaging platforms (e.g., WeChat, DingTalk, email, SMS) for rapid diagnosis.
4.5 System Fault Analysis
Monitoring provides quick fault detection and analysis of request chains in distributed systems.
Request chain analysis visualizes complex call paths.
Log recording capabilities enable pinpointing issues and narrowing problem scope.
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.