Java中的异常
1 什么是异常
前遇到过的异常:
1、空指针异常, NullPointerException
2、类型转换异常, ClassCastException
3、遇到这些异常使程序中断了
异常就是在程序运行过程中出现不正常现象导致了程序中断
2 异常的类结构
在Java中, 把经常出现的一些异常现象进行了抽象就形成了异常类
3 运行时异常的特点
1、运行时异常就是RuntimeException类及它的子类
2、运行时不需要预处理, 通过规范代码可以避免
/**
* 演示运行时异常
*
*/
public class Test01 {
public static void main(String[] args) {
// divide1(10, 2);
divide1(10, 0); //执行语句时, 除数为0,会产生算术异常, 导致了程序中断
divide2(10, 0);
System.out.println("main方法后面的代码");
}
//定义方法, 计算两个整数相除的结果
public static void divide1( int x , int y) {
System.out.println(x + " / " + y + " = " + (x/y) );
}
//运行时异常,可以通过规范的代码避免
public static void divide2( int x , int y) {
//整数相除, 如果除数为0,会产生算术异常
if ( y == 0 ) { //通过判断, 如果除数为0, 给用户一个提示信息, 不再进行计算
System.out.println("除数不能为0");
return;
}
System.out.println(x + " / " + y + " = " + (x/y) );
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at chapter02.demo01.Test01.divide1(Test01.java:18)
at chapter02.demo01.Test01.main(Test01.java:9)
Process finished with exit code 1
4 编译时异常特点
1、编译时异常,也叫受检异常, 即在方法定义时,通过throws声明抛出的异常
2、在编译前必须对受检异常进行预处理, 即在开发程序时,要指定如果产生了异常应该怎么办
注意:
1、编译时异常并不是说在编译阶段发生异常, 所有的异常都是在运行时才会发生.
2、编译就是把.java编译为.class字节码文件, 编译时要进行语法检查,
3、Java语法有很多条, 其中一条语法是说必须对受检异常进行预处理
/**
* 演示编译时异常
*
*/
public class Test02 {
public static void main(String[] args) throws IOException {
//假设读取d:/abc.txt文件的内容, 需要使用FileInputStream类
FileInputStream fis = new FileInputStream("d:/abc.txt");
/*
* 上行代码出现了语法错误
* FileInputStream构造方法在定义时, 通过throws语句声明抛出了FileNotFoundException异常, 该异常就是受检异常
* 受检异常就是方法定义时,通过throws声明抛出的异常
* 在调用方法时, 必须对受检异常进行预处理 ,否则编译报错
*/
m1();
}
//定义方法时, 通过throws声明抛出了一个异常, 这个异常就是受检异常
public static void m1() throws IOException {
}
}
5 对受检异常进行处理
5.1 try…catch 捕获处理
/**
* 演示捕获异常
*/
public class Test03 {
public static void main(String[] args) {
try {
System.out.println("try代码块中,可能有语句的受检异常需要预处理");
FileInputStream fis = new FileInputStream("d:/abc.txt");
System.out.println("如果上行代码产生了异常,就直接跳转到catch子句,try代码块后面的代码不再执行");
System.out.println("try代码块中中,可能会有多个受检异常需要预处理, 可以通过多个catch分别捕获");
fis.read();
} catch (FileNotFoundException e) {
//如果产生 了 文件找不到 异常, 进行处理
//开发阶段,捕获了异常,一般是打印异常栈跟踪信息, 方便调试
e.printStackTrace();
} catch (IOException e) {
//如果产生了 IO 异常, 进行处理
//FileNotFoundException继承了IOException,捕获的异常有继承关系, 应该先捕获子异常, 再捕获父异常
e.printStackTrace();
}
System.out.println("main方法后面的代码.......");
}
}
5.2 finally子句处理
/**
* finally子句
* 在异常捕获处理中, finally子句不是必须的, 可以这样使用:
* try...catch....,
* try... catch...finallly,
* try...finally...
*
* 一般在finally子句中释放系统资源
*
*/
public class Test04 {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("d:/def.txt");
} catch ( FileNotFoundException e) {
e.printStackTrace();
}finally {
System.out.println("finally子句");
}
System.out.println("main...end....");
}
}
5.3 finally子句
/**
* finally子句
*
*/
public class Test05 {
public static void main(String[] args) {
int x = m1(10);
System.out.println( x ); //10
}
private static int m1(int i) { //i=10
try {
return i;
} finally {
i += 10;
System.out.println(i);
}
/*
* 程序执行到return i;时, 并没有执行return语句, 而是把i的值保存到临时变量中
* 执行finally子句, 把i的值变为20
* 再执行return语句, 返回的不是i现在的值, 而是返回临时变量的值
*/
}
}
20
10
Process finished with exit code 0
5.4 throws抛出处理
/**
* throws抛出处理
*
*/
public class Test06 {
//main方法把异常抛出JVM, JVM默认的处理方式是中断程序,打印异常跟踪信息
public static void main(String[] args) {
System.out.println("在main方法中调用m1方法");
try {
m1();
} catch (FileNotFoundException e) {
//对m1()方法产生的异常捕获后, 不再继续上抛
e.printStackTrace();
}
System.out.println("main方法运行结束");
}
private static void m1() throws FileNotFoundException {
System.out.println("在m1方法中调用m2方法");
m2();
System.out.println("m1方法运行结束");
}
//在定义m2方法时, 把方法体中的受检异常声明抛出, 抛出给调用者
private static void m2() throws FileNotFoundException {
System.out.println("m2方法开始执行, 调用 FileINputStream");
FileInputStream fis = new FileInputStream("d:/def.txt");
System.out.println("m2运行结束 ");
}
}
/**
* throws抛出处理
*
*/
public class Test06 {
//main方法把异常抛出JVM, JVM默认的处理方式是中断程序,打印异常跟踪信息
public static void main(String[] args) throws FileNotFoundException {
System.out.println("在main方法中调用m1方法");
m1();
System.out.println("main方法运行结束");
}
private static void m1() throws FileNotFoundException {
System.out.println("在m1方法中调用m2方法");
m2();
System.out.println("m1方法运行结束");
}
//在定义m2方法时, 把方法体中的受检异常声明抛出, 抛出给调用者
private static void m2() throws FileNotFoundException {
System.out.println("m2方法开始执行, 调用 FileINputStream");
FileInputStream fis = new FileInputStream("d:/def.txt");
System.out.println("m2运行结束 ");
}
}
在main方法中调用m1方法
在m1方法中调用m2方法
m2方法开始执行, 调用 FileINputStream
Exception in thread "main" java.io.FileNotFoundException: d:\def.txt (系统找不到指定的文件。)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at java.io.FileInputStream.<init>(FileInputStream.java:93)
at chapter02.demo01.Test06.m2(Test06.java:28)
at chapter02.demo01.Test06.m1(Test06.java:21)
at chapter02.demo01.Test06.main(Test06.java:14)
Process finished with exit code 1
6 异常处理的作用
通过异常处理可以增加程序的键壮性, 鲁棒性(Robust)
如果没有对异常进行处理,出现了异常会程序中断(崩溃); 进行了异常处理,程序不会中断,还会继续向下执行
7 如何选择捕获处理还是抛出处理
在编程时,如何选择捕获处理还是抛出处理
1、一般情况下, 在调用方法时, 如果被调用的方法有受检异常, 选择捕获处理;
2、在定义方法时, 如果方法体中有受检异常的语句, 可以选择捕获处理, 也可以选择抛出处理,
3、如果方法体中通过throw抛出一个异常对象,所在方法通过throws声明抛出该异常
8 方法覆盖中的异常处理
1、方法签名必须相同
2、返回值类型可以相同,也可以是子类型
3、访问权限可以相同,也可以更大
4、异常不能更大,可以更小
/**
* 作为父类子类重写父类 的方法
*
*/
public class Father {
public void m1(int x ) {
}
public void m2() throws IOException{
}
public class Son extends Father{
//可以重写父类的方法
@Override
public void m1(int x) {
//该方法在父类中没有抛出异常, 重写后也不能抛出异常
}
@Override
// public void m2() throws IOException {
// public void m2() throws FileNotFoundException {
public void m2() {
//父类方法抛出了异常,重写后,可以抛出相同的异常
//也可以抛出子异常
//子类方法,也可以不抛出异常
}
}
9 异常在开发中的作用
//1)定义一个类继承Exception
public class AgeOutOfBoundsException extends Exception {
//2)一般情况下,只提供两个构造方法
public AgeOutOfBoundsException() {
super();
}
//有参数的构造方法, 这个String字符串参数就是异常的信息
public AgeOutOfBoundsException(String message) {
super(message);
}
}
public class Person {
private String name;
private int age;
private String gender; //性别
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + ", gender=" + gender + "]";
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
//4)throw所在的方法通过throws声明抛出该异常
public void setAge(int age) throws AgeOutOfBoundsException {
//对参数数据进行合理性验证
if ( age >= 0 && age < 130) {
this.age = age;
}else {
//年龄不合适, 抛出一个异常
//3)通过throw抛出一个异常对象
throw new AgeOutOfBoundsException("年龄越界");
}
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
}
public class Test {
public static void main(String[] args) {
//创建对象,直接给对象的字段赋值
/* Person p1 = new Person();
p1.name = "lisi";
p1.age = 188888; //age表示人的年龄,可能会赋一些无效的值
p1.gender = "男";
System.out.println( p1 );*/
//创建对象,调用setter方法实现赋值
Person p2 = new Person();
p2.setName("lisi");
try {
p2.setAge(188888);
} catch (AgeOutOfBoundsException e) {
e.printStackTrace();
}
p2.setGender("妖");
System.out.println( p2 );
}
}
chapter02.demo03.AgeOutOfBoundsException: 年龄越界
at chapter02.demo03.Person.setAge(Person.java:28)
at chapter02.demo03.Test.main(Test.java:20)
Person [name=lisi, age=0, gender=妖]
Process finished with exit code 0