一 Throwable
在刚开始学习编程的时候,或多或少的会出现一些问题,有的问题在你编写的时候已经提示了,而有的问题只有在你遇到问题中才会出现,而这个问题我们称它为异常
这里异常可以分三类
- 检查性异常 这是我们日常编程过程中最常见的问题,在编译的时候 不能被忽略
- 运行时异常 能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误 可以理解为不能被察觉的问题,不在我们编程时的意料之中.不好追究问题
前两个引用下菜鸟的分类
- 检查性异常: 不处理编译不能通过
- 非检查性异常:不处理编译可以通过,如果有抛出直接抛到控制台
- 运行时异常: 就是非检查性异常
- 非运行时异常: 就是检查性异常
这两个可以不用细分,在官方文档中,主要分为异常和错误,从之前的概念中我们已经得知在Java设计中,Object是万物的父类,即根类.可以注意到平常所用到的类除了第三方外都是继承自Object类中,异常也不类外
看下从APi文档中找到的异常类
Throwable是Java语言中所有错误和异常的Throwable类,只有作为此类或子类的实例对象才可以使用,即Throwable是所有异常的父类,
而Throwable的直接子类有两个 一个错误即Error 一个 异常即Exception,
Q:它们有什么区别呢
先来看看异常的表现形式,下面的代码作用为了去重并输出自定义对象储存的内容.当然用set集合更好,但是输出的时候下标范围+1,编译阶段没有发生任何问题,也没有提醒,说明是逻辑上是没有问题的,但是运行的时候却挂了,因为判断的时候引用了超出了集合的范围的元素:
import java.util.ArrayList;
//这里有class Teacher 里面有两个属性 name 和age
public class Test2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList<Teacher> tc=new ArrayList<Teacher>();
Teacher teacher1=new Teacher("张三",11);
Teacher teacher2=new Teacher("张三一",15);
Teacher teacher3=new Teacher("张三一",12);
Teacher teacher4=new Teacher("张三三一",11);
Teacher teacher5=new Teacher("张三一",15);
tc.add(teacher1);
tc.add(teacher2);
tc.add(teacher3);
tc.add(teacher4);
tc.add(teacher5);
ArrayList<Teacher> tc2=new ArrayList<Teacher>();
for(int x=0;x<tc.size()+1;x++) {//这里集合tc的下标+1,超出了范围
while(tc2.contains(tc.get(x))==false) {
tc2.add(tc.get(x));
}
}
System.out.println(tc2);
}
}
结果报错
在API文档中看下,这个异常的信息IndexOutOfBoundsException
- java.lang.Object
- java.lang.Throwable\\异常总类
- java.lang.Exception\\异常子类,可以被处理的类
- java.lang.RuntimeException
- java.lang.IndexOutOfBoundsException
这里我们可以看见,它继承自RuntimeException 而RuntimeException继承自Exception异常类
再观察下Error类
引入一个概念:
Error: 所有的编译时期的错误和系统错误由Error抛出的,表示故障是在虚拟机自身或者虚拟机运行时发生的,这些错误是不可查的,它们在应用程序的控制和处理能力之 外,而且大多数是程序运行时不允许出现的状况.对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况.
Exception: 异常子类,它规定的异常是程序本身可以处理的异常,异常和错误的区别是,异常是可以被处理的,而错误是没法处理的.
而我们一般所研究的则是可以处理的异常.
二 异常
Exception
捕获异常
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方:
try {
for(int x=0;x<2321002235515;x++) {
System.out.println(x);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try里面放的是可能发生异常的代码段, 一般来说, try里面不要放太多的代码,一方面如果代码多了, 我们是不容易发现出现问题的代码,不好去定位问题,一方面也不美观. 而Catch 语句放要捕获异常类型的声明。
上面的代码是有问题的,因为x小于的数,已经超出x的范围,已经是不能运行
public class Test10 {
public static void main(String[] args) {
int [] arr= {1,2,3};
try {
System.out.println(arr[3]);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
再看看这段代码数组下标最大为2,我输出了arr[3],是没有这个元素的,所以我用try将它抓起.
结果:
这种方法我们称它为捕获异常
一般来说,正确的try...catch结构是这样的
public class Test10 {
public static void main(String[] args) {
int [] arr= {1,2,3};
try {
System.out.println(arr[3]);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
finally {
System.out.println("结束啦");
}
}
}
try..catch...finally 有一点要注意finally关键字里的代码块一定会执行,无论是否捕捉到异常.除非一种情况,就是JVM虚拟机停止运行了.
此外finally这个结构里,有一些相关的逻辑题可以去做,能更加深入了解这个概念
比如(https://blog.csdn.net/WYpersist/article/details/80710352)
final finalze finally的区别等
多重捕获
一个catch代码块后还有多个catch的情况,我们称它为多重捕获,这时如果 catch不是对应的异常,继续在第二个catch里寻找
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}
注意:
如果父类抛出多个异常,则覆盖方法必须抛出那些异常的一个子集,不能抛出新异常。
捕获异常时,大的异常(Exception类)放在下方,小的异常放在上方,否则,在异常捕获时,小的异常将不能被捕获,因为全在大的异常类中捕获到。即: 如果多个 catch 块中的异常出现继承关系,父类异常 catch 块放在最下面。
throws/throw
如果一个方法没有捕获到一个检查性异常,那么该方法必须使用 throws 关键字来声明。throws 关键字放在方法的尾部。
也可以使用 throw 关键字抛出一个异常,无论它是新实例化的还是刚捕获到的。
一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。
class a{
void b() throws Exception{//这里可以抛出多个异常, 逗号隔开
throw new Exception();//这里抛一个异常,所以实现里也必须有个异常抛出
}
}
public class Test10 {
public static void main(String[] args) throws Exception {
a a=new a();
/* try {
a.b();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//如果没有try catch 则会报错,必须在方法后抛出一个异常
*/ a.b();
}
}
Q:throws和throw区别
从上面的代码我们也看到,两者都是抛出异常.
throws:
- 抛出在方法声明上,方法后面跟异常类名,且可以多个中间逗号隔开
- 表示抛出异常的可能性,交给调用者去处理
throw
- 抛出在方法体中,方法体中跟的是异常对象名 new xxException()
- throw表单抛出异常的肯定性,有了这个后面必须做出处理
- throw抛出的异常,交给方法体重语句处理
- throw后只能跟一个异常对象
自定义异常类
自定义异常有几个要求.
- 所有异常都必须是 Throwable 的子类。
- 如果希望写一个检查性异常类,则需要继承 Exception 类。
- 如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。
可以像下面这样定义自己的异常类:
class MyException extends Exception{ }
只继承Exception 类来创建的异常类是检查性异常类。
我们自定义的异常类和其它任何类没有什么区别,包含有变量和方法。
class a extends Exception{
void b() {
System.out.println("我是b方法");
}
}
public class Test10 {
public static void main(String[] args) {
a a=new a();
a.b();
}
}
这时候我们可以理解下自定义异常类的作用,不光用于抛出错误时候的异常,也可以用来处理异常,像银行取钱大于账户的余额当以当做异常,游戏的暴击,登录登出提示等等.关于使用方法我们可以再构思构思.
End