Java的异常处理
1、异常的概念
- 概念:程序在运行过程中出现的不正常现象。出现异常不处理将终止程序运行。
- 异常处理的必要性:任何程序都可以存在大量的未知问题、错误;如果不对这些问题进行正确处理,则可能导致程序的中断,造成不必要的损失。
- 异常处理:
Java
编程语言使用异常处理机制为程序提供了异常处理的能力。
2、异常的分类
Throwable
:可抛出的,一切错误或异常的父类,位于java.lang
包中。
Error
:JVM
、硬件、执行逻辑错误,不能手动处理。
StackOverflowError
:如果线程的堆栈大小超出分配的内存限制,比如:递归没有被结束。OutOfMemoryError
:内存不足
Exception
:程序在运行和配置中产生的问题,可处理。
RuntimeException
:运行时异常,可处理,可不处理。CheckedException
:检查时异常,必须处理。(还没运行就报错,显示红色波浪线)
3、异常的产生
- 当程序在运行时遇到不符合规范的代码或结果时,会产生异常或程序员使用
throw
关键字手动抛出异常。
4、异常的传递
- 异常的传递:按照方法的调用链反向传递,如始终没有处理异常,最终会由
JVM
进行默认异常处理(打印堆栈跟踪信息,并中断程序运行)。
4.1 案例(一)
package com.goshawk.chapter_03.section_1;
import java.util.Scanner;
public class Demo2 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int num1 = input.nextInt();
System.out.println("请输入第二个数字:");
int num2 = input.nextInt();
int result = num1/num2;
System.out.println("结果:"+result);
System.out.println("程序执行完毕");
}
}
package com.goshawk.chapter_03.section_1;
import java.util.Scanner;
public class Demo2 {
public static void main(String[] args) {
operation();
}
public static void operation(){
System.out.println("#######operation##########");
device();
}
public static void device(){
Scanner input = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int num1 = input.nextInt();
System.out.println("请输入第二个数字:");
int num2 = input.nextInt();
int result = num1/num2;
System.out.println("结果:"+result);
System.out.println("程序执行完毕");
}
}
5、异常的处理
Java
的异常处理是通过5个关键字来实现的:
try
:执行可能产生异常的代码catch
:捕捉异常,并处理finally
:无论是否发生异常,代码总能执行throw
:手动抛出异常throws
:声明方法可能要抛出的各种异常。
5.1 异常处理:try ... catch
try{
} catch (Exception e) {
}
- 会三种情况:
Mac
快捷键:command+option+t
Windows
快捷键:Alt+shift+z
5.1.1 案例
package com.goshawk.chapter_03.section_1;
import java.util.Scanner;
public class Demo3 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int result = 0;
try {
System.out.println("请输入第一个数字:");
int num1 = input.nextInt();
System.out.println("请输入第二个数字:");
int num2 = input.nextInt();
result = num1/num2;
} catch (Exception e) {
System.out.println(e.getMessage());
}
System.out.println("结果:"+result);
System.out.println("程序执行完毕");
}
}
5.2 异常处理:try ... catch ... finally
try {
] catch(Exception e) {
} finally {
}
finally
块是否发生异常都执行,可以释放资源。finally
块不执行的唯一情况,退出java
虚拟机。
5.2.1 案例
package com.goshawk.chapter_03.section_1;
import java.util.Scanner;
public class Demo4 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int result = 0;
try {
System.out.println("请输入第一个数字:");
int num1 = input.nextInt();
System.out.println("请输入第二个数字:");
int num2 = input.nextInt();
result = num1/num2;
System.exit(0);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
System.out.println("释放资源...");
}
System.out.println("结果:"+result);
System.out.println("程序执行完毕");
}
}
5.3 异常处理:多重 catch
try {
} catch (异常类型1) {
} catch (异常类型2) {
} catch (异常类型3) {
}
- 子类异常在前,父类异常在后
- 发生异常时按顺序逐个匹配
- 只执行第一个与异常类型匹配的
catch
语句 finally
根据需要可写或不写
5.3.1 案例
package com.goshawk.chapter_03.section_1;
import java.util.InputMismatchException;
import java.util.Scanner;
public class Demo5 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
int result = 0;
try {
System.out.println("请输入第一个数字:");
int num1 = input.nextInt();
System.out.println("请输入第二个数字:");
int num2 = input.nextInt();
result = num1/num2;
} catch (ArithmeticException e) {
System.out.println("算术异常");
} catch (InputMismatchException e) {
System.out.println("输入不匹配异常");
} catch (Exception e) {
System.out.println("未知异常");
}
System.out.println("结果:"+result);
System.out.println("程序执行完毕");
}
}
5.4 异常处理:try ... finally
try ... finally
不能捕获异常,仅仅用来当发生异常时,释放资源。- 一般用在底层代码,只释放资源不做异常处理,把异常向上抛出。
try {
} finally {
}
5.4.1 案例
package com.goshawk.chapter_03.section_1;
import java.util.Scanner;
public class Demo6 {
public static void main(String[] args) {
try {
device();
} catch (Exception e) {
System.out.println("出现异常:" + e.getMessage());
}
}
public static void device(){
Scanner input = new Scanner(System.in);
int result = 0;
try {
System.out.println("请输入第一个数字:");
int num1 = input.nextInt();
System.out.println("请输入第二个数字:");
int num2 = input.nextInt();
result = num1/num2;
} finally {
System.out.println("不能处理异常,可以释放资源,把异常向上抛出,如果不处理程序中断");
}
System.out.println("结果:"+result);
System.out.println("程序执行完毕");
}
}
5.5 异常处理的总结
try {} catch {}
try {} catch {} finally {}
try {} catch {} catch {}
try {} finally {}
- 注意:
- 多重
catch
,遵循从子(小)到父(大)的顺序,父类异常在最后。
6、声明异常
- 如果在一个方法体中抛出异常,如何通知调用者?
throws
关键字:声明异常。- 使用原则:底层代码向上声明或者抛出异常,最上层一定要处理异常,否则程序中断。
6.1 案例(一)
- 使用
throws Exception
强制调用者要处理异常
package com.goshawk.chapter_03.section_1;
import java.util.Scanner;
public class Demo7 {
public static void main(String[] args) {
try {
device();
} catch (Exception e) {
System.out.println("出现异常:" + e.getMessage());
}
}
public static void device() throws Exception {
Scanner input = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int num1 = input.nextInt();
System.out.println("请输入第二个数字:");
int num2 = input.nextInt();
int result = num1 / num2;
System.out.println("结果:"+result);
}
}
6.2 案例(二)
package com.goshawk.chapter_03.section_1;
import java.util.Scanner;
public class Demo7 {
public static void main(String[] args) throws Exception {
device();
}
public static void device() throws Exception {
Scanner input = new Scanner(System.in);
System.out.println("请输入第一个数字:");
int num1 = input.nextInt();
System.out.println("请输入第二个数字:");
int num2 = input.nextInt();
int result = num1 / num2;
System.out.println("结果:"+result);
}
}
7、抛出异常
- 除了系统自动抛出异常外,有些问题需要程序员自行抛出异常。
throw
关键字:抛出异常。- 语法:
throw 异常对象;
7.1 案例(一)
package com.goshawk.chapter_03.section_1;
public class Person {
private String name;
private int age;
private String sex;
public Person(){}
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
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>0&&age<=120){
this.age = age;
}else {
throw new RuntimeException("年龄不符合要求");
}
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
if (sex.equals("男") || sex.equals("女")) {
this.sex = sex;
}else {
throw new RuntimeException("性别不符合要求");
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
package com.goshawk.chapter_03.section_1;
public class TestPerson {
public static void main(String[] args) {
Person xiaoming = new Person();
xiaoming.setAge(20);
xiaoming.setSex("人妖");
System.out.println(xiaoming.toString());
}
}
7.2 案例(二)
package com.goshawk.chapter_03.section_1;
public class Person {
private String name;
private int age;
private String sex;
public Person(){}
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) throws Exception {
if (age>0&&age<=120){
this.age = age;
}else {
throw new Exception("年龄不符合要求");
}
}
public String getSex() {
return sex;
}
public void setSex(String sex) throws Exception {
if (sex.equals("男") || sex.equals("女")) {
this.sex = sex;
}else {
throw new Exception("性别不符合要求");
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
package com.goshawk.chapter_03.section_1;
public class TestPerson {
public static void main(String[] args) throws Exception{
Person xiaoming = new Person();
xiaoming.setAge(20);
xiaoming.setSex("人妖");
System.out.println(xiaoming.toString());
}
}
8、自定义异常
- 需要继承自
Exception
或Exception
的子类,常用RuntimeException
。 - 必要提供的构造方法:
- 无参数构造方法
String message
参数的构造方法。
8.1 案例
package com.goshawk.chapter_03.section_1;
public class SexException extends RuntimeException{
public SexException() {
}
public SexException(String message) {
super(message);
}
public SexException(String message, Throwable cause) {
super(message, cause);
}
public SexException(Throwable cause) {
super(cause);
}
public SexException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.goshawk.chapter_03.section_1;
public class AgeException extends RuntimeException{
public AgeException() {
}
public AgeException(String message) {
super(message);
}
public AgeException(String message, Throwable cause) {
super(message, cause);
}
public AgeException(Throwable cause) {
super(cause);
}
public AgeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
package com.goshawk.chapter_03.section_1;
public class Person {
private String name;
private int age;
private String sex;
public Person(){}
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
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>0&&age<=120){
this.age = age;
}else {
throw new AgeException("年龄不符合要求");
}
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
if (sex.equals("男") || sex.equals("女")) {
this.sex = sex;
}else {
throw new SexException("性别不符合要求");
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
package com.goshawk.chapter_03.section_1;
public class TestPerson {
public static void main(String[] args) {
Person xiaoming = new Person();
xiaoming.setAge(200);
xiaoming.setSex("人妖");
System.out.println(xiaoming.toString());
}
}
9、方法覆盖声明异常方法
- 带有异常声明的方法覆盖:
- 方法名、参数列表、返回值类型必须和父类相同。
- 子类的访问修饰符合父类相同或是比父类更宽。
- 子类中的方法,不能抛出比父类更多、更宽的检查时异常。
- 比如:父类抛出的异常为
FileNotFoundException
,那么子类就不能抛出Exception
异常。 - 比如:如果父类没有抛出异常,那么子类就不能抛出异常。因为子类不能比父类更多,但是子类可以抛出
RuntimeException
异常。
9.1 案例(一)
- 子类中的方法,不能抛出比父类更多、更宽的检查时异常
- 如果父类抛出
Exception
,子类重写的方法可以抛出Exception
异常,也可以不抛。
package com.goshawk.chapter_03.section_1;
public class Animal {
public void eat() throws Exception{
System.out.println("这是一个父类中的吃方法");
}
}
package com.goshawk.chapter_03.section_1;
public class Dog extends Animal{
@Override
public void eat() throws Exception{
System.out.println("这是一个子类中的吃方法");
}
@Override
public void eat() {
System.out.println("这是一个子类中的吃方法");
}
}
9.2 案例(二)
package com.goshawk.chapter_03.section_1;
import java.io.FileNotFoundException;
public class Animal {
public void eat() throws FileNotFoundException {
System.out.println("这是一个父类中的吃方法");
}
}
- 父类抛出的异常为
FileNotFoundException
,那么子类就不能抛出Exception
异常,因为Exception
异常要比FileNotFoundException
异常要广、要多。但是可以不抛或者抛出的异常与父类一致。
package com.goshawk.chapter_03.section_1;
public class Dog extends Animal{
@Override
public void eat() throws Exception{
System.out.println("这是一个子类中的吃方法");
}
}
9.3 案例(三)
- 如果父类没有抛出异常,那么子类也不能抛出异常。因为子类不能比父类更多,但是子类可以抛出
RuntimeException
异常。
package com.goshawk.chapter_03.section_1;
public class Animal {
public void eat(){
System.out.println("这是一个父类中的吃方法");
}
}
package com.goshawk.chapter_03.section_1;
import java.io.FileNotFoundException;
public class Dog extends Animal{
@Override
public void eat() throws RuntimeException {
System.out.println("这是一个子类中的吃方法");
}
}
10、常见运行时异常
类型 | 说明 |
---|
NullPointerException | 空指针异常 |
ArrayIndexOutOfBoundsException | 数组越界异常 |
ClassCastException | 类型转换异常 |
NumberFormatException | 数字格式化异常 |
ArithmeticException | 算术异常 |
10.1 案例
package com.goshawk.chapter_03.section_1;
import java.io.FileInputStream;
public class Demo1 {
public static void main(String[] args) {
}
}
11、总结
- 异常的概念
- 异常的分类
RuntimeException
:运行时异常,可处理,可不处理。CheckedException
:检查时异常,必须处理。
- 异常的产生
- 异常的传递
- 按照方法的调用链反向传递,如始终没有处理异常,最终会由
JVM
进行默认异常处理(打印堆栈跟踪信息并且中断程序)。
- 异常的处理
try {} catch {} finally {}
- 带有异常声明的方法覆盖