Java 异常的处理 throw关键字 Objects非空判断requireNonNull 异常处理的第一种方式:throws 异常处理的第二种方式:try catch Throwable类中3个异常处理的方法 finally代码块 异常注意事项:多异常的捕获处理 异常注意事项:finally有return语句 子父类异常 自定义异常类的简单练习
作用
可以使用throw关键字在指定的方法中抛出指定的异常
使用格式
throw new xxxException(“异常产生的原因“)
注意
- throw关键字必须写在方法的内部
- throw关键字后边new的对象必须是 Exception或者 Exception的子类对象
- throw关键字抛出指定的异常对象,我们就必须处理这个异常对象。
- throw关键字后边创建的是 RuntimeException或者是RuntimeException的子类对象,我们可以不处理,默认交给JW处理(打印异常对象,中断程序)
-
throw关键字后边创建的是编译异常,我们就必须处理这个异常,要么 throws,要么try...catch
举例
例子1:
public class DemoThrow { public static void main(String[] args) { int[] array = {}; int element = DemoThrow.getElement(array, 1); } public static int getElement(int[] array, int index) { if (array.length == 0) { throw new NullPointerException("传入的数组为空"); } else if (index == 0 || index > array.length - 1) { throw new ArrayIndexOutOfBoundsException("传递的索引超出了数组的适用范围"); } else { return array[index]; } } }
抛出错误: Exception in thread "main" java.lang.NullPointerException: 传入的数组为空 at XXX.DemoThrow.getElement(DemoThrow.java:10) at XXX.DemoThrow.main(DemoThrow.java:5)
例子2:
public class DemoThrow { public static void main(String[] args) { int[] array = {1, 2, 3}; int element = DemoThrow.getElement(array, 100); } public static int getElement(int[] array, int index) { if (array.length == 0) { throw new NullPointerException("传入的数组为空"); } else if (index == 0 || index > array.length - 1) { throw new ArrayIndexOutOfBoundsException("传递的索引超出了数组的适用范围"); } else { return array[index]; } } }
抛出错误: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 传递的索引超出了数组的适用范围 at XXX.DemoThrow.getElement(DemoThrow.java:12) at XXX.DemoThrow.main(DemoThrow.java:5)
Objects非空判断requireNonNull
java.util.Objects类是由一些静态实用方法组成,这些方法是空指针安全的(即:容忍空指针的)
在源码中对对象null值进行了抛出异常错误
源码如下: // 查看指定对象时不时null public static <T> T requireNonNull(T obj) { if (obj == null) throw new NullPointerException(); return obj; }
requireNonNull方法的使用
import java.util.Objects; public class DemoObjectsNonNull { public static void main(String[] args) { String s = null; Objects.requireNonNull(s, "传入的参数为空"); } }
抛出错误: Exception in thread "main" java.lang.NullPointerException: 传入的参数为空 at java.util.Objects.requireNonNull(Objects.java:228) at XXX.DemoObjectsNonNull.main(DemoObjectsNonNull.java:6)
自定义非空判断
public class DemoObjectsNonNull { public static void main(String[] args) { String s = null; methodNonNull(s); } public static <E> void methodNonNull(E e) { if (e == null) { throw new NullPointerException("传入的参数为空"); } } }
抛出错误: Exception in thread "main" java.lang.NullPointerException: 传入的参数为空 at XXX.DemoObjectsNonNull.methodNonNull(DemoObjectsNonNull.java:9) at XXX.DemoObjectsNonNull.main(DemoObjectsNonNull.java:5)
异常处理的第一种方式:throws
throws作用
当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象。可以使用 throws关键字处理异常对象,它会把异常对象声明抛给方法的调用者处理(自己不处理,绐别人处理)最终交给JVM-->中断处理
使用格式
修饰符 返回值类型 方法名(参数列表) throws AaaException, BbbException ... { throw new AaaException("产生原因"); throw new BbbException("产生原因"); ... }
注意
- throws关键字必须写在方法声明处。
- throws关键字后边声明的异常必须是Exception或者是Exception的子类。
- 方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常。如果批出的多个异常对象有子父类关系,那么直接声明父类异常即可
- 调用了一个声明抛出异常的方法,我们就必须处理声明的异常,要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM,要么try...catch自己处理异常。
举例
例子1:
import java.io.FileNotFoundException; public class Demo01Throws { public static void main(String[] args) throws FileNotFoundException{ String s = "/Users/data.txt"; readFile(s); } public static void readFile(String s) throws FileNotFoundException { if ("/Users/data.txt".equals(s)) { System.out.println("传入的参数是'/Users/data.txt'"); } else { throw new FileNotFoundException("传入的参数不是'/Users/data.txt'"); } } }
输出结果:
传入的参数是'/Users/data.txt'
例子2:
import java.io.FileNotFoundException; public class Demo01Throws { public static void main(String[] args) throws FileNotFoundException{ String s = "/Users/data"; readFile(s); } public static void readFile(String s) throws FileNotFoundException { if ("/Users/data.txt".equals(s)) { System.out.println("传入的参数是'/Users/data.txt'"); } else { throw new FileNotFoundException("传入的参数不是'/Users/data.txt'"); } } }
抛出错误: Exception in thread "main" java.io.FileNotFoundException: 传入的参数不是'/Users/data.txt' at XXX.Demo01Throws.readFile(Demo01Throws.java:11) at XXX.Demo01Throws.main(Demo01Throws.java:6)
异常处理的第二种方式:try catch
使用格式
格式: try { ... } catch (异常类名 变量名) { ... } ... catch(异常类名 变量名) { ... } ...
注意
- try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象。
- 如果try中产生了异常,那么就会执行ctch中的异常处理逻辑,执行完catch中的处理逻辑,会继续执行try...catch之后的代码。
- 如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try...catch之后的代码。
举例
public class Demo01TryCatch { public static void main(String[] args) { try { String[] strings = {}; System.out.println(strings[100]); } catch (ArrayIndexOutOfBoundsException e) { // 数组索引越界异常 System.out.println("Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException"); } catch (NullPointerException e) { // 空指针异常 System.out.println("Exception in thread "main" java.lang.NullPointerException"); } System.out.println("程序执行完成"); } }
输出结果: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException 程序执行完成
Throwable类中3个异常处理的方法
public void printStackTrace():打印异常的跟踪栈信息,并输出到控制台。包含了异常的类型,异常的原因,还包括异常出现的位置。在开发和调试阶段都得使用printStackTrace。 public String getMessage():获取发生异常的原因。提示给用户时候就提示误原因。 public String toString():获取异常的类型和异常描述信息。
printStackTrace()方法源码
public void printStackTrace() { printStackTrace(System.err); }
getMessage()方法源码
public String getMessage() { return detailMessage; }
toString()方法源码
public String toString() { String s = getClass().getName(); String message = getLocalizedMessage(); return (message != null) ? (s + ": " + message) : s; }
三个方法举例
public class DemoTryCatch { public static void main(String[] args) { try { String[] strings = new String[10]; getElement(strings, 10); } catch (ArrayIndexOutOfBoundsException e) { // 获取发生异常的原因 System.out.println(e.getMessage()); // 获取异常的类型和异常描述信息 System.out.println(e.toString()); // 获取栈的跟踪信息(异常的类型、异常的原因、异常出现的位置) e.printStackTrace(); } } public static String getElement(String[] array, int index) { if (array.length == 0) { throw new NullPointerException("传入的数组为空"); } else if (index == 0 || index > array.length - 1) { throw new ArrayIndexOutOfBoundsException("传递的索引超出了数组的适用范围"); } else { return array[index]; } } }
控制台输出: 传递的索引超出了数组的适用范围 java.lang.ArrayIndexOutOfBoundsException: 传递的索引超出了数组的适用范围 java.lang.ArrayIndexOutOfBoundsException: 传递的索引超出了数组的适用范围 at XXX.DemoTryCatch.getElement(DemoTryCatch.java:19) at XXX.DemoTryCatch.main(DemoTryCatch.java:5)
finally代码块
格式
try { ... } catch (异常类名 变量名) { ... } ... catch(异常类名 变量名) { ... } finally { ... }
注意
-
finally不能单独使用,必须要和try一起使用
-
finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要释放IO
举例
public class DemoFinally { public static void main(String[] args) { try { String[] strings = {}; System.out.println(strings[100]); } catch (ArrayIndexOutOfBoundsException e) { // 获取异常的类型和异常描述信息 System.out.println(e.toString()); } finally { System.out.println("程序执行完成"); } } }
控制台输出: java.lang.ArrayIndexOutOfBoundsException: 100 程序执行完成
异常注意事项:多异常的捕获处理
多个异常分别处理
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Demo01ManyException { public static void main(String[] args) { try { int[] array = {1, 2, 3}; int a = array[3]; } catch (ArrayIndexOutOfBoundsException e) { System.out.println(e.toString()); } try { List<Integer> list = new ArrayList<>(); Collections.addAll(list, 1, 2, 3); int b = list.get(3); } catch (IndexOutOfBoundsException e) { System.out.println(e.toString()); } } }
控制台输出: java.lang.ArrayIndexOutOfBoundsException: 3 java.lang.IndexOutOfBoundsException: Index: 3, Size: 3
多个异常一次捕获多次处理
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Demo01ManyException { public static void main(String[] args) { try { int[] array = {1, 2, 3}; int a = array[3]; List<Integer> list = new ArrayList<>(); Collections.addAll(list, 1, 2, 3); int b = list.get(3); } catch (ArrayIndexOutOfBoundsException e) { System.out.println(e.toString()); } catch (IndexOutOfBoundsException e) { System.out.println(e.toString()); } } }
控制台输出:
java.lang.ArrayIndexOutOfBoundsException: 3
注意:一个try,多个catch,catch里面定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在前面,否则会报错
如:
多个异常一次捕获一次处理
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Demo01ManyException { public static void main(String[] args) { try { int[] array = {1, 2, 3}; int a = array[3]; List<Integer> list = new ArrayList<>(); Collections.addAll(list, 1, 2, 3); int b = list.get(3); } catch (Exception e) { System.out.println(e.toString()); } } }
控制台输出:
java.lang.ArrayIndexOutOfBoundsException: 3
异常注意事项:finally有return语句
如果finally中有return语句,只会返回finally中的值。
如:
public class Demo01FinallyReturn { public static void main(String[] args) { System.out.println(Demo01FinallyReturn.method()); } public static String method() { try { String s = "abc"; return s; } catch (Exception e) { e.printStackTrace(); } finally { String s = "ABC"; return s; } } }
控制台输出:
ABC
子父类异常
如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异常。
父类方法没有抛出异常,子类重写父类该方法时也不可抛出异常。此时子类产生该异常,只能捕获处理,不能声明抛出。
如:
public class Fu { public static void method1() throws NullPointerException, ClassCastException { } public static void method2() throws IndexOutOfBoundsException { } public static void method3() { } public static void method4() { } }
public class Zi extends Fu { /** * 子类重写父类方法时,抛出和父类相同的异常 * @throws NullPointerException 空指针异常 * @throws ClassCastException 类强制转换异常 */ public static void method1() throws NullPointerException, ClassCastException { } /** * 子类重写父类方法时,抛出父类异常的子类 * @throws ArrayIndexOutOfBoundsException 索引越界异常 */ public static void method2() throws ArrayIndexOutOfBoundsException { } /** * 子类重写父类方法时,不抛出异常 */ public static void method3() { } /** * 父类方法没有抛出异常,子类重写父类方法时,也不可以抛出异常 * 只能捕获处理,不能声明抛出 */ public static void method4(){ try { throw new Exception("编译期异常"); } catch (Exception e) { e.printStackTrace(); } } }
自定义异常类的简单练习
要求
模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。
分析
1.使用数组保存已经注册过的用户名 2.使用Scanner获取用户输入的注册的用户名 3.定义一个方法,对用户输入中的注册用户名进行判断,遍历存储已经注册过用户名的数组,获取每一个用户名,
使用获取到的用户名和用户输入的用户名比较 true:用户名已经存在,抛出 RuntimeException异常,告知用户"亲,该用户名已经被注册" false:继续遍历比较。如果循环结束了,还没有找到重复的用户名,提示用户“恭喜您注册成功!"。
实现
import java.util.Scanner; public class MyUsersException { private static String[] names = {"小红", "小明", "李华", "小军", "大雄"}; public static void registerUser(String scannerUser) { for (String name : names) { if (name.equals(scannerUser)) { try { throw new RuntimeException("亲,该用户名已被注册!"); } catch (RuntimeException e) { System.out.println(e.getMessage()); return; } } } System.out.println("恭喜您注册成功!"); } public static void main(String[] args) throws RuntimeException{ Scanner scn = new Scanner(System.in); System.out.print("输入要注册的用户名:"); String registerUserName = scn.next(); MyUsersException.registerUser(registerUserName); } }
控制台输入:李华
控制台输出:
亲,该用户名已被注册!
控制台输入:静香
控制台输出:
恭喜您注册成功!