异 常

本文探讨了异常处理的设计原则,包括异常的正确使用场景、如何通过状态测试方法避免异常、何时使用运行时异常及如何进行异常转译。此外还介绍了自定义业务异常的重要性,并强调了文档化异常以及保证操作原子性的必要性。
a>:异常只应该被用于不正常的条件,它们永远不应该被用于正常的控制流.

b>:一个设计良好的API不应该强迫它的客户为了正常的控制流而使用异常。如Iterator<T>, next()状态方法,hasNext()状态测试方法.
pubilc interface Iterator<T> {
public boolean hasNext();
public T next();
public void remove();
}
"状态测试方法"和"可被识别的返回值"这两种做法选择的指导原则.

c>:用运行时异常来指明程序错误.也就是说,检查应该由调用方来完成,而调用方未保证条件的正确性,刚导致异常的出现,此时应该
使用RuntimeException.
对于可恢复的使用Exception,它也相当于一个方法的除了正常返回值的第二个返回值.
异常要表达:出了什么错误,在那儿出错了,出错的信息收集,建议的解决办法.
public IndexOutOfBoundsException(int lowerBound,int upperBound,int index) {
//记录发生异常时的状态值.
super("Lower bound: " + lowerBound + ", Upper bound: " + upperBound + ", Index: " + index);
}


d>:异常转译,出现频率最高的:将Exception转换为RuntimeException抛出.
高层的实现应该捕获低层的异常,同时抛出一个可以按照高层抽象进行解释的异常,这种做法被称为异常转译(exception translation).
尽管异常转译比不加选择地传递低层异常的做法有所改进,但是它也不能被滥用。如果可能的话,处理来自低层的最好做法是,在
调用低层方法之前确保它们会成功执行,从而避免它们会抛出异常。有时候,你可以在给低层传递实参之前,显式地检查这些实参
的有效性,从而避免低层方法会抛出异常, 其次是让高层来处理这些异常,从而将高层方法的调用者与低层的问题隔离开.
如果既不能阻止来自低层的异常,也无法将它们与高层隔离开,那么一般的做法是使用异常转译。只有在低层方法的规范碰巧可以
保证“它所抛出的异常对于高层也是合适的”情况下,才可以将异常从低层传播到高层.


e>:业务处理中可能要定义一套自己的业务异常.
但尽量使用标准的异常.
IllegalArgumentException 参数的值不合适
IllegalStateException 对于这个方法调用而言,对象状态不合适.
NullPointerException 在null被禁止的情况下参数值为null
IndexOutOfBoundException 下标超界
ConcurrentModificationException 在禁止并发修改的情况下,对象检测到并发修改
UnsupportedOperationException 对象不支持客户请求的方法

ArithmeticException
NumberFormatException


f>:每个方法抛出的异常都要有文档.
一但方法可能抛出异常,不管是Exception还是RuntimeException,都需要在doc中进行描述,特别是RuntimeException
对于一个方法可能抛出的未被检查的异常,如果将这些异常信息很好地组织成一个列表文档,则可以有效地描述出这个方法
被成功执行的前提条件。

g>:失败的原子性.
不可变对象自动保证失败的原子性.
对于在可变对象上执行操作的方法,获得失败原子性最常见的办法是,在执行操作之前检查参数的有效性。这可以使得在对象
的状态被修改之前,适当的异常首先被抛出来.
public Object pop() {
if (size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;//Eliminate obsolete reference
return result;
}
如果初始的大小(size)检查被处掉的话,当这个方法企图从一个empty stack 中弹出元素时,它仍然会抛出一个异常。然而,这将会
导致size域保持在不一致的状态(负数)中,从而使得将来对该对象的任何方法调用都会失败.而且,那时候,pop方法抛出的异常对于
该stack抽象来说也是不恰当的.

另一种类似的获得失败原子性的办法是,对计算处理过程调整顺序,使得任何可能会失败的计算部分都发生在对象状态被修改之前.
在向TreeMap中添加一个元素,该元素的类型必须可能过TreeMap的排序准则与其它的元素进行比较,企图增加一个类型不正确的
元素将导致ClassCastException异常.这实质上是上一种办法的变体.都是在真正修改对象状态之前,抛出异常.
第三种办法也一段恢复代码.解释操作过程中发生的失败,以及使对象回滚到操作开始之前的状态上.
最后一种办法就是在对象一份临时拷贝上执行操作,当操作完成之后再把临时拷贝中的结果复制给原来的对象.这样做的原因
可能是出于性能的考虑,也有一个附加好处,即使失败了,对象的状态仍保持原样.
总结一条规则:作为方法规范的一部分,任何一个异常都不应该改变对象调用该方法之前的状态.如果这条规则被违反,则API
文档应该清楚地指明对象将会处于什么样的状态。
### 运行时异常(RuntimeException)实例 - **空指针异常(NullPointerException)**:当试图在一个 `null` 对象上调用方法或访问属性时会抛出该异常。 ```java public class NullPointerExceptionExample { public static void main(String[] args) { String str = null; // 这里会抛出 NullPointerException System.out.println(str.length()); } } ``` - **数组越界异常(ArrayIndexOutOfBoundsException)**:当访问数组时,使用的索引超出了数组的有效范围会抛出该异常。 ```java public class ArrayIndexOutOfBoundsExceptionExample { public static void main(String[] args) { int[] arr = new int[5]; // 这里会抛出 ArrayIndexOutOfBoundsException System.out.println(arr[10]); } } ``` - **类型转换异常(ClassCastException)**:当试图将一个对象强制转换为不兼容的类型时会抛出该异常。 ```java public class ClassCastExceptionExample { public static void main(String[] args) { Object obj = new Integer(10); // 这里会抛出 ClassCastException String str = (String) obj; } } ``` - **算术异常(ArithmeticException)**:当进行数学运算时,如果出现不合法的操作,如除以零,会抛出该异常。 ```java public class ArithmeticExceptionExample { public static void main(String[] args) { int a = 10; int b = 0; // 这里会抛出 ArithmeticException int result = a / b; } } ``` ### 编译时异常(Checked Exception)实例 - **文件未找到异常(FileNotFoundException)**:当试图打开一个不存在的文件时会抛出该异常。 ```java import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; public class FileNotFoundExceptionExample { public static void main(String[] args) { try { File file = new File("nonexistent.txt"); FileInputStream fis = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } } } ``` - **输入输出异常(IOException)**:在进行输入输出操作时,如果发生错误会抛出该异常,它是许多输入输出相关异常的父类。 ```java import java.io.FileReader; import java.io.IOException; public class IOExceptionExample { public static void main(String[] args) { try { FileReader fr = new FileReader("test.txt"); int data = fr.read(); while (data != -1) { System.out.print((char) data); data = fr.read(); } fr.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` - **SQL异常(SQLException)**:在使用 JDBC 进行数据库操作时,如果发生数据库相关的错误会抛出该异常。 ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class SQLExceptionExample { public static void main(String[] args) { try { Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "password"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM non_existent_table"); } catch (SQLException e) { e.printStackTrace(); } } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值