Java异常复习笔记
文章目录
1.Java异常简单族谱
Java异常的老祖宗是Throwable,它有两个儿砸Error和Exception。Error表示系统内部错误和资源耗尽错误,这部分异常发生较少。Exception主要分为RuntimeException和其他非RuntimeException,如IOException。
RuntimeException是程序员仔细检查代码就可以规避的异常,比如空指针异常等等,Error和RuntimeException联合称为非受检异常/运行时异常,而Exception的非RuntimeException称为受检异常/编译时异常。
我们应该关心那些经常发生的异常,即受检异常。一类题太难做不来(Error);一类题简单,小心一点就好(RuntimeException)。还有一类题比较难,所以着重考虑(Exception下的非RuntimeException)。瞎理解…
所有异常都在运行时发生。
2.异常处理方式
受检异常必须处理,非受检异常可以不处理。异常的处理方式具体有两种,一种是自己用哪个try catch处理,另外一种方式是在方法定义后面抛出异常,交由上层处理。
第一种方式 try-catch处理
public class TwoKindHandlers {
public static void main(String[] args) {
//自己内部处理
try {
FileInputStream stream = new FileInputStream("");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
第二种方式 throws异常,交由上层处理
public class TwoKindHandlers {
public static void main(String[] args) throws FileNotFoundException {
FileInputStream stream = new FileInputStream("");
}
注释:FileInputStream的构造方法
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
其中FileNotFoundException为受检异常,所有必须由程序员进行处理。
3.throw 和 throws
throw 的用处是用来显式抛出异常,throws的用处是用来上抛异常(异常处理方式之一)。
public class TwoKindHandlers {
public static void main(String[] args) throws Throwable {
//自己内部处理
try {
FileInputStream stream = new FileInputStream("123");
} catch (FileNotFoundException e) {
Throwable t = new IOException("找不到文件", e);
throw t;
}
}
}
4.try-catch-finally
4.1try块
try块并不能单独存在,必须依附catch或是finally。try语句块里面是可能产生异常的语句块,如果有受检异常,则必须处理。
4.2catch块
catch块用于捕捉异常,catch语块可以是多个,前面的异常类型不能是后面类型的祖先。一个catch语句块可以捕获多种异常,不过要求这些类型不能有继承关系。
try{
//.........
}catch(ExceptionType1 e ){
//一个catch语句块可以捕获多种异常, 但是不能有继承关系
}catch(ExceptionType2 |ExceptionType3 |ExceptionType4 e){
}.....
catch(ExceptionTypeN e){
}
4.3finally块
finally的代码是肯定会执行的(除了System.exit函数调用),它的作用通常是用来回收资源。如下所示:
public class TwoKindHandlers {
public static void main(String[] args) {
FileInputStream stream=null;
try {
stream = new FileInputStream("123");
} catch (FileNotFoundException e) {
e.printStackTrace();
}finally {
if (stream!=null){
try {
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
如果有return语句,那么finally仍然会执行吗?
仍然会执行。
下面的debug的过程说明了finally语句确实执行了。
但是这里有一个问题,下面的代码
public class TwoKindHandlers {
public static void main(String[] args) {
System.out.println(demo1(0));
}
private static int demo1(int i){
try {
return i;
}finally {
i++;
}
}
}
控制台结果是
0
但是debug过程显示i 曾经是1
后来尝试用引用类型来进行测试,发现上面的代码其实类似于
public class TwoKindHandlers {
public static void main(String[] args) {
System.out.println(demo1(0));
}
private static int demo1(int i){
try {
return i;
}finally {
int j =i;
j++;
}
}
}
引用类型案例如下:
public class TwoKindHandlers {
public static void main(String[] args) {
System.out.println(demo1(new Student()).getAge());
}
private static Student demo1(Student student){
try {
return student;
}finally {
student.setAge(2);
}
}
}
class Student{
private int age ;
public Student() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
结果为:2.
其实这里说明了两个道理:
- try-catch-finally 碰到 return语句,finally仍然会进行。
- 如果是返回值的话,该值在finally语句中会首先被拷贝一次,然后用新的值去参加运算。
不过需要强调的是
如果调用System.exit(0)函数,程序直接终止。
5.自定义异常
自定义异常大概是两种,受检异常和非受检异常,非受检异常继承于RuntimeException,受检异常直接继承自Exception。(注意Error类是当系统内部错误或资源耗尽时才会出现,我们不要继承于它)。自定义异常按照下面的模板写,很多的异常都是下面这个模子。
public class MyException extends Exception {
//显示声明无参构造函数,方便以后继承
public MyException() {
}
//message 代表异常发生的原因,用String存储
public MyException(String message) {
super(message);
}
//cause即具体产生的异常,这里可以通过cause.getMessage()获取原因,
//然后将原因封装到本异常里面
//具体的效果 就是 异常 转型
public MyException(String message, Throwable cause) {
super(message, cause);
}
//还有一些构造函数未展示
}
6.异常常见的方法
//获取异常的原因 返回String
e.getMessage();
//另开一个线程,用于打印异常的堆栈执行过程
e.printStackTrace();
7.总结
异常的处理的目的是为了便于调试,使得程序不那么容易宕机。