Java Review(三十二、异常处理)

图一:Java 异常层次结构

在这里插入图片描述

需要注意的是,所有的异常都是由 Throwable 继承而来,但在下一层立即分解为两个分支:ErrorException:

Error 类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误。 应用程序不应该抛出这种类型的对象。 如果出现了这样的内部错误, 除了通告给用户,并尽力使程序安全地终止之外, 再也无能为力了。这种情况很少出现。

在设计 Java 程序时, 需要关注 Exception 层次结构。 这个层次结构又分解为两个分支:

Checked异常Runtime 异常 (运行时异常) 。 所有的 RuntimeException类及其子类的实例被称为 Runtime 异常:不是 RuntimeException 类及其子类的异常实例则被称为Checked 异常 。

图二:Java 常见异常类层次结构

在这里插入图片描述

只有 Java 语言提供了 Checked 异常,其他语言都没有提供 Checked 异常 。 Java 认为 Checked 异常都是可以被处理(修复〉的异常,所以 Java 程序必须显式处理 Checked 异常 。 如果程序没有处理 Checked异常,该程序在编译时就会发生错误,无法通过编译。

Checked 异常体现了 Java 的设计哲学一一没有完善错误处理的代码根本就不会被执行 !

Java 语言规范将派生于Error 类 或 RuntimeException 类的所有异常称为非受检( unchecked ) 异常,所有其他的异常称为受检( checked) 异常。 编译器将核查是否为所有的受査异常提供了异常处理器。

捕捉异常

======================================================================

使用 try… . catch 捕获异常


要想捕获一个异常, 必须设置 try/catch语句块。最简单的 try语句块如下所示:

try{

// 业务实现代码

}catch (Exception e){

alert 输入不合法

goto retry

}

如果在 try语句块中的任何代码抛出了一个在 catch 子句中说明的异常类, 那么:

  • 程序将跳过 try语句块的其余代码。

  • 程序将执行 catch 子句中的处理器代码。

如果在 try 语句块中的代码没有拋出任何异常,那么程序将跳过 catch 子句。

如果方法中的任何代码拋出了一个在 catch 子句中没有声明的异常类型,那么这个方法就会立刻退出(希望调用者为这种类型的异常设记了catch 子句。

如下是一个典型的捕获异常示例:

public void read(String filename) {

try{

InputStream in = new Filei叩utStream(filename);

int b;

while ((b = in.read()3 != -1) {

process input

}

}catch (IOException exception) {

exception.printStackTrace();

}

捕获多个异常


在一个 try 语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理。可以按照下列方式为每个异常类型使用一个单独的 catch 子句:

try{

code that might throwexceptions

}catch (FileNotFoundException e) {

emergencyactionfor missingfiles

}catch (UnknownHostException e) {

emergency actionfor unknown hosts

}catch (IOException e) {

emergencyactionfor all other I/O problems

}

异常对象可能包含与异常本身有关的信息。要想获得异常对象的更多信息, 可以使用以下几个方法:

  • getMessage(): 返回该异常的详细描述字符串 。

  • printStackTrace() : 将该异常的跟踪枝信息输出到标准错误输出 。

  • printStackTrace(PrintS仕eam s): 将该异常的跟踪枝信息输出到指定输出流 。

  • getStackTrace() : 返回该异常的跟踪枝信息 。

可以使用

e.getClass().getName()

得到异常对象的实际类型。

在 Java SE 7中,同一个 catch 子句中可以捕获多个异常类型。例如,假设对应缺少文件和未知主机异常的动作是一样的,就可以合并 catch 子句:

try{

code that might throw exceptions

}catch (FileNotFoundException | UnknownHostException e) {

emergency action for missing files and unknown hosts

}catch (IOException e) {

emergency action for all other I/O problems

}

使用一个 catch 块捕获多种类型的异常时需要注意如下两个地方 :

  • 捕获多种类型的异常时 , 多种异常类型之间用竖线 (|) 隔开。

  • 捕获多种类型的异常时 , 异常变量有隐式的 final 修饰,因此程序不能对异常变量重新赋值。

使用 finally 回收资源


当代码抛出一个异常时, 就会终止方法中剩余代码的处理,并退出这个方法的执行。如果方法获得了一些本地资源,并且只有这个方法自己知道,又如果这些资源在退出方法之前必须被回收,那么就会产生资源回收问题。一种解决方案是捕获并重新抛出所有的异常,这种解决方案并不完美,这是因为需要在两个地方清除所分配的资源。一个在正常的代码中;另一个在异常代码中。

Java 有一种更好的解决方案,这就是 finally 子句。下面将介绍 Java 中如何恰当地关闭一个文件。如果使用 Java 编写数据库程序,就需要使用同样的技术关闭与数据库的连接。当发生异常时,关闭所有数据库的连接是非常重要的。不管是否有异常被捕获,finally 子句中的代码都被执行。在下面的示例中, 程序将在所

有情况下关闭文件:

InputStream in = new FileInputStream(. . .);

try{

code that might throwexceptions

}catch (IOException e) { // 3

showerror message

// 4

}finally{ // 5

in.close();

}

在上面这段代码中,有下列 3 种情况会执行 finally 子句:

1 ) 代码没有抛出异常。 在这种情况下, 程序首先执行 try 语句块中的全部代码,然后执行 finally 子句中的代码t 随后, 继续执行 try 语句块之后的第一条语句。也就是说,执行标注的 1、 2、 5、 6 处。

2 ) 抛出一个在 catch 子句中捕获的异常。在上面的示例中就是 IOException 异常。在这种情况下,程序将执行 try语句块中的所有代码,直到发生异常为止。此时,将跳过 try语句块中的剩余代码,转去执行与该异常匹配的 catch 子句中的代码, 最后执行 finally 子句中的代码。如果 catch 子句没有抛出异常,程序将执行 try 语句块之后的第一条语句。在这里,执行标注 1、 3、 4、5、 6 处的语句。如果 catch 子句抛出了一个异常, 异常将被抛回这个方法的调用者。在这里, 执行标注1、 3、 5 处的语句。

3 ) 代码抛出了一个异常, 但这个异常不是由 catch 子句捕获的。在这种情况下,程序将执行 try 语句块中的所有语句,直到有异常被抛出为止。此时, 将跳过 try 语句块中的剩余代码, 然后执行 finally 子句中的语句, 并将异常抛给这个方法的调用者。在这里, 执行标注 1、 5 处的语句。

try 语句可以只有 finally 子句,而没有 catch 子句。例如,下面这条 try 语句:

InputStream in = . .

try{

code that might throwexceptions

}finally{

in.close();

}

警告:当 finally 子句包含 return 语句时, 将会出现一种意想不到的结果„ 假设利用 return语句从 try语句块中退出。在方法返回 前,finally 子句的内容将被执行。如果 finally 子句中也有一个 return 语句,这个返回值将会覆盖原始的返回值。如:

public static int f(int n) {

try{

int r = n * n;

return r;

}finally{

if (n = 2) return 0;

}

}

如果调用 f(2), 那么 try 语句块的计算结果为 r = 4, 并执行 return 语句然而,在方法真正返回前,还要执行 finally 子句。finally 子句将使得方法返回 0, 这个返回值覆盖了原始的返回值 4

抛出异常

======================================================================

使用throws声明抛出异常


使用throws声明抛出异常的思路是:当前方法不知道如何处理这种类型的异常,该异常应该由上一级调用者处理;如果main方法也不知道如何处理该类型的异常,也可以使用throws声明抛出异常,该异常交给JVM处理,JVM对异常的处理方法是:打印异常的跟踪栈信息,并终止程序运行。

throws只能在方法签名中使用,throws可以声明抛出多个异常类,多个异常类之间以逗号隔开:

throws ExceptionClass1,ExceptionClass2 …………

ThrowsTest.java

public class ThrowsTest{

public static void main(String[] args) throws Exception{

// 因为test()方法声明抛出IOException异常,

// 所以调用该方法的代码要么处于try…catch块中,

// 要么处于另一个带throws声明抛出的方法中。

test();

}

public static void test()throws IOException{

// 因为FileInputStream的构造器声明抛出IOException异常,

// 所以调用FileInputStream的代码要么处于try…catch块中,

// 要么处于另一个带throws声明抛出的方法中。

FileInputStream fis = new FileInputStream(“a.txt”);

}

}

使用 throw 抛出异常


如果需要在程序中自行抛出异常,则应使用 throw 语句。throw 吾句可以单独使用,throw 语句抛出的不是异常类,而是一个异常实例,而且每次只能抛出一个异常实 throw 语句的语法格式如下:

throw ExceptionInstance ;

ThrowTest.java

public class ThrowTest{

public static void main(String[] args){

try{

// 调用声明抛出Checked异常的方法,要么显式捕获该异常

// 要么在main方法中再次声明抛出

throwChecked(-3);

}catch (Exception e){

System.out.println(e.getMessage());

}

// 调用声明抛出Runtime异常的方法既可以显式捕获该异常,

// 也可不理会该异常

throwRuntime(3);

}

public static void throwChecked(int a)throws Exception{

if (a > 0){

// 自行抛出Exception异常

// 该代码必须处于try块里,或处于带throws声明的方法中

throw new Exception(“a的值大于0,不符合要求”);

}

}

public static void throwRuntime(int a){

if (a > 0){

// 自行抛出RuntimeException异常,既可以显式捕获该异常

// 也可完全不理会该异常,把该异常交给该方法调用者处理

throw new RuntimeException(“a的值大于0,不符合要求”);

}

}

}

自定义异常类


在程序中,可能会遇到任何标准异常类都没有能够充分地描述清楚的问题。 在这种情况下,可以自定义异常类。

是定义一个派生于Exception 的类,或者派生于 Exception 子类的类。例如, 定义一个派生于 IOException 的类。

习惯上, 定义的类应该包含两个构造器, 一个是默认的构造器;另一个是带有详细描述信息的构造器(超类 Throwable 的 toString 方法将会打印出这些详细信息, 这在调试中非常有用)。

class FileFormatException extends IOException{

public FileFormatExceptionO {}

public FileFormatException(String gripe) {

super(gripe);

}

接下来,就可以抛出自定义的异常类型:

String readData(BufferedReader in) throws FileFormatException{

while (. . .) {

if (ch == -1){ // EOF encountered

if (n < len){

throw new FileFornatException();

}

}

}

异常链


对于真实的企业级应用而言,常常有严格的分层关系,层与层之间有非常清晰的划分,上层功能的实现严格依赖于下 API,也不会跨层访问:

图三:MVC三层结构

在这里插入图片描述当业务逻辑层访问持久层出现

SQLException 异常时, 程序不应该把底层的 SQLException 异常传到用户界面,

有如下两个原因。

  • 对 于 正 常 用 户 而 言 , 他 们不想看到底层 SQLException异常,SQLException 异常对他们使用该系统没有任何帮助。

  • 对于恶意用户而言, 将 SQLException 异常暴露出来不安全

把底层的原始异常直接传给用户是一种不负责任的表现。 通常的做法是:程序先捕获原始异常, 然后抛出一个新的业务异常, 新的业务异常中包含了对用户的提示信息, 这种处理方式被称为异常转译。 假设程序需要实现工资计算的方法,

则程序应该采用如下结构的代码来实现该方法:

public void calSal() throws SalException{

try{

// 实现结算工资的业务逻辑

}catch(SQLException sqle){

// 把原始异常记录下来, 留给管理员

// 下面异常中的 message 就是对用户的提示

throw new SalException(“访问底层数据库出现异常”);

} catch(Exception e)

// 把原始异常记录下来, 留给管理员

// 下面异常中的 message 就是对用户的提示

throw new SalException( “系统出现未知异常”);

}

}

Java的异常跟踪栈

============================================================================

异常对象的 printStackTrace()方法用于打印异常的跟踪栈信息, 根据 printStackTrace()方法的输出结果, 开发者可以找到异常的源头, 并跟踪到异常一路触发的过程。

PrintStackTraceTest.java

class SelfException extends RuntimeException

{

SelfException(){}

SelfException(String msg)

{

super(msg);

}

}

public class PrintStackTraceTest

{

public static void main(String[] args)

{

firstMethod();

}

public static void firstMethod()

{

secondMethod();

}

public static void secondMethod()

{

thirdMethod();

}

public static void thirdMethod()

{

throw new SelfException(“自定义异常信息”);

}

}

运行结果:

在这里插入图片描述

异常从thirdMethod方法开始触发 , 传到 secondMethod 方法,再传到firstMethod 方法, 最后传到 main 方法, 在 main 方法终止, 这个过程就是 Java 的异常跟踪栈。

线程、数据库、算法、JVM、分布式、微服务、框架、Spring相关知识

一线互联网P7面试集锦+各种大厂面试集锦

学习笔记以及面试真题解析

public class PrintStackTraceTest

{

public static void main(String[] args)

{

firstMethod();

}

public static void firstMethod()

{

secondMethod();

}

public static void secondMethod()

{

thirdMethod();

}

public static void thirdMethod()

{

throw new SelfException(“自定义异常信息”);

}

}

运行结果:

在这里插入图片描述

异常从thirdMethod方法开始触发 , 传到 secondMethod 方法,再传到firstMethod 方法, 最后传到 main 方法, 在 main 方法终止, 这个过程就是 Java 的异常跟踪栈。

线程、数据库、算法、JVM、分布式、微服务、框架、Spring相关知识

[外链图片转存中…(img-vawQjefY-1718697462821)]

一线互联网P7面试集锦+各种大厂面试集锦

[外链图片转存中…(img-IfxieX0M-1718697462822)]

学习笔记以及面试真题解析

[外链图片转存中…(img-eZK2qtzD-1718697462822)]

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java代码review是为了确保代码的质量和可维护性,以及遵循最佳实践和编码规范。以下是一些常见的Java代码review检查项: 1. 命名规范:检查变量、方法和类的命名是否符合驼峰命名法,并且能够描述其含义。 2. 代码注释:检查代码中是否有足够的注释,以解释代码的功能和实现。注释应该清晰、简洁,并且保持同步更新。 3. 代码格式化:检查代码的格式是否一致,例如缩进、空格和换行符的使用。 4. 异常处理:检查是否对可能出现的异常情况做了处理,例如使用try-catch语句来捕获异常,并给出适当的错误处理和日志记录。 5. 可读性:检查代码的可读性,确保代码清晰易懂。通过使用有意义的变量名、避免过长的方法和类,以及使用合适的代码结构来提高代码的可读性。 6. 避免硬编码:检查是否避免了硬编码,即在代码中直接使用固定的数值和字符串,应该使用常量或配置文件来管理这些值。 7. 安全性:检查是否对潜在的安全漏洞进行了预防和处理。例如,是否对用户输入进行了验证和过滤,并避免了SQL注入和跨站脚本攻击等。 8. 性能优化:检查是否有可能的性能优化点,例如避免重复计算和循环、选择合适的数据结构和算法等。 9. 代码复用:检查是否有可能的代码复用机会,例如使用继承、接口和设计模式来避免重复编写相似的代码。 10. 版本控制和代码管理:检查是否遵循正确的代码版本控制和管理,例如对代码进行适当的分支、提交和合并。 通过进行代码review,可以帮助发现并修复潜在的问题,提高代码质量和可维护性,并且促进团队之间的合作和知识共享。 ### 回答2: Java代码review是检查Java程序的代码质量和规范的过程。在进行Java代码review时,我们需要关注以下几个主要的检查项: 1. 代码风格:检查代码的缩进、命名规范、注释等,保证代码易读易懂,符合统一的编码风格。 2. 代码结构:检查代码的模块划分和类的组织结构是否合理,遵循面向对象的设计原则,并且模块之间的依赖关系是否清晰。 3. 可读性:检查代码的逻辑是否清晰,每个方法或函数是否只做一件事,避免过长的方法和复杂的嵌套结构。 4. 异常处理:检查代码中是否捕获和处理了可能出现的异常,避免程序崩溃或者出现不必要的错误。 5. 性能优化:检查代码是否存在性能问题,例如是否存在冗余的计算、不必要的内存占用等,优化代码性能提高程序运行效率。 6. 安全性:检查代码是否存在潜在的安全隐患,例如SQL注入、跨站脚本攻击等,增加代码的安全性。 7. 代码复用性:检查代码是否存在重复代码,避免代码冗余,提高代码复用性和维护性。 8. 单元测试:检查是否存在有效的单元测试覆盖率,保证代码的功能和逻辑正确性。 以上是常见的Java代码review检查项,通过对这些检查项的审核,可以帮助我们提高代码的质量、可维护性和安全性。 ### 回答3: Java代码review是一种对Java程序代码进行审查和评估的过程,旨在发现潜在的问题和改进程序质量。在进行Java代码review时需要关注以下几个检查项: 1. 代码风格:检查代码是否符合约定的编码规范和风格,比如变量命名、缩进、注释等。遵循一致的代码风格可以提高代码的可读性和维护性。 2. 逻辑错误和bug:检查代码中是否存在逻辑错误和潜在的bug,例如条件判断是否正确、循环是否正确终止等。通过预先发现这些错误可以提高程序的可靠性和稳定性。 3. 性能问题:检查代码中是否存在性能瓶颈和低效的操作,比如循环嵌套过多、频繁的对象创建和销毁等。优化这些性能问题可以提高程序的执行效率。 4. 可维护性:检查代码是否易于维护和扩展,例如代码的模块化程度、接口设计是否合理等。具有良好的可维护性可以减少代码维护的难度和成本。 5. 安全性问题:检查代码是否存在安全漏洞和潜在的攻击风险,例如未经验证的输入、SQL注入、跨站脚本攻击等。确保代码的安全性可以保护用户的信息和系统的稳定性。 6. 单元测试覆盖率:检查代码是否具有足够的单元测试覆盖率,确保代码功能的正确性和稳定性。通过编写全面的单元测试可以提高代码的可靠性和可测试性。 7. 文档和注释:检查代码是否有清晰的文档和注释,包括方法和类的功能、输入输出等。良好的文档和注释可以提高代码的可读性和理解性。 通过进行Java代码review,可以发现和纠正代码中的问题,提高代码质量和可靠性,同时也可以加强团队之间的沟通和知识交流。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值