Feb 25&26 2020 异常相关
一、概述
package day0225_1;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/*
* java.lang.Throwable类
* 是所有错误或异常的父类
* 1.Exception :异常
* Exception类有一个重要的子类RuntimeException
* 1) 运行时异常:
* 2) 非运行时异常 (编译异常):
* 2.Error :错误
*/
public class Error1 {
public static void main(String[] args) /*throws ParseException*/ {
// 一、 Exception中的非运行时异常
/* a.第一种处理方式 通过throws抛出
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = sdf.parse("2000-02-07"); // 这句会报错
System.out.println(date);
b.第二种处理方式 try catch
*/
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = sdf.parse("2000-02-07");
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(date);
// 二、 Exception中的运行时异常:RuntimeException
int[] arr = { 1, 2, 3 };
// System.out.println(arr[-10]); // 此时越界了 会报一个索引越界异常错误
// 使用try catch处理异常
try { // try中放置可能会出现异常的代码
System.out.println(arr[-10]);
} catch (Exception e) { // catch中放置异常的处理逻辑
System.out.println(e); // 报错提示为:java.lang.ArrayIndexOutOfBoundsException: -10
System.out.println("出错啦!!");
}
// 三、Error
int[] arr1 = new int[999999999];
// 报错:Java heap space 内存溢出
// 处理的方式只有处理源代码
}
}
try {
// try中放置可能会出现异常的代码
} catch (Exception e) {
// catch中放置异常的处理逻辑
}
二、异常的产生过程
此次不予深究 若有需要以后再研究
三、异常的处理
Java异常处理的五个关键字:try、catch、finally、throw、throws
3.1 throw
throw:可以在指定的方法中抛出指定的异常
- 作用:
可以在指定的方法中抛出指定的异常 - 使用格式:
throw new xxxException(“异常产生原因”) - 注意:
- throw关键字必须写在方法内部
- throw关键字后边new的对象必须是Exception或者Exception的子类对象
- 抛出后的处理规范
- throw抛出指定的异常对象,我们就必须处理这个异常对象
- throw关键字后边创建的是RuntimeException或者是RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异常,中断程序)
- throw关键字后边创建的是非运行时异常,我们就必须处理这个异常,要么throw要么try…catch
1.数组索引相关问题
package day0225_1;
public class Error2 {
public static void main(String[] args) {
int[] arr1 = null;
getElement(arr1, 1);
/*
* 第一组数据:数组非法 参数合法
Exception in thread "main" java.lang.NullPointerException: 数组为null!
at day0225_1.Error2.getElement(Error2.java:27)
at day0225_1.Error2.main(Error2.java:18)
*/
int[] arr2 = new int[3];
getElement(arr2, -5);
/*
* 第二组数据:数组合法 参数非法
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 参数非法!
at day0225_1.Error2.getElement(Error2.java:20)
at day0225_1.Error2.main(Error2.java:8)
*/
int[] arr3 = null;
getElement(arr3, -2);
/*
* 第三组数据:数组和索引参数均非法
此处没有后续参数非法的提示是因为在判断数组为空之后,程序已终止
Exception in thread "main" java.lang.NullPointerException: 数组为null!
at day0225_1.Error2.getElement(Error2.java:27)
at day0225_1.Error2.main(Error2.java:18)
*/
}
private static void getElement(int[] array, int index) {
// 对传递过来的数组array进行合法性校验,如果数组为空则抛出空指针异常,并提示方法调用者
if (array == null) {
throw new NullPointerException("数组为null!");
}
// 对传递过来的参数index进行合法性校验,如果参数非法,则抛出数组索引越界异常,并提示方法调用者
if (index >= array.length || index < 0) {
throw new ArrayIndexOutOfBoundsException("参数非法!");
}
}
}
2.Objects非空判断
package day0225_1;
import java.util.Objects;
public class Error3 {
public static void main(String[] args) {
Object a = null;
demo(a);
}
private static void demo(Object obj) {
// 判断传递过来的对象是否为空
/*手写的方式
if(obj == null) {
throw new NullPointerException("对象为空");
}
*/
// 可以直接调用Objects中的方法进行判断 抛弃手写 简化代码
Objects.requireNonNull(obj,"对象为空"); // Exception in thread "main" java.lang.NullPointerException
}
}
3.2 throws
throws:异常处理的一种方式
- 作用:
- 当方法内部抛出异常的时候,我们就必须处理这个异常
- 使用throws关键字处理异常对象
会把异常对象声明抛出给方法调用者处理
即最终给JVM处理–>中断处理
- 使用格式:
方法声明时使用
修饰符 返回值类型 方法名(参数列表) throws{
throw newAAAException("···");
throw newBBBException("···");
······
}
- 注意:
- throws关键字必须写在方法声明处
- throws关键字后边声明的异常必须是Exception或者是其子类
- 方法内部如果抛出了多个异常对象
那么throws后边必须也声明多个异常
如果抛出多个异常有父子类关系,则只声明父类异常即可 - 调用了一个声明抛出异常的方法,我们就必须处理声明的异常 可以选择交给别人处理或自己处理
- 给方法调用者处理:使用throws声明抛出给方法调用者处理
即最终给JVM处理–>中断处理 - 自己处理异常:try…catch
- 给方法调用者处理:使用throws声明抛出给方法调用者处理
需要注意的是:要尽量使用try…catch自己处理异常 因为交给虚拟机处理则会终止程序
3.2.1 throws 声明异常处理
第一种方式:用throws声明异常交给虚拟机处理
package day0225_1;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
* 需要注意的是 其实需要抛出两个异常:FileNotFoundException和IOException
* 但是因为FileNotFoundException继承于IOException
* 所以仅抛出父类IOException即可
*/
public class Error4 {
public static void main(String[] args) throws IOException {
readFile("d:\\a.txt");
}
//
public static void readFile(String fileName) throws IOException {
if (!fileName.endsWith("txt")) {
throw new IOException("传递的文件后缀不是.txt");
}
if (!fileName.equals("c:\\a.txt")) {
throw new FileNotFoundException("传递的文件路径不是c盘下的a.txt");
}
}
}
3.2.2 try…catch 捕获异常处理
第二种方式:利用try…catch捕获异常自己处理
try {
// try中放置可能会出现异常的代码
} catch (异常类名 变量名) {
// catch中放置异常的处理逻辑
// 一般会把异常信息记录到一个日志中
}
package day0225_1;
import java.io.IOException;
public class Error5 {
public static void main(String[] args) throws IOException {
try {
readFile("d:\\a.jpg");
} catch (IOException ioe) { // try中抛出什么异常对象,catch就会定义什么异常变量来接收
System.out.println("传递的文件后缀不是.txt");
}
System.out.println("后续代码"); // 不管咋样 这句都执行
}
// 这儿的throws是把错误抛给了main方法叫main方法自己处理==
// main方法用try...catch处理掉了异常==
// 所以才有System.out.println("后续代码"); // 不管咋样 这句都执行
public static void readFile(String fileName) throws IOException {
if (!fileName.endsWith("txt")) {
throw new IOException("传递的文件后缀不是.txt");
}
else {
System.out.println("没问题!");
}
}
}
Throw类中的3个异常处理方法
- String getMessage() 返回此 throwable 的简短描述
- String toString() 返回此 throwable 的详细描述
- void printStackTrace() JVM打印异常对象时默认调用此方法,打印的异常信息最为全面
package day0226;
import java.io.IOException;
public class Error1 {
public static void main(String[] args) throws IOException {
try {
readFile("d:\\a.jpg");
} catch (IOException ioe) {
System.out.println(ioe.getMessage());
// 输出:传递的文件后缀不是.txt
System.out.println(ioe.toString());
// 输出:java.io.IOException: 传递的文件后缀不是.txt
System.out.println(ioe);
// 输出:java.io.IOException: 传递的文件后缀不是.txt 默认调用toString
ioe.printStackTrace();
/*
* java.io.IOException: 传递的文件后缀不是.txt
at day0226.Error1.readFile(Error1.java:28)
at day0226.Error1.main(Error1.java:8)
*/
}
}
public static void readFile(String fileName) throws IOException {
if (!fileName.endsWith("txt")) {
throw new IOException("传递的文件后缀不是.txt");
} else {
System.out.println("没问题!");
}
}
}
3.3 finally代码块
- 使用格式:
try {
业务实现代码
} catch (Exception e) {
异常处理块
} finally {
资源回收块
finally块中的代码通常用于资源回收
无论是否出现异常 try和catch是否有 return值 都会执行
}
- 注意事项
- 必须和try一起使用
- 一般用于资源释放(资源回收),无论程序是否异常,最后都要通过finally进行资源的释放
3.4 异常的3种处理方式
- 多个异常的3种处理方式
- 多个异常分别处理
即多组try…catch - 多个异常 一次捕获、多次处理
即一个try对应多个catch
try块中把可能产生异常的语句的都写进去
再写多个catch块处理多种异常- 需要注意的是catch里面定义的异常变量如果有子父类关系,则子类异常必须写在父类异常上边,否则就会报错
- 多个异常 一次捕获、一次处理
即就1组try…catch
- 多个异常分别处理
3.5 finally中有return语句时
简单理解为:同组的try、catch、finally出现时 finally无论如何都会被执行,且总是在最后被执行
所以会产生的效果为:无论try中或者catch中return什么 如果finally中有return语句 则返回的永远是finally中return语句所返回的值
要避免这种情况的发生
package day0226;
public class Error2 {
public static void main(String[] args) {
String arr = test();
System.out.println(arr);
/*
finally返回
java.lang.ArrayIndexOutOfBoundsException: 100
at day0226.Error2.test(Error2.java:12)
at day0226.Error2.main(Error2.java:5)
*/
}
private static String test() {
try {
int[] array = { 0, 0, };
System.out.println(array[100]);
return "try返回";
} catch (Exception e) {
e.printStackTrace();
return "catch返回";
} finally {
return "finally返回";
}
}
}
3.6 子类与父类之间的异常
- 如果父类抛出多个异常,子类重写父类方法时,
- ①可以抛出和父类相同的异常
- ②或是父类异常的子类异常
- ③或不抛出异常
- 如果父类未抛出异常则子类也不可抛出异常
即此时子类若产生异常,则不可声明抛出,只能捕获处理
记住一句话:当爹的要比儿子牛逼
package day0226;
import java.text.ParseException;
public class Father {
public void test1() throws NullPointerException,ClassCastException {}
public void test2() throws IndexOutOfBoundsException{}
public void test3() throws ParseException{}
public void test4() {}
}
class Son extends Father{
// ①相同
public void test1() throws NullPointerException,ClassCastException {}
// ②异常的子类异常
public void test2() throws ArrayIndexOutOfBoundsException{}
// ③不抛出
public void test3() {}
// ④父类未抛出,则子类不可抛出,只能捕获处理
public void test4() {
try {
} catch (Exception e) {
}
}
}
四、自定义异常类
因为java提供的异常类不够实际使用
所以需要自己定义一些异常类
- 格式:
public class XXXexception extends Exception()或者RuntimeException() {
添加一个空参数的构造方法
添加一个带异常信息的构造方法
}
- 注意:
- 一般以Exception结为,说明该类是一个异常类
- 必须继承Exception或者RuntimeException
- 继承Exception:非运行时异常
必须处理,要么throws,要么try…catch - 继承RuntimeException:运行时异常
无需处理,交给虚拟机进行中断处理
- 继承Exception:非运行时异常
4.1自定义异常类的练习
模拟注册操作,如果用户名已存在,则抛出异常并提示:该用户名已被操作
package day0226;
import java.util.Scanner;
/*
* 流程分析:
* 1.使用数组保存已经注册过得用户名
* 2.使用Scanner获取用户输入
* 3.定义方法对用户输入进行判断
* 遍历比较
* 1)相等为true则抛出自定义的异常RegisterException
* 2)不相等为false
*/
public class TestRegisterException {
static String[] usernames = { "AAA", "BBB", "CCC" };
public static void main(String[] args) throws RegisterException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入您的用户名");
String someUsername = sc.next();
checkUsername(someUsername);
}
public static void checkUsername(String username) throws RegisterException {
for (String name : usernames) {
if (name.equals(username)) {
throw new RegisterException("用户名已存在,请重新注册");
}
}
System.out.println("注册成功");
}
}
/*
* 输入BBB时 会输出
* Exception in thread "main" day0226.RegisterException: 用户名已存在,请重新注册
at day0226.TestRegisterException.checkUsername(TestRegisterException.java:28)
at day0226.TestRegisterException.main(TestRegisterException.java:21)
*/
/*
* 输入awfaw时 会输出注册成功
*/
具体参考:https://blog.csdn.net/qq_29229567/article/details/80773970