我的Java基础学习第十二天

    科技2024-12-04  22

    一、异常

    异常:非正常运行

    @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.Error 和 java.lang.Exception

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

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

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

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

    1.1.1 编译时异常

    1.1.2 运行时异常

    空指针异常:NullPointerException:当某个引用为空,同时这个引用调用了这个方法或者属性。在使用某个引用之前先进行空处理,在以后的编码过程中在使用引用变量之前最好是判空处理,这样可以提高程序的健壮性。数组下标超出异常:ArrayIndexOutBoundsException:当通过下标给数组赋值或者取值超过了数组下标的最大值,就会发生该异常;将下标缩减为下标的范围算术异常:ArithmeticException:进行除法或者取模运算时,除数为0,将除数修改为非0即可类型转换异常:ClassCastException:在进行强转之前先用instanceof判断一下,属于这种类型就可以强转数字格式异常: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 类型的异常对象,来表示当前这种异常情况。(空指针异常)

    手动抛出异常

    第一种方式: 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)


    第二种方式:

    注意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 异常捕获

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

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

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

    捕获处理 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 方法结束之前返回值之后执行

    Processed: 0.010, SQL: 8