异常处理
异常
在Java中,异常(Exception)是指程序执行过程中可能出现的不正常情况或错误。它是一个事件,会干扰程序的正常执行流程,并可能导致程序出现错误或崩溃。
异常体系
异常的根类是java.lang.Throwable
,Java提供的所有异常类均继承自此类,其下有两个子类:java.lang.Error
与java.lang.Exception
,平常所说的异常指java.lang.Exception
。
Throwable体系:
- Error:严重错误Error,无法通过处理的错误,只能事先避免,好比绝症。
- 例如:
StackOverflowError
、OutOfMemoryError
。
- 例如:
- Exception(编译时异常):表示异常,其它因编程错误或偶然的外在因素导致的一般性问题,程序员可以通过相应预防处理措施,使程序发生异常后还可以继续运行。好比感冒、阑尾炎。
- 例如:空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界
Exception
是我们通常说的异常和异常处理。Exception异常又通常分两大类:
运行期异常(unchecked Exception):这类异常的发生多数是因为程序员编写的代码逻辑不够严谨造成的(如数组脚标越界异常),可以选择进行处理或不处理,最好是通过修正、优化代码避免异常的发生(或者使用异常处理简化复杂的逻辑判断代码)。
@Test
public void test01(){
//NullPointerException
int[] arr=null;
System.out.println(arr.length);
}
@Test
public void test02(){
//ClassCastException
Person p = new Man();
Woman w = (Woman) p;
}
@Test
public void test03(){
//ArrayIndexOutOfBoundsException
int[] arr = new int[5];
for (int i = 1; i <= 5; i++) {
System.out.println(arr[i]);
}
}
@Test
public void test04(){
//InputMismatchException
Scanner input = new Scanner(System.in);
System.out.print("请输入一个整数:");
int num = input.nextInt();
}
@Test
public void test05(){
int a = 1;
int b = 0;
//ArithmeticException
System.out.println(a/b);
}
编译期异常(checked Exception):这类异常一般由程序之外的因素引起的(如程序读取的文件不存在、网络中断),而不是程序员写的代码逻辑有问题,所以程序员容易忽略对这类异常的处理,而恰恰这类异常又很常发生,所以Java要求针对这类可能发生的异常必须进行处理,否则编译无法通过。(只有java语言有需强制处理的异常)
@Test
public void test06() throws InterruptedException{
Thread.sleep(1000);//休眠1秒
}
@Test
public void test07() throws FileNotFoundException{
FileInputStream fis = new FileInputStream("Java学习秘籍.txt");
}
@Test
public void test08() throws SQLException{
Connection conn = DriverManager.getConnection("....");
}
异常的生成与抛出机制throw
Java程序的执行过程中如出现异常,会生成一个异常类对象,然后该异常对象会被提交给Java运行时系统,这个过程称为抛出(throw)异常。异常对象的生成与抛出有两种方式:
1.虚拟机自动生成
由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,就会在后台自动创建一个对应异常类的实例对象并自动抛出。
// 工具类
public class ArrayTools {
// 对给定的数组通过给定的角标获取元素。
public static int getElement(int[] arr, int index) {
int element = arr[index];
return element;
}
}
// 测试类
public class ExceptionDemo {
public static void main(String[] args) {
int[] arr = { 34, 5, 67 };
intnum = ArrayTools.getElement(arr, 4)
System.out.println("num=" + num);
System.out.println("over");
}
}
/*
一旦发生了异常 就会产生一个异常对象 当前方法是否对异常进行了处理 如果没有处理则沿着方法的调用关系逐级向上抛出,直到抛给jvm;
抛给了jvm 1.结束/终止程序 2.打印异常的详细信息 异常的类型 异常的原因 异常的抛出流程
*/
2.由开发人员手动创建
由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样,手动创建的异常对象需要手动抛出,才会对程序产生影响。
在Java中,使用关throw关键字手动抛出一个异常对象,throw用在方法内,将这个异常对象传递到方法调用者处,同时结束当前方法的执行。
使用格式:
throw new 异常类名(参数);
例如:
throw new NullPointerException("要访问的arr数组不存在");
throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");
throw的使用示例:
public class ThrowDemo {
public static void main(String[] args) {
//创建一个数组
int[] arr = {2,4,52,2};
//根据索引找对应的元素
int index = 4;
int element = getElement(arr, index);
System.out.println(element);
System.out.println("over");
}
/*
* 根据 索引找到数组中对应的元素
*/
public static int getElement(int[] arr,int index){
if(arr == null){
/*
判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。
*/
throw new NullPointerException("要访问的arr数组不存在");
}
//判断 索引是否越界
if(index<0 || index>arr.length-1){
/*
判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。
这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。
*/
throw new ArrayIndexOutOfBoundsException("哥们,角标越界了~~~");
}
int element = arr[index];
return element;
}
}
注意:如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。
那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续将问题声明出去,使用throws声明处理。
异常的处理机制
如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理。如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。这一过程称为捕获(catch)异常。
捕获异常try…catch
捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。
try{
// 编写可能会出现异常的代码-->出现异常就会执行catch的语句--不会再往下执行了
}catch(异常类型1 e){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}catch(异常类型2 e){
处理异常的代码
//记录日志/打印异常信息/继续抛出异常
}catch(异常类型1 | 异常类型2 e){
//记录日志/打印异常信息/继续抛出异常
}
....
try:捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。建议:此范围尽量小。
try中声明的变量只能在try的语句块内使用 其他位置想要使用必须进行作用域提升
catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常。
- 可以有多个catch块,按顺序匹配。
- 如果多个异常类型有包含关系,那么小上大下
- 可以使用一个catch 捕获多个异常 不能与Exception同时存在
获取异常信息:
捕获到了异常对象,就可以获取异常对象中封装的异常信息,Throwable类中定义了一些方法用于获取异常对象中的信息:
public String getMessage()
:获取异常的描述信息。public void printStackTrace()
:打印异常的跟踪栈信息并输出到控制台。这些信息包含了异常的类型,异常信息,还包括异常出现的位置,在开发和调试阶段,建议使用printStackTrace。
finally块
finally:在finally代码块中存放的代码都是一定会被执行的。由于异常会引发程序跳转,导致后面有些语句执行不到,如果一定要执行这些语句就可以使用finally,finally常用于释放系统资源。
finally的语法:
try{
}catch(...){
}finally{
无论try中是否发生异常,也无论catch是否捕获异常,也不管try和catch中是否有return语句,都一定会执行
}
或
try{
}finally{
无论try中是否发生异常,也不管try中是否有return语句,都一定会执行。
}
注意:finally不能单独使用。
当只有在try或者catch中调用退出JVM的相关方法,例如System.exit(0),此时finally才不会执行,否则finally永远会执行。
声明异常throws
throws:用在方法上,表明此方法可能会产生的异常类型。
如果在某方法内通过抛出了必须要处理的编译期异常,有两种选择:要么在当前方法进行捕获处理,要么通过throws在当前方法上进行声明,让方法的调用者去处理。
声明异常格式:
修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ }
import java.io.File;
import java.io.FileNotFoundException;
public class TestException {
public static void main(String[] args) throws FileNotFoundException {
readFile("不敲代码学会Java秘籍.txt");
}
// 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明
public static void readFile(String filePath) throws FileNotFoundException{
File file = new File(filePath);
if(!file.exists()){
throw new FileNotFoundException(filePath+"文件不存在");
}
}
}
throw和throws区别
throw 在方法内抛出一个异常对象
throws 在方法的声明处 声明1个或多个异常类型
自定义异常类
(1)要继承一个异常类型 自定义一个编译时异常类型:自定义类继承java.lang.Exception。
自定义一个运行时异常类型:自定义类继承java.lang.RuntimeException。
public class IndexOutOfBoundsException extends RuntimeException{
//异常信息
private String message;
public IndexOutOfBoundsException() {
}
//构造函数
public IndexOutOfBoundsException(String message){
super(message);
}
}
(2)建议大家提供至少两个构造器,一个是无参构造,一个是(String message)构造器。
public class IndexOutOfBoundsException extends RuntimeException{
//异常信息
private String message;
public IndexOutOfBoundsException() {
}
//构造函数
public IndexOutOfBoundsException(String message){
super(message);
}
}
(3)自定义异常需要提供serialVersionUID
public class IndexOutOfBoundsException extends RuntimeException{
//异常信息
private String message;
static final long serialVersionUID = 23423423435L;
public IndexOutOfBoundsException() {
}
//构造函数
public IndexOutOfBoundsException(String message){
super(message);
}
}
(4)使用自定义异常类
public void setAge(int age) {
if(age<=0||age>120){
throw new IndexOutOfBoundsException("超龄了!!!");
}
this.age = age;
}
@org.junit.Test
public void test01(){
UseMyException 蔡徐坤 = new UseMyException("蔡徐坤", 25);
蔡徐坤.setAge(250);
}
注意点
自定义的异常只能通过throw抛出。
自定义异常最重要的是异常类的名字和message属性。当异常出现时,可以根据名字 判断异常类型。比如:TeamException("成员已满,无法添加");、 TeamException("该员工已是某团队成员");
自定义异常对象只能手动抛出。抛出后由try..catch处理,也可以甩锅throws给调用 者处理。
clone方法和java.lang.Cloneable接口
所有类型都可以重写这个方法,它是获取一个对象的克隆体对象用的,就是造一个和当前对象各种属性值一模一样的对象。当然地址肯定不同。
1.让要克隆的对象所在的类实现接口Cloneable
2.重写Object clone()
3.让要克隆的对象调用此方法即可
public class CloneTest {
}
class Person implements Cloneable{
String name;
int age;
//重写clone方法因为默认时protected修饰的
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
/**
test
*/
public class CloneTest {
@Test
public void test01() throws CloneNotSupportedException {
Person person = new Person();
person.name="cxk";
System.out.println(person.name);
//克隆person
Object clone = person.clone();
//向下转换
Person person1 =(Person) clone;
person1.name="cxk2";
System.out.println(person1.name);
System.out.println(person.name);
}
}