1,异常机制
-
什么是异常,java提供异常处理机制有什么用?
- 以下程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常。
- java语言是很完善的语言,提供了异常的处理方式,程序执行过程中出现了不正常情况,
java把该异常信息打印输出到控制台,供程序员参考;程序员看到异常信息之后,可以
对程序进行修改,让程序更加的健壮。
-
程序执行控制台出现了:
Exception in thread "main" java.lang.ArithmeticException: 这个信息被我们为:异常信息,这个信息是JVM打印的。
-
总结:
- 什么是异常,程序执行过程中的不正常情况。
- 异常的作用:增强程序的健壮性。
代码示例
public class ExceptionTest01 {
public static void main(String[] args) {
// System.out.println(10/0);// 出现异常 Exception in thread "main" java.lang.ArithmeticException:
// 通过"异常类"实例化异常对象
NumberFormatException numberFormatException = new NumberFormatException("数字格式化异常!");
System.out.println(numberFormatException);
NullPointerException nullPointerException = new NullPointerException("空指针异常!");
System.out.println(nullPointerException);
}
}
2,异常的存在形式
- 异常在java中以类的形式存在,每一个异常类都可以创建异常对象。
- 类是模板,对象是实际存在的个体。
- Object下有个Throwable(可抛出的),Throwable有两个分支:
- Error(不可处理,直接退出jvm)
- Exception(可处理的):
- Exception的直接子类:编译时异常
- 要求程序员编写程序阶段必须预先对这些异常进行处理,如果不处理编译器报错,因此得名编译时异常。
- 受检异常:CheckedException
- 受控异常:
- RunTimeException:运行时异常。
- 在编写程序阶段程序员可以预先处理,也可以不管,都可以。
- 未受检异常:UnCheckedException
- 未受控异常:
- Exception的直接子类:编译时异常
- 编译时异常和运行时异常,都是发生在运行阶段,编译阶段异常是不会发生的。因为只要程序运行阶段才可以new对象,异常的发生就是new异常对象。
- 编译时异常和运行时异常的区别:
- 编译时异常一般发生的概率比较高
- 运行时异常一般发生的概率比较低
3,异常的处理
- Java语言中对异常的处理包括两种方式:
- 1,在方法声明的位置上,使用throws关键字,抛给上一级。
- 2,使用try..catch语句进行异常的捕捉。
- 注意:java中异常发生之后如果一直上抛,最终抛给了main方法,main方法继续上抛,抛给了调用者JVM,JVM知道了这个
异常发生,只有一个处理结果:终止java程序的执行。
3.1 分析异常案例
public class ExceptionTest02 {
public static void main(String[] args) {
/*
System.out.println(100/0);
程序执行发生了ArithmeticException异常,底层new了一个ArithmeticException异常对象,
然后抛出了,由于是main方法调用了"100/0",所以这异常ArithmeticException抛给了main
方法,main方法没有处理,将这个异常抛给了JVM,JVM终止了程序的执行。
ArithmeticException继承RunTimeException,属于运行时异常,
在编写程序阶段不需要对这种异常进行预先处理。
*/
// System.out.println(100/0);
// ------------------------------------------------------------------------------
// 调用doSome方法
// 因为doSome方法声明位置上有throws ClassNotFoundException
// 所以我们在调用doSome方法时候必须对这种异常进行预先处理,如果不处理编译器会报错
// 编译报错信息: java: 未报告的异常错误java.lang.ClassNotFoundException; 必须对其进行捕获或声明以便抛出
// doSome();
}
/**
* doSome方法在方法声明的位置使用了throws ClassNotFoundException
* 表示doSome方法在执行过程中,有可能会出现ClassNotFoundException异常
* 叫做类没有找到异常,这个异常值接父类Exception,所以ClassNotFoundException属于编译异常
* @throws ClassNotFoundException
*/
public static void doSome() throws ClassNotFoundException{
System.out.println("ClassNotFoundException");
}
}
3.2 处理异常
public class ExceptionTest03 {
// 第一种处理方式,在声明方法的位置上继续使用throws,异常继续上抛,抛给调用者。
// public static void main(String[] args) throws ClassNotFoundException {
// doSome();
// }
public static void main(String[] args) {
// 第二种处理方式:try..catch进行捕捉
try {
doSome();
} catch (ClassNotFoundException e) {
// catch捕捉异常之后,处理异常
e.printStackTrace();
}
}
/**
* doSome方法在方法声明的位置使用了throws ClassNotFoundException
* 表示doSome方法在执行过程中,有可能会出现ClassNotFoundException异常
* 叫做类没有找到异常,这个异常值接父类Exception,所以ClassNotFoundException属于编译异常
* @throws ClassNotFoundException
*/
public static void doSome() throws ClassNotFoundException{
System.out.println("ClassNotFoundException");
}
}
3.3 异常对象的两个重要方法:
-
获取异常简单的描述信息
String msg = exception.getMessage();
-
打印异常追踪的堆栈信息:
exception.printStackTrace();
代码示例
public class ExceptionTest05 {
public static void main(String[] args) {
NullPointerException e = new NullPointerException("空指针异常");
String msg = e.getMessage();
System.out.println(msg);// 空指针异常
// 打印异常追踪的堆栈信息
e.printStackTrace(); // java.lang.NullPointerException: 空指针异常
System.out.println("hello world"); // e.printStackTrace(); 不影响hello world的输出
}
}
3.4 处理异常深入了解try..catch
- catch后面的小括号中的类型可以是具体的异常类型,也可以是该异常的父类型。
- catch可以写多个,建议catch的时候,精确的一个一个的处理。这样有利于程序的调试。
- catch写多个的时候,从上到下,必须遵守从小到大。
代码示例
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class ExceptionTest04 {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("");
fis.read();
//
System.out.println(100/0); // 这个异常是运行时异常,编写程序时可以处理,也可以不处理。
}catch (FileNotFoundException e){ // 所有异常都走catch分支
System.out.println("文件不存在");
}catch (ArithmeticException | NullPointerException e){ // jdk8之后新特性 可以用|的形式编写
System.out.println("读取文件失败");
}catch (IOException e){
}
}
}
3.5 关于try..catch中finally子句
- 在finally子句中的代码是最好执行的,并且一定会执行,即使try语句块中的代码出现了异常。
- finally子句必须和try一起出现,不能单独编写。
- finally语句通常使用在哪些情况下呢?
- 通常在finally语句块中完成资源的释放/关闭,因为try语句块中的代码出现异常,finally中的代码也会正常执行。
代码示例
public class ExceptionTest06 {
public static void main(String[] args) {
FileInputStream fis = null;
try {
// 创建输入流对象
fis = new FileInputStream("");
String s = null;
s.toString(); // 这里会出现空指针异常
// 流使用完需要关闭,因为流是占用资源的
// 即使程序出现了异常,流也必须关闭!
// 放在这里有可能关闭不了。
fis.close();
}catch (FileNotFoundException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}catch (NullPointerException e){
e.printStackTrace();
}finally {
// 流的关闭放在这里比较保险,finally中的代码一定会执行的。
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
3.6 finally面试题
3.6.1 final、finally、finalize有什么区别
final是一个关键字,表示最终的不变的 finally也是一个关键字,和try联合使用,使用在异常处理机制中,在finally语句块中的代码一定会执行。 finalize是标识符,是Object类中的一个方法,作为方法名出现。
3.6.2 分析下面程序的运行结果
public class ExceptionTest08 {
public static void main(String[] args) {
int result = m();
System.out.println(result);// 100
}
/*
java语法规则
方法体中的代码必须遵循自上而下顺序依次执行,
return语句一旦执行,整个方法必须结束。
*/
public static int m(){
int i = 100;
try {
// 这行代码出现在int i = 100的下面,所以最终结果必须是返回100
// return语句还必须保证是最后执行的,一旦执行整个方法结束。
return i;
}finally {
i++;
}
}
}
上篇:JavaSE进阶六 通用类
下篇:JavaSE进阶八 集合一 Collection接口