java异常
java中程序执行中不正常的情况称为异常(Expection)(逻辑错误和语法错误不是异常)
异常类Throwable(父类是object,实现了serializable)分两类
1.Error
指jvm无法解决的严重问题,如jvm内部的系统错误,资源耗尽(比如:栈溢出overstack,oom(out of memory))等情况,出现Error代表问题很严重,程序直接崩溃。
2.Expection
其他因编程错误或偶然外在因素的一般性问题,可以用代码进行针对性处理,例如空指针想访问一个不存在的文件,网络连接中断等等。Expection也分为两大类:运行时异常和编译时异常。
运行时异常可以不try—catch,因为这类型的异常太普遍了,若全处理会严重影响代码的运行效率和可读性,很多时靠修改代码逻辑来解决
编译时异常是编译器要求你必须处置的异常
异常处理
当我们在运行一个程序时,如果出现一个异常,哪怕很小小的问题,也会终止程序,小一点程序还好说,如果是淘宝,京东这些流量那么大的网站,那么将要承受极大的损失,所以java是有一个异常处理的机制,能让程序在发生异常后,根据异常处理机制来自动处理异常,并且可以让程序不会就此终止,继续执行下面的代码
1.try-catch-finally(程序员自己用代码捕获到的异常,自行处理)
try { //放入可能会出现异常的代码
//可以有多行代码,一旦检测到异常就会立刻停止try语句块,进行后续操作,即try语句块发生异常的位置开始,剩下的代码将不会执行
} catch (Exception e) { //当出现异常时,系统会将异常封装成一个对象传给e,然后执行catch语句块的代码 //没出现异常则不执行catch语句块的代码
//如果不想要catch语句块,可以不写 } finally { //不管有没有异常都会执行finally语句块的代码, //所以通常这里是做一些释放资源的操作 //如果用不到finally语句块,可以不写 }
①一个程序可以有多个try-catch语句块
②catch和finally至少要有其中一个
③当我们希望:不管被try的代码有无异常,都要执行某些操作时,直接try-finally即可,当然这种用法在异常发生时是不会捕获到的(结果就是执行完finally后就崩掉程序),所以如果想要把异常也给处理掉,那么直接try-catch-finally全家桶
④特别注意,当t-c-f在用在方法里时,如果try或者catch语句块里有返回语句,那么将会被finally的返回语句抢先在前执行(因为一定要等finally所有语句执行过后才能结束t-c-f语句块),同理,如果finally没有返回语句,那么通过调试可以看到,方法里会先计算好try或catch的返回值是多少,然后转到finally,执行完finally的所有语句,再回到try或catch的返回语句,并返回计算好的返回值,如下代码
public class Test { public static void main(String[] args) throws Exception { System.out.println(new A().a()); } } class A { public int a() { int i = 0;//用来测试会不会执行catch的返回值 try { return i + 1; } finally { System.out.println("哈哈,我是finally我最大,我气死你们"); return i + 2; } } }
运行结果
哈哈,我是finally我最大,我气死你们
2
=================当出现异常时=============
public class Test { public static void main(String[] args) throws Exception{ System.out.println(new A().a()); } } class A{ public int a(){
int i = 0;//用来测试会不会执行catch的返回值 try{ int a = 1,b = 0; System.out.println(a / b); return i + 1; }catch(Exception e){ return i + 2; }finally {
System.out.println("哈哈,我是finally我最大,我气死你们"); return i + 3; } } } 运行结果
哈哈,我是finally我最大,我气死你们 3
==========当finally没有返回语句=========
public class Test { public static void main(String[] args) { System.out.println(new A().a()); } } class A { public int a() { int i = 0;//用来测试会不会执行catch的返回值 try { return ++i; } finally { System.out.println("哈哈,我是finally我最大,我气死你们"); } } } 运行结果 哈哈,我是finally我最大,我气死你们 1
可以看到,当try或catch将要执行返回语句的 “返回” 这个步骤时都会停下,转而执行finally语句块的全部内容
注意:我们要重点明白什么叫“执行返回语句”,其实return是把其后面的语句的值计算好后存到临时变量,在赋值好这个临时变量的值之后,才会返回这个临时变量的值,也就是返回是分两步的:计算-----返回,那么在某些时候,就会出现 “一些在返回语句后面的变量,因为计算过后,自身的值更新了,但是并没有返回,而导致程序员错以为返回语句一点都没执行,该变量的值没有更新” 的情况,我把上面代码的返回值改一改,再来看下输出结果
public class Test { public static void main(String[] args) throws Exception{ System.out.println(new A().a()); } } class A{ public int a(){ int i = 0;//用来测试会不会执行try的返回值 try{ return ++i;//先加后赋,i = 1,但finally的return会抢先在前执行“返回” }finally { return i++;
//先赋值给临时变量,然后自加,会输出1
//然后不管return成不成功执行“返回”操作,i都已经更新了值 } } } 运行结果 1 ========把finally的i++改为++i====== public class Test { public static void main(String[] args) throws Exception{ System.out.println(new A().a()); } } class A{ public int a(){ int i = 0;//用来测试会不会执行try的返回值 try{ return ++i;//先加后赋,i = 1,但finally的return会抢先在前执行“返回” }finally { return ++i;
//先自身加,然后赋值给临时变量,,会输出2
//然后不管return成不成功执行“返回”操作,i都已经更新了值
} } } 运行结果 2
2.throws(扔给上一级的方法,最多是扔到给jvm)
public class Test { public static void main(String[] args) throws Exception{//甩锅给该方法的调用者,这里可以是该异常的类或者父类,且可以抛出多个异常 BufferedReader bf = new BufferedReader(new InputStreamReader(System.in)); String name = bf.readLine(); } }
具体如下图
注意,异常处理机制是两个机制二选一的,上图的每个方法都是两个机制中二选一
①如果是不确定要怎么处理的异常,一定要显式地指明怎么处理,比如BufferedReader的readLine()
②如果是运行时异常。默认是throws
③子类重写的父类方法,抛出的异常类型要与父类方法的异常类型相同,或是父类方法的的异常类的子类
④如果try和throws都同时写了,那么直接执行throws
自定义异常
语法格式
class 自定义异常名 extends 要继承的的异常类名{
//构造器,用来传递异常信息
}
举个例子
public class Test { public static void main(String[] args) { int a = 1; if (a == 1) throw new Customize("年龄不能为1"); else System.out.println("年龄正确"); } } class Customize extends RuntimeException { public Customize(String message) { super(message); //这个message会一直向上级传递,直到传到Throwable //然后赋值给Throwable的detailMessage //detailMessage会把异常信息输出 } }
其实提示异常信息这个动作,本质都是这样的:当某一段代码出现异常后,异常处理机制将异常信息用一个变量message,以字符串的形式传递给上一级父类的message,直到传递到throwable里面,这时message会赋值给throwable的detailMessage,然后这个detailMessage就会输出得到的异常信息,具体可以去看源码
同时上面这段代码出现了个throw,我们比较下它和throws的区别