我的Java基础学习第十二天

一、异常

异常:非正常运行
在这里插入图片描述
在这里插入图片描述

@Test
	public void testHappenException() {
		int[] arr = {1,2,3,4,5};
		for (int i = 1; i <= 20; i++) {
			if (i == 15) {
				// ArrayIndexOutOfBoundsException
				int j = arr[100]; // 数组越界
			}
			System.out.println("正常:"+ i);
		}
	}

在这里插入图片描述

可以看出,当前程序出现异常情况时,会创建并抛出和该异常情况对应的异常类的对象,这个异常对象中保存了一些信息,用来表示当前程序到底发生了什么异常情况。
通过异常信息,我们可以定位异常发生的位置,以及异常发生的原因

1.1 异常体制

异常体系中的根类是:java.lang.Throwable,该类下面有两个子类型,java.lang.Errorjava.lang.Exception

在这里插入图片描述

在这里插入图片描述

  • Error,表示错误情况,一般是程序中出现了比较严重的问题,并且程序自身并无法进行处理。
  • Exception,表示异常情况,程序中出了这种异常,大多是可以通过特定的方式进行处理和纠正的,并且处理完了之后,程序还可以继续往下正常运行。

注意,我们一般说的异常,都是指的Exception

Exception中并没有定义方法,它的方法都是从Throwable中继承过来的,其中常用的方式有:

  • printStackTrace(),打印输出当前发送异常的详细信息(重要
  • getMessage(),获取异常信息;返回异常对象抛出是携带的信息,一般是异常的发生原因(重要
  • printStackTrace(PrintWriter s),方法重载,可以指定字符输出流,对异常信息进行输出
  • printStackTrace(PrintStream s),方法重载,可以指定字节输出流,对异常信息进行输出
    在这里插入图片描述

1.1.1 编译时异常

在这里插入图片描述

1.1.2 运行时异常

在这里插入图片描述

  1. 空指针异常:NullPointerException:当某个引用为空,同时这个引用调用了这个方法或者属性。在使用某个引用之前先进行空处理,在以后的编码过程中在使用引用变量之前最好是判空处理,这样可以提高程序的健壮性。
  2. 数组下标超出异常:ArrayIndexOutBoundsException:当通过下标给数组赋值或者取值超过了数组下标的最大值,就会发生该异常;将下标缩减为下标的范围
  3. 算术异常:ArithmeticException:进行除法或者取模运算时,除数为0,将除数修改为非0即可
  4. 类型转换异常:ClassCastException:在进行强转之前先用instanceof判断一下,属于这种类型就可以强转
  5. 数字格式异常:NumberFormatException:将字符串转成数字发生了异常,将字符串改成数字字符传即可。
Integer.parseInt("aaa");

1.2 传播异常

如果一个方法中抛出了异常,并且一直没有进行处理,那么这个异常将会抛给当前方法的调用者,并一直向上抛出直到抛给JVM,最后JVM将这个异常信息打印输出,同时程序运行的停止。

public class Test {

    public static void main(String[] args) {
        System.out.println("hello");
        test1();
        System.out.println("world");
    }

    public static void test1(){
        test2();
    }
    public static void test2(){
        test3();
    }
    public static void test3(){
        int a = 1/0;
    }

}

//运行结果:
hello
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at com.lzyy.demo.Test.test3(Test.java:16)
	at com.lzyy.demo.Test.test2(Test.java:13)
	at com.lzyy.demo.Test.test1(Test.java:10)
	at com.lzyy.demo.Test.main(Test.java:5)

可以看出,test3方法中抛出了异常,因为是运行时异常,编译器不检查,这个异常在程序中没有处理,那么这个异常就抛给了test2,test2方法又将异常抛给了test1方法,test1中又将异常抛给main方法,main方法将异常抛给JVM,最后JVM将当前发生异常时,栈内存中方法的调用情况,打印输出,方便我们定位错误信息!之后JVM停止运行。

如果,在异常传播的过程中,任何一个地方对异常进行了处理,那么JVM不会停止,程序还会正常往下运行!

1.3 异常抛出

  • 自动抛出异常
    例如,当代码中执行 int a = 1/0;的时候,代码会自动创建并抛出 ArithmeticException类型的异常对象,来表示当前的这种异常情况。(算术异常)

    例如,当前代码中执行 String str = null; str.toString(); 的时候,代码会自动创建并抛出 NullPointerException 类型的异常对象,来表示当前这种异常情况。(空指针异常)

  • 手动抛出异常

  1. 第一种方式:
public class Test {
	
	public void test(String name) {
		if (!"tom".equals(name)) {
			throw new RuntimeException();
		}
	}
	
	public static void main(String[] args) {
		new Test().test("lisa");
	}

}

注意,以为方法中抛出的异常是编译异常,编译器会做检查,所以代码编译会报错,提示我们需要在test方法上声明出方法内抛出异常的类型,或者在方法内对这个异常进行处理!

Exception in thread “main” java.lang.RuntimeException
at com.lzyy.test.Test.test(Test.java:7)
at com.lzyy.test.Test.main(Test.java:12)


  1. 第二种方式:

注意throws

public class Test {
	
	public void test(String name) throws Exception{
		if (!"tom".equals(name)) {
			throw new RuntimeException();
		}
	}
	
	public static void main(String[] args) throws Exception {
		new Test().test("lisa");
	}
}

使用throws关键字,声明方法所抛出的异常类型即可
这个声明的目的,就是告诉test方法的调用者,你调用我的这个test方法的时候要小心啦,方法在运行的时候可能会抛出Exception类型的异常
这里描述为可能会抛出异常的原因是,只有name的值不是tom的时候才会抛出异常,其他情况没有异常!

Exception in thread “main” java.lang.RuntimeException
at com.lzyy.test.Test.test(Test.java:13)
at com.lzyy.test.Test.main(Test.java:18)

方法内抛出异常对象使用关键字throw,方法上声明异常使用的是throws

1.4 异常捕获

当一个方法内,抛出了编译异常的时候,编译器在编译期间检查到,就会报错,提示我们两种修改方式:

  • 把这个异常在方法上进行声明抛出
  • 把这个异常再方法内进行捕获处理
  1. 声明抛出
public void test(String className)throws ClassNotFoundException{
    
    Class.forName(className);
    
}

将来谁调用test方法,谁就要处理这个异常情况

  1. 捕获处理
public void test(String className){
    try {
        Class.forName(className);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

这里使用了try-catch语句块,对可能抛出异常的代码进行异常捕获处理

1.4.1 try-catch

try-catch语句块,就是用来对指定代码,进行异常捕获处理,并且处理完成后,JVM不会停止运行,代码还可以正常的往下运行!

try:该代码块中编写可能产生异常的代码。
catch:用来进行某种异常的捕获,并对捕获到的异常进行处理。

使用 | 来表示捕获多种不同的异常类型

catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
        e.printStackTrace();
    }

其实使用一个Exception就可以了 【 getMessage() : 获取异常信息】

其中:

public static void main(String[] args) {


    String className = "com.briup.demo.Student";
    String methodName = "sayHello";

    try {
        //forName声明抛出ClassNotFoundException
        Class c = Class.forName(className);
        //getMethod方法声明抛出NoSuchMethodException
        Method m = c.getMethod(methodName);
        //invoke方法声明抛出IllegalAccessException和InvocationTargetException
        m.invoke(null);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }

}

这里,使用了四个catch语句,分别对四种不同的异常类型进行捕获处理

注意,这种异常处理方式,要求多个catch中的异常不能相同,并且如果catch中的多个异常之间有子父类异常的关系的话,
那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。
因为如果父类型异常再最上面的话,下面catch语句代码,永远不会被执行

1.4.2 finall语句

try-catch语句块,虽然可以捕获并处理异常情况,但是它也会改变代码的执行流程,例如:

public class Test {

    public static void main(String[] args) {
        System.out.println("hello");

        Test t = new Test();

        try {
            t.test("zs"); // 9
            System.out.println("你好");//注意观察,这句代码是否执行 10
        } catch (Exception e) { // 11
            e.printStackTrace();
        }
        System.out.println("world");
    }

    public void test(String name)throws Exception{
        if(!"tom".equals(name)){
            throw new Exception("用户名不正确");
        }
    }

}
//运行结果:
hello
world
java.lang.Exception: 用户名不正确
	at com.lzyy.demo.Test.test(Test.java:20)
	at com.lzyy.demo.Test.main(Test.java:9)

可以看出,以上代码中的第10行,并没有执行,因为第9行代码调用方法出现异常时,代码就从第9行跳到了11行的catch语句块,刚好把11行的输出语句代码给跳过去了。
其中,只要使用finally关键字,就可以保证指定代码一定会执行,无论是否发生异常!

思考,以下代码中的finally中的代码是否会被执行

public static void main(String[] args) {
    System.out.println("hello");

    try {
        int a = 1;
        if(a>0){
            return; //结束该方法
        }
    } finally {
        System.out.println("你好");//是否会被执行? 会
    }

    System.out.println("world");
}

hello
你好

该方法在finally 方法结束之前返回值之后执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值