目录
运行时异常(unchecked,RuntimeException)
1.异常概述与异常体系结构
在开发过程中,即便把代码写的尽善尽美,但在系统运行过程中仍然会遇到一些问题。很多问题不是靠代码就能够避免的。比如:用户输入数据的格式、读取的文件是否存在、网络是否通畅等等。
- 异常:在Java中,将程序执行中发生的不正常情况称为“异常”。
- Java程序在执行过程所发生的异常事件可分为两类:
- Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError(栈溢出)和OOM(堆溢出)。一般不编写针对性的代码进行处理。
- Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以用针对性的代码进行处理。例如:
- 空指针访问
- 试图读取不存在的文件
- 网络连接中断
- 数组下标越界
- 解决方法
- 第一种:遇到错误就终止程序运行。
- 第二种:在编写程序时,考虑到错误的检测、错误消息的提示,以及错误的处理。
- 捕获最理想的是在编译期间,但又的错误只有在运行时才会发生。比如:除数为0,数组下标越界等。
- 分类:编译时异常和运行时异常
2.常见异常
-
编译时异常(checked)
- IOException
- FileNotFoundException
- ClassNotFoundException
- IOException
-
运行时异常(unchecked,RuntimeException)
- NullPointerException 空指针异常
- ArrayIndexOutOfBoundsException 数组下标越界
- ClassCastException 类型转换异常
- NumberFormatException 数字格式化异常
- InputMismatchException
- Arithmeticexception
import java.util.Date;
import java.util.Scanner;
import org.junit.Test;
public class ExceptionTest {
// NullPointerException 空指针异常
@Test
public void test1() {
String str = "abc";
str = null;
System.out.println(str.charAt(0));
}
// ArrayIndexOutOfBoundsException 数组下标越界
@Test
public void test2() {
int[] arr = new int[3];
System.out.println(arr[3]);
}
// ClassCastException 类型转换异常
@Test
public void test3() {
Object obj = new Date();
String str = (String) obj;
}
// NumberFormatException 数字格式化异常
@Test
public void test4() {
String str = "123";
str = "abc";
Integer.parseInt(str);
}
// InputMismatchException 输入不匹配异常
@Test
public void test5() {
Scanner in = new Scanner(System.in);
int score = in.nextInt();
// 输入字符串
System.out.println(score);
}
// Arithmeticexception 算数异常
@Test
public void test6() {
int a = 4;
int b = 0;
// 输入字符串
System.out.println(a / b);
}
}
3.异常处理机制一:try-catch-finally
try {
//可能出现异常的代码
} catch (异常类型1 变量1) {
//处理异常的方式1
} catch (异常类型2 变量2) {
//处理异常的方式2
} catch (异常类型3 变量3) {
//处理异常的方式3
}
finally {
//一定会执行的代码
}
try-catch:
- 使用try将可能出现异常的代码包装起来,在执行过程中,一单出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。
- 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出try-catch结构(在没有写finally的情况下),继续执行其后的代码。
- catch中的异常类型如果没有子父类关系,则声明的上下顺序无所谓;如果存在子父类关系,则要求子类一定在上面,否则报错。
- 常用的异常对象处理的方式:1.String getMessage() 2.printStackTrace()
- 在try结构中声明的变量,在出了try结构之后,不能再被调用。
finally:
- finally是可选的。
- finally 中声明的是一定会被执行的代码。即使catch中又出现异常、try中有return、catch中有return的情况。且在执行return、throw前,先执行finally。
- 像数据库连接、输入输出、网络编程Sokect等资源,JVM是不能自动回收的,我们需要自己手动进行资源的释放,此时需要将资源释放声明在finally里。
面试题:final、finally、finalize三者的区别
final | finally | finalize | |
区别 | 最终。 修饰类不能被继承 修饰方法不能被重写 修饰属性表示常量,不能被重新赋值 | 处理异常时一定会运行的部分。 | 是Object里面的一个方法。 当一个堆空间中的对象没有被栈空间变量指向的时候,这个对象会等待被java回收。 |
体会:
- 使用try-catch-finally结构处理编译时异常,使得程序在编译时就不再报错,但运行时仍可能报错。相当于使用try-catch-finally讲一个编译时可能出现的异常,延迟到运行时才出现。
- 针对编译时异常,一定要考虑异常的处理。但由于运行时异常比较常见,所以通常不对运行时异常编写try-catch-finally。
package com.ch.exceptionDemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.junit.Test;
public class TryCatchExceptionTest {
@Test
public void test1() {
FileInputStream fis = null;
try {
File file = new File("hello.txt");
fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.print((char) data);
data = fis.read();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (fis != null) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4.异常处理机制二:throws+异常类型
throws:
- 写在方法的声明出。指明次方法执行时,可能会抛出的异常类型。一旦方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后面的代码,就不再执行。
体会:
- try-catch-finally:真正将异常处理掉。
- throws:将异常抛给方法的调用者,并没有真正的把异常处理掉。
package com.ch.exceptionDemo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ThrowsExceptionTest {
public void method2() throws IOException {
method();
}
public void method() throws FileNotFoundException, IOException {
FileInputStream fis = null;
File file = new File("hello.txt");
fis = new FileInputStream(file);
int data = fis.read();
while (data != -1) {
System.out.print((char) data);
data = fis.read();
}
}
}
throws方法重写:
- 子类重写的方法抛出的异常<=被重写的方法抛出的异常。
package com.ch.exceptionDemo;
import java.io.FileNotFoundException;
import java.io.IOException;
public class OverrideTest {
public static void main(String[] args) {
OverrideTest overridetest = new OverrideTest();
SubClass subclass = new SubClass();
overridetest.show(subclass);
}
public void show(SuperClass s) {
try {
s.method();
} catch (IOException e) {
e.printStackTrace();
}
}
}
class SuperClass {
public void method() throws IOException {
}
}
class SubClass extends SuperClass {
@Override
public void method() throws FileNotFoundException {
}
}
实际中选择:
- 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws。意味着如果子类重写的方法中有异常,必须使用try-catch-finally处理。
- 执行的方法中,先后又调用了另外的几个方法。这几个方法是递进关系执行的,建议几个被调方法使用throws的方式进行处理,而执行的方法可以使用try-catch-finally处理。
5.手动抛出异常:throw
public class ThrowTest {
public static void main(String[] args) {
Student s = new Student();
try {
s.regist(-1001);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(s);
}
}
class Student {
private int id;
public void regist(int id) throws Exception {
if (id > 0) {
this.id = id;
} else {
// System.out.println("输入id有误");
// 手动抛出异常
// throw new RuntimeException("输入id有误");
throw new Exception("输入id有误");
}
}
@Override
public String toString() {
return "Student [id=" + id + "]";
}
}
6.用户自定义异常类
如何自定义异常类?
- 继承于现有的异常结构:RuntimeException(运行时异常,可以不考虑处理)、Exception(考虑处理)
- 提供全局常量:serialVersionUID
- 提供重载的构造器。
7.面试题:throw 跟 throws的区别
throw | throws | |
阶段 | 异常的生成阶段(抛) | 异常的处理方式(抓) |
内容 | 手动抛出异常对象 | 声明方法可能要抛出的异常类,将异常抛给方法的调用者,让调用者去具体处理。 |