异常的概念
在java中,将程序执行过程中发生的不正常的情况称之为异常。(开发过程中的语法错误或者逻辑错误不是异常哦)
在执行过程中发生的异常有两大类:
- Error :java虚拟机(JVM)无法解决的严重问题,程序会崩溃。
- Exception :其他编程错误或者偶然的外在因素导致的一般性问题,可以使用针对性代码进行出解决处理。
Exception又可以分为两大类:
- 运行时异常(RuntimeException) :程序运行过程中发生的异常。编译器不会进行检查,一般时程序的逻辑错误,可不作异常处理。(有默认处理)
- 编译时异常(CompiletimeException):一般叫做CheckedException(检查异常),即编译器检查出的一异常。必须要求进行处理,可以理解为在编写代码时出现的红色下划线。
异常结构体系图
所有的异常类是从 java.lang.Exception 类继承的子类。Exception 类是 Throwable 类的子类。下图只是列举了一些常见的异常类型。
常见的运行时异常类型
NullPointerException (空指针异常)
public static void main(String[] args) {
String name = null;
System.out.println(name.length());
}
当处理对象是Null时,会产生的报错,例如在例子中,name的值为空,但是在输入中却要访问name字符串的长度,就会报出NullPointerException错误。
ArrayIndexOutOfBoundsException (数组下标越界异常)
public static void main(String[] args) {
int [] array = {2,57,32,4,6};
for(int i = 0 ; i <=5 ;i++)
System.out.println(array[i]);
}
当访问数组的下标超过数组本身的容量时,就会报出ArrayIndexOutOfBoundsException异常,在例子中,数组array的下标索引的最大值是4,但是在for循环中i是可以取到5的,所以会发生数组下标越界异常。
ClassCastException (类型转换异常)
public static void main(String[] args) {
//创建一个classA实例对象
classA a = new classB(); //向上转型,默认允许的。
classA b = (classB)a; //向下转型,是允许的。
classC c = (classC)a; //抛出ClassCastException
}
//类classA
class classA {
}
//B类,继承自classA
class classB extends classA{
}
//C类,继承自classA
class classC extends classA{
}
当试图将对象强制转换为不是实例的子类时,就会抛出ClassCastException异常。在例子中 classA a = new classB()是向上转型,是被默认、允许的。classA b = (classB)a 是向下转型,使用了(classB)强制转型,是允许的。但是 classC c = (classC)a时就会抛出ClassCastException异常。
NumberFormatException (数字格式不正确异常)
public static void main(String[] args) {
String name = "中华民族的伟大复兴";
int num = Integer.parseInt(name);
}
当在非数字型字符串进行转换成数字时,会抛出的异常。在例子中,因为字符串时中文汉字,而不是数字型的,所以在进行运行时会抛出NumberFormatException异常。
ArithmeticException (算术异常)
public static void main(String[] args) {
int num1 = 10;
int num2 = 0;
System.out.println(num1 / num2);
}
当出现异常的运算条件时,抛出此异常。在例子中,num1 / num2 的除数num2都是0了,所以在执行程序时会抛出ArithmeticException异常。
常见的编译异常
SQLException : 操作数据库时、查询表时可能发生的异常。
IOException : 操作文件时发生的异常。
FileNotFoundException : 当一个文件找不到(不存在)时发生的异常。
ClassNotFoundException : 加载类,但是类并不存在时发生的异常。
EOFException : 操作文件,到文件末尾时发生的异常。
IllegalArguementException : 非法参数异常。
异常处理
概念:当发生异常时,对异常进行的处理方法,称为异常处理。
对异常的处理通常有两种方式:
- try -- catch -- finally
在代码中捕获发生的异常,自行进行处理。
- throws
将发生的异常抛出,交给调用者(方法)来进行处理,可以一层一层向上抛出,最顶级的调用者时JVM(Java虚拟机)。
try -- catch --finally
语法格式:
try{
//可能有异常抛出的代码
}catch( ExceptionName e ){
//捕获到异常,系统将异常封装成Exception 对象e ,传递给catch块,得到异常对象e,程序员可以自行进行异常的处理。
//如果try块中没有异常抛出,catch块中的代码不执行。
//ExceptionName是异常的类型名称。
}finally{
//不管catch块中代码块是否有异常发生,始终都要执行finally语句。
//使用场景:通常将关闭资源的代码放在finally中。
}
try --catch --finally异常处理的细节
- 如果异常发生了,则异常发生后面的代码不会执行,直接进入catch块。
- 如果异常没有发生,则继续执行try块中代码,不会进入到catch块中。
- 不管是否有异常发生,finally块中语句都会被执行。
- 可以有多个不同的catch块,捕获不同的异常(进行不同的业务处理),但是有着要求:父类异常类型在后,子类异常在前。如果发生异常,只匹配一个catch块中语句进行执行。
- 可以进行try -- finally配合使用,该用法相当于无异常捕获,因为程序会崩溃。 应用场景:执行一段代码,不管是否会发生异常,都要执行的某个任务逻辑。
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}finally{
// 程序代码
}
//注意事项:
//1、异常类型2一定是异常类型1的父类,不然会报错。
//父子类关系参考上述异常结构体系图。
throws
基本介绍
1、如果某种方法中的语句执行,可能会造成某种异常,但并不确定如何处理该异常,则在此方法声明出显式的抛出异常,表明该方法将不对这些异常进行处理,而是该方法的调用者(一般也是方法)负责处理。
2、在方法声明中使用throws关键字可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可是产生的异常类型的父类。
throws异常处理机制:(throws和try--catch--fainally两种方法处理异常时,二选一。)
如果经过层层抛出,到达JVM时,就会输出信息,终端程序。
throws异常处理细节
- 对于编译异常,程序中必须进行处理,比如使用try--catch--finally或者throws进行处理。
- 对于运行时异常,程序中如果没有进行异常处理,默认采用throws处理方式进行处理。
- 子类重写父类方法时,对抛出的异常的规定:子类重写父类的方法所抛出的异常类型要么和父类抛出的异常类型相同,要么为父类抛出异常的类型的子类型。
- 在使用throws过程中,如果有方法使用可try--catch--finally方法,就相当于进行了异常处理,就可以不必使用throws了。
try--with--resource
JDK7 之后,Java 新增的 try-with-resource 语法来打开资源,并且可以在语句执行完毕后确保每个资源都被自动关闭 。
try-with-resources 是一种异常处理机制,它可以简化资源管理代码的编写。
JDK7 之前所有被打开的系统资源,比如流、文件或者 Socket 连接等,都需要被开发者手动关闭,否则将会造成资源泄露。
import java.io.*;
public class TryWithResourcesExample {
public static void main(String[] args) {
try (
// 声明并初始化资源
FileInputStream fileInputStream = new FileInputStream("example.txt");
FileOutputStream fileOutputStream = new FileOutputStream("copy.txt")
) {
// 使用资源
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fileInputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这个例子中,我们使用 try-with-resources 来自动关闭 FileInputStream 和 FileOutputStream。这些资源在
try 代码块执行完毕后自动关闭,或者在发生异常时也会被关闭。
try-with-resources 的一个关键优势是它能够处理多个资源。如果需要关闭多个资源,只需在 try 语句的括号内用分号分隔它们即可。如下所示:
try (
Resource1 res1 = new Resource1();
Resource2 res2 = new Resource2();
// ...
) {
// 使用资源
}
自定义异常
概念
当程序中出现了某些“错误”但是这些错误信息并没有在Throwable子类中描述处理,这个时候就可以进行自行设计异常类,用于描述错误信息。
自定义异常的要点
1、定义一个类,自定义异常类名,继承 Exception 类或者继承RuntimeException 类。
2、如果是继承Exception类,那么自定义的异常类就属于编译时异常类。
3、如果是继承RuntimeException类,那么自定义的异常类就属于运行时异常类。
4、咱们在自定义异常类时,一般时继承RuntimeException类,原因时RuntimeException类在进行异常处理时,如果没有显式的写明异常处理,就会默认使用throws进行处理,比较方便。
举例说明:接收一个Person对象年龄时,要求范围在18~120之间,否则便抛出一个自定义异常(继承自RuntimeException类),并给出提示信息。
public static void main(String[] args) {
System.out.println("请输入您的年龄:(18-120岁)");
Scanner input = new Scanner(System.in);
int age = input.nextInt();
if(age >=18 && age <=120)
System.out.println("您输入的年龄时:" + age);
else
throw new AgeException("您输入的年龄不符合要求!");
}
//自定义异常类
class AgeException extends RuntimeException{
public AgeException (String message) {
super(message);
}
}
正确输入年龄的运行结果:
错误输入年龄的运行结果:
throw 和 throws 的区别
区别一览表
意义 | 位置 | 后面跟的东西 | |
throw | 异常处理的一种 | 方法声明处 | 异常类型 |
throws | 手动生成异常对象关键字 | 方法体中 | 异常对象实例 |