持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第9天,点击查看活动详情
一、异常的概述和分类
1. 异常的概述
异常就是Java程序在运行过程中出现的错误。
2. 异常的分类
a. 严重问题: Error
不予处理,因为这种问题一般是很严重的问题,如:栈溢出(StackOverflowError)和内存溢出(OOM)。
b. 非严重问题(异常): Exception
可以针对性进行处理,分为编译时异常和运行时异常,如:空指针、类型转换错误。
二、编译期异常和运行期异常的区别
Java中的异常被分为两大类:编译时异常(非RuntimeException)和运行时异常(RuntimeException)。 所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常。
1. 编译时异常:
Java程序必须显示处理,否则程序就会发生错误,无法通过编译。
2. 运行时异常
Java程序无需显示处理,也可以和编译时异常一样处理。
三、Error和Exception代码示例
1. Error
a. java.lang.StackOverflowError(栈溢出)
public class Test {
public static void main(String[] args) {
//栈溢出:java.lang.StackOverflowError
main(args);
}
}
b. java.lang.OutOfMemoryError(堆溢出)
public class Test {
public static void main(String[] args) {
//堆溢出:java.lang.OutOfMemoryError
Long[] arr = new Long[1024*1024*1024];
}
}
2. Exception
a. 运行时异常
//NullPointerException
@Test
public void test1(){
int[] arr = null;
System.out.println(arr[1]);
}
//ClassCastException
@Test
public void test2(){
Object obj = new Boolean(true);
String str = (String)obj;
}
b. 编译时异常
@Test
public void test3(){
File file = new File("a.txt");
//java.io.FileNotFoundException
FileInputStream fis = new FileInputStream(file);
//java.io.IOException
int date = fis.read();
while (date != -1){
System.out.println((char)date);
date = fis.read();
}
fis.close();
}
三、异常处理方式
1. 异常处理的两种方式
a. try…catch…finally
b. throws
2. try...catch和try-catch-finally处理异常的基本格式
a. try...catch
//try...catch
//处理一个异常
try {
//可能出现问题的代码
}catch(异常类型 变量名){
//针对问题的处理
}
//处理多个异常
try{
//可能出现异常的代码
}catch(异常类型1 变量名1){
//处理异常的方式1
}catch(异常类型2 变量名2){
//处理异常的方式2
}catch(异常类型3 变量名3){
//处理异常的方式3
}
注意事项:
a. try中的代码越少越好
b. catch中要做处理,哪怕是一条输出语句也可以(不能将异常信息隐藏)。
b. try-catch-finally
try{
//可能出现异常的代码
}catch(异常类型 变量名){
//针对问题的处理
}finally{
//一定会执行的代码
}
注意事项:
a. 能明确的尽量明确,不要用大的来处理。
b. 平级关系的异常谁前谁后无所谓,如果出现了子父关系,父必须在后面。
3. throws处理异常
定义功能方法时,需要把出现的问题暴露出来让调用者去处理,那么就通过throws在方法上标识。
public class Test {
public static void main(String[] args) {
try{
test();
// 数组下标越界异常
}catch(IndexOutOfBoundsException e){
e.printStackTrace();
}
}
public static void test() throws IndexOutOfBoundsException{
Byte[] bytes = new Byte[3];
System.out.println(bytes[4]);
}
}
四、throw的概述以及和throws的区别
1. throw的概述
在功能方法内部出现某种情况,程序不能继续运行,需要进 行跳转时,就用throw把异常对象抛出。
代码示例:
public class Test {
public static void main(String[] args) {
try{
test();
// 数组下标越界异常
}catch(IndexOutOfBoundsException e){
e.printStackTrace();
}
}
public static void test(){
try {
Byte[] bytes = new Byte[3];
System.out.println(bytes[4]);
// 数组下标越界异常
}catch(IndexOutOfBoundsException e){
throw new IndexOutOfBoundsException();
}
}
}
2. throws和throw的区别
a. throws
- 用在方法声明后面,跟的是异常类名
- 可以跟多个异常类名,用逗号隔开
- 表示抛出异常,由该方法的调用者来处理
- throws表示出现异常的一种可能性,并不一定会发生这些异常
b. throw
- 用在方法体内,跟的是异常对象名
- 只能抛出一个异常对象名
- 表示抛出异常,由方法体内的语句处理
- throw则是抛出了异常,执行throw则一定抛出了某种异常
五、自定义异常
1. 自定义异常类步骤
- 继承于现有的异常结构:RuntimeException 、Exception
- 提供全局常量:serialVersionUID
- 提供重载的构造器
2. 代码示例
自定义异常类
public class MyException extends Exception {
static final long serialVersionUID = -5641210210148784L;
public MyException() {
}
public MyException(String message) {
super(message);
}
}
手动抛出自定义异常
public class Test {
public static void main(String[] args) {
try {
testException();
} catch (MyException myException) {
myException.printStackTrace();
}
}
public static void testException() throws MyException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入大于0的数据:");
double next = scanner.nextDouble();
if (next > 0) {
System.out.println("您输入的数据为:" + next);
} else {
throw new MyException("您输入的数据不满足要求!");
}
}
}
六、异常处理执行顺序
1. 除以下4种情况外,都会执行finally语句
- 在try 或catch语句中执行了System.exit(0)
- 在执行finally之前jvm崩溃
- try语句中执行死循环
- 电脑宕机
2. finally语句执行原则
a. 不论有没有出现异常,finally块中代码都会执行
//无异常
public class Test {
public static void main(String[] args) {
try {
System.out.println("try...");
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
System.out.println("finally...");
}
}
}
执行结果:
如果未出现异常,顺序执行try、finally代码块。
//有异常
public class Test {
public static void main(String[] args) {
try {
System.out.println("try...");
Byte[] bytes = new Byte[3];
System.out.println(bytes[4]);
} catch (Exception e) {
System.out.println("exception...");
} finally {
System.out.println("finally...");
}
}
}
执行结果:
如果有异常,则try中异常后的代码将不执行,再顺序执行catch、finally代码块。
b. 当try和catch中有return时,finally仍然会执行
public class Test {
public static void main(String[] args) {
int i = test();
System.out.println("retuen:"+i);
}
public static int test() {
try {
System.out.println("try...");
return 0;
} catch (Exception e) {
System.out.println("exception...");
return 1;
} finally {
System.out.println("finally...");
}
}
}
执行结果:
当finally里面没有return语句时,执行try、finally语句之后最后再执行return。
c. finally是在return后面的代码运算后执行的
public class Test {
public static void main(String[] args) {
int i = test();
System.out.println("retuen:"+i);
}
public static int test() {
int i = 0;
try {
i = 2;
System.out.println("try...");
return i;
} catch (Exception e) {
System.out.println("exception...");
return i;
} finally {
i = 12;
System.out.println("finally...");
}
}
}
执行结果:
d. finally中最好不要包含return,否则程序会提前退出,不会再返回try或catch中保存的返回值
public class Test {
public static void main(String[] args) {
int i = test();
System.out.println("retuen:" + i);
}
public static int test() {
int i = 0;
try {
System.out.println("try...");
return i;
}catch (Exception e) {
System.out.println("exception...");
return i;
} finally {
i = 2;
System.out.println("finally...");
return i;
}
}
}
执行结果:
在程序还未执行try中的return语句时就先执行了finally里面的return语句
七、JVM默认如何处理异常
main函数收到这个问题时,有两种处理方式:一是自己将该问题处理,然后继续运行; 二是自己没有针对的处理方式,只有交给调用main的jvm来处理。
jvm有一个默认的异常处理机制,就将该异常进行处理,并将该异常的名称、异常的信息、异常出现的位置打印在了控制台上,同时将程序停止运行。