概念:
异常就是程序出现了不正常的情况,会导致JVM非正常停止,语法错误不算异常体系中
异常体系:
编译时异常&运行时异常:
虚拟机默认处理方式:
- 代码中出现异常,程序会先判断有没有处理异常的代码,如果没有就会让调用者(虚拟机)处理,如果有就会走处理的代码。
- 虚拟机会做两件事情;
- 虚拟机会把异常的名称,错误原因及异常出现的位置等信息输出在控制台
- 程序停止执行,哪里出现异常就在哪里停止,下面的代码都不会再走
throws方式处理异常:
throws:
声明异常,定义在方法括号后面,表示调用该方法会出现异常- 编译时异常必须要进行处理,两种处理方案:try…catch …或者 throws,如果采用 throws 这种方案,在方法上进行显示声明,将来谁调用这个方法谁处理
- 运行时异常因为在运行时才会发生,所以在方法后面可以不写,运行时出现异常默认交给jvm处理
格式:
public void 方法() throws 异常类名 {}
代码演示:
public static void main(String[] args) throws ParseException{
System.out.println("开始");
// method(); // 这里调用者也没有处理异常,最终还是虚拟机处理
method2(); // 这里调用者也没有处理异常,最终还是虚拟机处理
System.out.println("结束");
}
//编译时异常
public static void method2() throws ParseException {
String s = "2048-08-09";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date d = sdf.parse(s);
System.out.println(d);
}
//运行时异常
public static void method() throws ArrayIndexOutOfBoundsException {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
}
throw抛出异常:
throw:
抛出异常对象,定义在方法里面,由方法体内的语句处理,编译时异常因为在编译时就要检查,所以必须要写在方法后面进行显示声明- 这个格式是在方法内的,表示当前代码手动抛出一个异常,下面的代码不用再执行了
格式:
throw new 异常();
public static void main(String[] args) {
//int [] arr = {1,2,3,4,5};
int [] arr = null;
printArr(arr);//就会 接收到一个异常.
//我们还需要自己处理一下异常.
}
private static void printArr(int[] arr) {
if(arr == null){
//调用者知道成功打印了吗?
//System.out.println("参数不能为null");
throw new NullPointerException(); //当参数为null的时候
//手动创建了一个异常对象,抛给了调用者,产生了一个异常
}else{
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
抛出异常使用场景:
- 告诉调用者方法中出现了问题 在方法传递参数有误,没有继续运行下去的意义就要抛出异常,让方法结束
- 有异常不是直接交给JVM去处理的,而是先看看有没有自己处理,没有才去交给JVM
throws和throw的区别:
throws | throw |
---|---|
用在方法声明后面,跟的是异常类名 | 用在方法体内,跟的是异常对象名 |
表示声明异常,调用该方法有可能会出现这样的异常 | 表示手动抛出异常对象,由方法体内的语句处理 |
try-catch方式处理异常:
好处:为了让代码继续往下执行
格式:
try {
可能出现异常的代码;
} catch(异常类名 变量名) {
异常的处理代码;
}
执行流程:
- 程序从 try 里面的代码开始执行
- 出现异常,就会跳转到对应的 catch 里面去执行
- 执行完毕之后,程序还可以继续往下执行
代码演示:
public static void main(String[] args) {
System.out.println("开始");
method();
System.out.println("结束");
}
public static void method() {
try {
int[] arr = {1, 2, 3};
System.out.println(arr[3]);
System.out.println("这里能够访问到吗");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("你访问的数组索引不存在,请回去修改为正确的索引");
}
}
try…catch常见问题:
- try没有遇到问题怎么执行?
会把try中所有的代码全部执行完毕,不会执行catch里面的代码
- try遇到问题, try下面的还会执行吗?
不会,直接跳转到对应的catch语句中,try下面的代码就不会再执行了,当catch里面的语句全部执行完毕,表示整个体系全部执行完全,继续执行下面的代码
- 如果出现的问题没有被捕获,那么程序如何运行?
相当于自己没有处理,默认交给虚拟机处理
- 同时有可能出现多个异常怎么处理?
出现多个异常,就要写多个catch,如果多个异常之间存在子父类关系,那么父类一定要写在下面
- 为什么不直接使用Exception呢?
根据需求,不同的异常会有不同的处理方式,使用Exception就相当于,不管什么异常都统一处理
Throwable成员方法:
方法名 | 说明 |
---|---|
public String getMessage() | 返回此 throwable 的详细消息字符串 |
public String toString() | 返回此可抛出的简短描述 |
public void printStackTrace() | 把异常的错误信息输出在控制台 |
代码演示:
public static void main(String[] args) {
try {
int[] arr = {2, 3, 4};
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace();
} finally {
System.out.println("我是finally,我一定会执行")
}
}
finallly:
- 被finally控制的语句一定会执行,除非JVM退出
- 在try/catch后可以追加finally代码块,其中的代码一定会被执行,通常用于资源回收。
- 如果try中有return会先执行finally中的语句再执行try中的returne
- 如果finally中有returne的,会覆盖其他地方的return。
自定义异常:
为什么要自定义异常?
JDK提供的异常虽然比较多,但是不一定符合我们的需求.,此时我们可以根据自己的业务来定义异常类。
例如年龄负数问题,考试成绩负数问题。
自定义异常的步骤:
- 定义异常类
- 写继承关系
- 提供空参构造
- 提供带参构造
代码演示:
异常类:
public class AgeOutOfBoundsException extends RuntimeException {
public AgeOutOfBoundsException() {}
public AgeOutOfBoundsException(String message){
super(message);
}
}
学生类:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age >= 18 && age <= 25){
this.age = age;
}else{
//自定义异常,指向定义的异常类
throw new AgeOutOfBoundsException("年龄超出了范围");
}
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
测试类:
public class TestStudent {
public static void main(String[] args) {
// 键盘录入学生的姓名和年龄,其中年龄为18 - 25岁,超出这个范围是异常数据不能赋值.需要重新录入,一直录到正确为止
Student s = new Student();
Scanner sc = new Scanner(System.in);
System.out.println("请输入姓名");
String name = sc.nextLine();
s.setName(name);
while (true) {
System.out.println("请输入年龄");
try {
String agetest = sc.nextLine();
int age = Integer.parseInt(agetest);
s.setAge(age);
break;
} catch (NumberFormatException e) {
System.out.println("请输入一个整数");
continue;
} catch (AgeOutOfBoundsException e) {
System.out.println("请输入规定年龄");
continue;
}
}
System.out.println(s);
}
}
Optional:
Optional:可以传递一个为空的的对象,也可以传递一个不为空的
构造方法:
方法名 | 说明 |
---|---|
static Optional of(T value) | 获取一个Optional对象,封装的是非null值的对象 |
static Optional ofNullable(T value) | 获取一个Optional对象,Optional封装的值对象可以是null也可以不是null |
常用方法:
方法名 | 说明 |
---|---|
T get() | 如果存在值,返回值,否则抛出NoSuchElementException |
boolean isPresent() | 如果存在值,则返回true,否则为false |
代码演示:
public static void main(String[] args) {
Student s = null;
Optional<Student> optional = Optional.ofNullable(s);
//如果封装的是一个null,那么通过get方法再次获取会抛出NoSuchElementException。
if (optional.isPresent()) {
Student student = optional.get();
System.out.println(student);
} else {
System.out.println("Optional封装的对象为空");
}
}
处理空指针的方法:
方法名 | 说明 |
---|---|
T orElse(T other) | 如果不为空,则返回具体的值,否则返回参数中的值 |
T orElseGet(Supplier<? extends T> supplier) | 如果不为空,则返回具体的值,否则返回由括号中函数产生的结果 |
void ifPresent (Consumer<? super T> action) | 如果不为空,则使用该值执行给定的操作,否则不执行任何操作 |
void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction) | 如果不为空,则使用该值执行给定的操作,否则执行给定的基于空的操作 |
代码演示:
public static void main(String[] args) {
//method1();
//method2();
//method3();
//method4();
}
private static void method4() {
//Student s = new Student("zhangsan",23);
Student s = null;
Optional<Student> optional = Optional.ofNullable(s);
//public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)、
//如果不为空,则使用该值执行给定的操作,否则执行给定的基于空的操作。
optional.ifPresentOrElse(student -> System.out.println(student),
()->System.out.println("为空了"));
}
private static void method3() {
//Student s = new Student("zhangsan",23);
Student s = null;
Optional<Student> optional = Optional.ofNullable(s);
//ifPresent (Consumer<? super T> action)
//如果不为空,则使用该值执行给定的操作,否则不执行任何操作
optional.ifPresent(student -> System.out.println(student));
}
private static void method2() {
Student s = new Student("zhangsan",23);
//Student s = null;
Optional<Student> optional = Optional.ofNullable(s);
//orElseGet(Supplier<? extends T> supplier)
//如果不为空,则返回具体的值,否则返回由括号中函数产生的结果
Student student = optional.orElseGet(()-> new Student("lisi" , 24));
System.out.println(student);
}
private static void method1() {
//Student s = new Student("zhangsan",23);
Student s = null;
Optional<Student> optional = Optional.ofNullable(s);
//orElse(T other) 如果不为空,则返回具体的值,否则返回参数中的值
Student student = optional.orElse(new Student("lisi", 24));
System.out.println(student);
}
IO异常的处理:
JDK1.7以前的
1.创建流有异常,使用try处理
2.IO流一定要关闭,在finally中来关闭
3.将fis声明放到try的外面
4.fis可能是null,null不能调用方法,需要判断fis!=null,再做close
5.close有异常,再try
6.read方法有异常,添加一个catch分支
public static void test02() {
try (FileInputStream fis = new FileInputStream("day10demo\\abc\\in.txt");
FileOutputStream fos = new FileOutputStream("day10demo\\abc\11.txt");
) {
fis.read(); // 读取数据
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
JDK1.7的处理方式
会自动关流
try (创建流的代码) {
其他代码;
} catch (异常类名 变量名) {
处理异常代码
}
public static void test01() {
FileInputStream fis = null;
try {
fis = new FileInputStream("day10demo\\abc\\in.txt");
fis.read();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}