异常就是程序在运行过程中出现的一些错误,我们通过面向对象的思想,把这些错误也用类来描述,那么一旦产生一个错误,即就是创建了某一个错误的对象,这个对象就是我们所说的异常对象。
在这之前也见过很多的异常错误,例如:
(1)IndexOutOfBoundsException:
ArrayIndexOutOfBoundsException
数组角标越界异常 角标不在数组范围内
StringfIndexOutOfBoundsException
字符串角标越界异常 角标不在字符串范围内
(2)NullPointerException
空指针异常 对null调用其成员。
(3)ArithmeticException
数学运算异常 非法的数学运算。
(4)ClassCastException
类型转换异常 将类进行错误的强制转换。
(5)NumberFormatException
数字格式化异常 将数字字符串进行解析。
(6)InputMismatchException
输入不匹配异常 在输入时输入非法数据。
(7)ParseException
时间解析异常 非法的时间格式。
(8)StackOverFlowError
栈内存溢出异常 函数递归。
(9)OutOfMemoryError
堆内存异常 数组空间开辟过大 程序中对象太多。
Error和Exception区别
系统错误(Error)
- 系统錯误(system error) 是由 Java 虚拟机抛出的,用 Error 类表示。Error 类描述的是内部系统错误。这样的错误很少发生。如果发生,除了通知用户以及尽量稳妥地终止程序外,几乎什么也不能做
OutOfMemoryError :内存耗尽 ;
NoClassDefFoundError :无法加载某个Class ;
StackOverflowError :栈溢出。
编译时异常(Exception)
编译时异常:Exception及其子类(除了RuntimeException),在编译时期抛出的异常,在编译期间检查程序是否可能会出现问题,如果可能会有,则预先防范:捕获 声明。
Exception 则是编译时异常,它可以被捕获并处理。
某些异常是应用程序逻辑处理的一部分,应该捕获并处理。例如:
NumberFormatException :数值类型的格式错误;
FileNotFoundException :未找到文件;
SocketException :读取网络失败。
还有一些异常是程序逻辑编写不对造成的,应该修复程序本身。例如:
NullPointerException :对某个 null 的对象调用方法或字段;
IndexOutOfBoundsException :数组索引越界。
运行时异常(RuntimeException)
运行时异常(runtime exception) 是用 RuntimeException 类表示的,它描述的是程序设计错误,例如,错误的类型转换、访问一个越界数组或数值错误。运行时异常通常是由 Java 虚拟机抛出的。
RuntimeException是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类,可能在执行方法期间抛出但未被捕获的 RuntimeException 的任何子类都无需在 throws 子句中进行声明,指的就是这些问题不需要提前被预防(本质上也可以的,只不过没必要),因为只有在真正运行的时候才能发现是否发生问题,一旦在运行期间发生了问题,则一般不会修正,程序直接终端。
RuntimeException、Error 以及它们的子类都称为免检异(unchecked exception )。所有其他异常(编译时异常)都称为必检异常(checked exception), 意思是指编译器会强制程序员检査并通过 try- catch 块处理它们,或者在方法头进行声明。
在大多数情况下,免检异常都会反映出程序设计上不可恢复的逻辑错误。例如,如果通过一个引用变量访问一个对象之前并未将一个对象陚值给它,就会抛出 NullPointerException异常;如果访问一个数组的越界元素,就会抛出IndexOutOfBoundsException 异常。这些都是程序中必须纠正的逻辑错误。免检异常可能在程序的任何一个地方出现。为避免过多地使用 try-catch 块,Java 语言不强制要求编写代码捕获或声明免检异常。
Java规定:
必须捕获的异常,包括 Exception 及其子类,但不包括 RuntimeException 及其子类,这种类型的异常称为Checked Exception。
不需要捕获的异常,包括 Error 及其子类, RuntimeException 及其子类。
运行时异常类
ArithmeticException 算数运算异常,由于除数为0引起的异常;
ClassCastException 类型转换异常,当把一个对象归为某个类,但实际上此对象并不是由这个类创建的,也不是其子类创建的,则会引起异常;
ArrayStoreException 由于数组存储空间不够引起的异常;
NullPointerException 空指针异常,程序试图访问一个空的数组中的元素或访问空的对象中的方法或变量时产生异常;
IndexOutOfBoundsExcention 索引越界异常,由于数组下标越界或字符串访问越界引起异常;
ConcurrentModificationException 并发修改异常;
NoSuchElementException 找不到元素异常;
UnsupportedOperationException 不支持请求异常;(使用Arrays工具类的asList将数组转成集合增加元素时,会报此异常
受检查异常类(非运行类)
受检查异常(Checked Exceptions)是指编译器强制要求处理的异常。这意味着,当一个方法可能抛出受检查异常时,该方法必须通过throws
关键字声明这些异常,或者在方法内部使用try-catch
块来捕获并处理这些异常。
以下是一些Java中常见的受检查异常类:
- IOException - 用于处理I/O操作时发生的异常,如文件读写错误。
- SQLException - 用于处理数据库操作时发生的异常。
- FileNotFoundException - 当尝试打开一个不存在的文件时抛出。
- MalformedURLException - 当URL格式不正确时抛出。
- IndexOutOfBoundsException - 当访问数组或集合的非法索引时抛出。
- NumberFormatException - 当尝试将字符串转换为数字,但字符串不是有效的数字格式时抛出。
- ArithmeticException - 当发生算术错误时抛出,比如除以零。
受检查异常与非受检查异常(Unchecked Exceptions,也称为运行时异常)不同。非受检查异常是编译器不强制要求处理的异常,通常是编程错误导致的,如NullPointerException
或ArrayIndexOutOfBoundsException
。
Java中的异常处理机制要求开发者预见并处理可能发生的受检查异常,以提高程序的健壮性和可靠性。
受检查异常通常是由于外部因素导致的,比如文件操作失败、网络问题等。Java的Exception
类是所有受检查异常的超类。
声明异常
在 Java 中,当前执行的语句必属于某个方法。Java 解释器调用 main 方法开始执行一个程序。每个方法都必须声明它可能抛出的必检异常的类型。这称为声明异常( declaring exception)。只对编译时异常进行声明,、告知方法的调用者有异常。
为了在方法中声明一个异常,就要在方法头中使用关键字 throws, 如下所示:
public void myMethodO throws IOException
1
关键字 throws 表明 myMethod 方法可齙会抛出异常 IOException。如果方法可能会抛出多个异常,就可以在关键字 throws 后添加一个用逗号分隔的异常列表:
public void myMethodO throws Exceptionl, Exception2,… ,ExceptionN
1
注意:如果方法没有在父类中声明异常,那么就不能在子类中对其进行继承来声明异常。
抛出异常
throw与throws用法
检测到错误的程序可以创建一个合适的异常类型的实例并抛出它,这就称为抛出一个异常(throwing an exception)。这里有一个例子,假如程序发现传递给方法的参数与方法的合约不符(例如,方法中的参数必须是非负的,但是传入的是一个负参数),这个程序就可以创建 IllegalArgumentException 的一个实例并抛出它,如下所示:
IllegalArgumentException ex = new II1egalArgumentException("Wrong Argument");
throw ex;
或者,大多数使用下面的语句:
throw new IllegalArgumentException("Wrong Argument");
1
注意:IllegalArgumentException 是 Java API 中的一个异常类。通常,JavaAPI 中的每个异常类至少有两个构造方法:一个无参构造方法和一个带可描述这个异常的 String 参数的构造方法。该参数称为异常消息(exceptionmessage), 它可以用 getMessage()获取。
提示:声明异常的关楗字是 throws, 抛出异常的关键字是 throw。
捕获异常
处理顺序
现在我们知道了如何声明一个异常以及如何抛出一个异常。当抛出一个异常时,可以在try-catch 块中捕获和处理它;
try-catch-finally
try语句块中:放的是可能出现问题的代码,尽量不要把不相关的代码放入到里面,否则会出现截断的问题。
try{
codeA
throw ...
codeB
}
如果throw这个地方一旦抛出异常 codeB就不会执行了 建议把codeB放到后面。
catch语句块中:放的是出现问题并捕获后,处理问题的代码code,如果问题在try语句块中没有出现 则catch中不会运行。
catch(Exception e) {
code
}
finally语句块中:放的是不管问题异常是否产生 都要执行的代码code。
finally{
code//关闭资源(IO 数据库 网络),结尾处理的一些工作
}
处理顺序
- try块:
- 执行
try
块中的代码,如果在执行过程中没有发生异常,那么catch
块将被跳过。
- 执行
- catch块:
- 如果在
try
块中发生了异常,JVM会查找与之匹配的catch
块。如果有多个catch
块,JVM会按照它们在代码中出现的顺序,从上到下查找,直到找到一个可以处理该异常的catch
块。
- 如果在
- 匹配的catch块:
- 当找到匹配的
catch
块时,JVM会执行该块中的代码来处理异常。
- 当找到匹配的
- finally块:
- 不论是否发生异常,
finally
块中的代码都会执行。它通常用于执行清理工作,如关闭文件流、数据库连接等。
- 不论是否发生异常,
- 方法返回或继续执行:
- 如果异常被捕获并处理,方法可能会正常返回或继续执行(取决于异常处理的方式)。如果没有匹配的
catch
块,异常会沿着调用栈向上传播,直到被处理或导致程序终止。
- 如果异常被捕获并处理,方法可能会正常返回或继续执行(取决于异常处理的方式)。如果没有匹配的
异常传播
- 如果当前方法无法处理异常,异常会向上抛到调用栈中的上一个方法。如果上层方法也没有处理,异常会继续向上传播,直到被捕获或导致程序崩溃。
ava中的异常传播主要通过try-catch语句实现。以下是一个简单的示例:
try {
// 尝试执行的代码
int result = 10 / 0; // 这将抛出一个ArithmeticException
} catch (ArithmeticException e) {
// 处理异常的代码
System.out.println("捕获到了一个ArithmeticException: " + e.getMessage());
} catch (Exception e) {
// 处理其他类型的异常
System.out.println("捕获到了一个未知的异常: " + e.getMessage());
}
在这个例子中,我们尝试执行一些代码,其中包含一个会导致ArithmeticException的除法操作。如果这个异常被抛出,它将被第一个匹配的catch块捕获并处理。如果没有任何catch块可以处理这个异常,那么程序将终止。
除了try-catch语句之外,Java还提供了其他一些机制来处理异常传播,例如finally块和throw语句。无论是否捕获异常,finally块中的代码总是会被执行。这可以用于清理资源,例如关闭文件或数据库连接。throw语句用于手动抛出一个异常,这可以用于在代码中创建新的异常或重新抛出已经捕获的异常。
总之,Java中的异常传播是通过try-catch语句实现的,它允许方法将异常传递给调用者,直到找到可以处理它的代码。这对于确保程序的稳定性和可靠性非常重要。
在Java中,异常传播是强制性的。这意味着如果一个方法有可能会抛出异常,那么它必须声明这个异常或者将其传递给调用者。这种做法有助于提高代码的可读性和可维护性,因为调用者可以清楚地看到哪些方法可能会抛出异常以及如何处理这些异常。
自定义异常类的使用步骤如下:
帮助定位问题。
1、自定义异常类继承Exception类
-
自定义异常类 */ public class MyException extends Exception { //异常信息 private String message; //构造函数 public MyException(String message){ super(message); this.message = message; } //获取异常信息,由于构造函数调用了super(message),不用重写此方法 //public String getMessage(){ // return message; //} }