java学习面向对象之异常之一
一、异常的概述:
什么是异常?在我们编写java程序的时候,会出现一些问题,比如内存溢出啊或者数组索引超出最大索引啊,这些编程当中出现的这些个问题就是异常。但是异常也分为可以处理的和不可以处理的。比如JVM虚拟机出现的异常就是不可以处理的。可以处理的异常就是指,在我们java程序当中的数组最大索引超出啊,这些问题我们是可以处理的。同时异常也分为Error和Exception。从字面意义上来理解的话,可以理解成错误和异常。Error指在JVM运行当中出现异常,程序自动退出。下面我们来写个例子看下什么是异常?
1 class Demo1 2 { 3 4 int[] arr = new int[3]; 5 6 } 7 8 class ExcepDemo1 9 { 10 11 public static void main(String[] args) { 12 13 Demo1 demo = new Demo1(); 14 System.out.println(demo.arr[6]); 15 16 } 17 18 }
因为这个地方我们的数组索引超出了最大值,这里就提示我们:Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
如果我们这里出现语法错误又会怎样呢?
既然出现了异常,我们就要去修正。修正的方法有哪些呢,比如上面的索引溢出异常,这个时候,我们就得在代码当中来判断,如果索引大于最大值,我们应该怎么办。在处理错误的时候就得写很多的代码,来描述现在反生的异常是怎样的,在什么地方等等异常的信息。java是一门面向对象的语言,面向对象的特点之一就是封装,也就是把属性还有方法封装到一个类当中,这样就方便了我们的调用,以前是正常代码和异常处理代码结合,现在是把正常代码和异常处理代码分离,同时也提高了代码的复用性。在java当中,为了方便我们来处理异常,提供了一个异常类Exception给我们。其实异常就是java通过面向对象的思想将问题封装成对象,然后用异常类来描述。不同的问题,用不同的异常类进行描述。
问题很多,意味着描述类也很多,将其共性不断向上抽取,就形成了异常体系。对于体系而言,父类当中定义的都是最共性的东西。
最终问题 不正常情况分为两大类:
1、一般不可处理的。用Error类来表示。比如java xx 如果xx这个javaclass 不存在就会Error。一般由JVM抛出的,这种问题发生不会针对性处理,直接修改程序。
2、可处理的。用Exception类来表示, 可以进行针对性的处理
无论Error还是Exception,发生问题后,都要抛出。该体系的特点是throwable及其所有他的子类都有可抛性。
就像人生活中得病一样,一种是可治愈的,一种是不可治愈的。Error就是不可治愈的范畴了,Exception可以治愈的。
子类都是以其父类名作为后缀,阅读性就很强。
但是两者都有共性,向上抽取之后就是throwable。那么怎么体现可抛性呢?通过两个关键字来体现的,一个是throws,一个是throw。凡是可以被这两个关键字可以操作的类或者对象,都具有可抛性。
那么异常的执行流程也就是内部原理是怎样的呢?当出现异常的时候,首先调到功能定义的部分,如果功能定义的部分没有处理,继续网上抛出,直至被处理。如果自始至终都没有被处理的话,这个时候,就会把异常抛给虚拟机。虚拟机收到异常之后,停止程序的执行然后把异常的相关信息直接打印到控制台上面。
比如上述索引越界,我们可以这个样子来写:
1 class Demo1 2 { 3 4 int[] arr = new int[3]; 5 6 } 7 8 class ExcepDemo1 9 { 10 11 public static void main(String[] args) { 12 13 Demo1 demo = new Demo1(); 14 int num=6; 15 if(num>demo.arr.length){ 16 17 throw new Exception("the num is too large");//我们new完了异常之后,这个时候为了能够让调用者知道异常原因,这个时候我们引入了throw这个关键字 18 19 }else{ 20 21 System.out.println(demo.arr[num]); 22 23 } 24 25 } 26 27 }
throw和return的区别,有的人会说我们不用throw用return,这个就涉及工作的一个完成一个未完成,一个事情的完成是有结果的,使用return的作用就是返回结果。throw的作用就是终止现在的事情出异常了,没法继续下去了,没有结果就。比如,你让一个人去做一件事情,做到一半的时候,那个人感冒了,没法做下去了,就把信息返回给你说“我感冒了,这件事做不下去了”,这个时候这件事没有结果了就。所以不能用return。但是throw是有前提的,必须具有可抛性。
这个时候我们javac编译的话,真的出现异常啦:
这里提示我们要对未报告的异常错误进行捕捉或者声明,那么为什么要声明一个异常呢,声明的意思就是说他可能会出现什么错误。我们设计一个功能,这个功能(函数)可能会出现异常,我们知道他可能出现哪些异常,比如你买了一个面包要给我吃,面包放了三天了,你得最起码要在面包上面贴个标签说:面包可能坏了。这个就是声明,方便我就可以采取相应的措施进行处理。如何声明一个异常呢?这里又用到了一个关键字叫做throws,但是这个声明是有条件的,thorws是给函数修饰的,谁可能出现异常就声明谁。如果函数可能会出现异常,我们要在函数上声明异常,否则会编译失败。如果调用了声明异常的函数,你必须要处理,不处理一样也是编译失败,处理问题之一就是抛出问题。最终把异常抛给虚拟机了。
也就是说要想让一个类成为异常类,必须先让他继承异常体系,因为只有具备异常体系的共性,才具有可抛性,才可以被throws 还有 throw来操作。
修改代码如下:
1 class Demo1 2 { 3 4 int[] arr = new int[3]; 5 6 } 7 8 class ExcepDemo1 9 { 10 11 /** 12 *这个main方法可能出现错误,所以这个时候就该throws Exception 声明这个异常 13 */ 14 public static void main(String[] args) throws Exception 15 { 16 17 Demo1 demo = new Demo1(); 18 int num=6; 19 if(num>demo.arr.length){ 20 21 throw new Exception("the num is too large"); 22 23 }else{ 24 25 System.out.println(demo.arr[num]); 26 27 } 28 29 } 30 31 }
抛出的异常结果为:
这里就是我们自定义的异常信息。但是我们发现这个异常信息提示的是java.lang.Exception,如果冷不丁来这么个异常,我们也不知道问题出在那里哇?肿么办?我们自定义个非常好理解的异常来说下,如何定义一个异常?我们只需要定义一个类然后继承自Exception,然后覆盖相应的方法就可以了。下面我们来自定义一下:
1 /** 2 *这个地方我们命名的时候最好是以Exception结尾,这样可以增强阅读性 3 */ 4 class MyOutofArrayIndexException extends Exception 5 { 6 /**继承完之后,我们来覆盖下Exception当中的方法,具体方法去查手册可知**/ 7 public MyOutofArrayIndexException(String s) 8 { 9 10 super(s); 11 12 } 13 14 } 15 16 class Demo1 17 { 18 19 int[] arr = new int[3]; 20 21 } 22 23 class ExcepDemo1 24 { 25 26 /** 27 *这个main方法可能出现错误,所以这个时候就该throws Exception 声明这个异常 28 */ 29 public static void main(String[] args) throws MyOutofArrayIndexException 30 { 31 32 Demo1 demo = new Demo1(); 33 int num=6; 34 if(num>demo.arr.length){ 35 36 throw new MyOutofArrayIndexException("the num is too large"); 37 38 }else{ 39 40 System.out.println(demo.arr[num]); 41 42 } 43 44 } 45 46 }
此时运行之后就体现了我们自定义的异常信息了。
那么我们每次调用个自定义的异常多麻烦啊,我们要是不用声明异常该多好啊。我们知道在我们第一个写的来演示异常的代码的时候,我们并没有声明异常啊,不也是照样过了。为什么自定义就要声明呢?这是因为啊,在我们没写的情况下,当时调用的是RuntimeException这个对象有个特点就是,编译的时候并不对他进行检查,只有在执行的时候才检查,如果有异常就调用。这样多好,我们不用声明啦,直接继承他看看。
1 /** 2 *这个地方我们继承了RuntimeException,也就是运行时异常,编译时并不检查 3 */ 4 class MyOutofArrayIndexException extends RuntimeException 5 { 6 7 public MyOutofArrayIndexException(String s) 8 { 9 10 super(s); 11 12 } 13 14 } 15 16 class Demo1 17 { 18 19 int[] arr = new int[3]; 20 21 } 22 23 class ExcepDemo1 24 { 25 26 27 public static void main(String[] args) 28 { 29 30 Demo1 demo = new Demo1(); 31 int num=6; 32 if(num>demo.arr.length){ 33 34 throw new MyOutofArrayIndexException("the num is too large"); 35 36 }else{ 37 38 System.out.println(demo.arr[num]); 39 40 } 41 42 } 43 44 }
这个是编译通过的。
异常的分类:
1、编译时被检测异常:Exception 和 其子类 除了 RuntimeException类除外都是。这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应针对的处理方式。
2、编译时不检测异常(运行时检测)RuntimeException 及其子类都是,编译器并不检测这个。这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的,或者引发了内部状态的改变导致的。这种问题一般不处理,在运行时,让程序强制停止,让调用者对代码进行修改。RuntimeException是在正常运行之中,出现异常。而Error直接让虚拟机停掉。
自定义异常一个是继承Exception一个是继承自RuntimeException。
throw throws的区别
throws使用在函数上,throw是用在函数当中。
throws 声明异常类,可以声明多个,throw抛出的是异常对象。