异常:在java语言中,将程序执行中发生的不正常情况称为异常
- 开发过程中的语法错误和逻辑错误不是异常
异常可以分为Error和Exception
ErrorError:java虚拟机无法解决的严重问题(Error发生后程序员无法作为,这个时候java虚拟机(jvm)已经停止运行了)
eg:
- jvm系统内部错误
- 资源耗尽等严重情况
Error的发生我们一般不编写针对性的代码进行处理(因为这个时候jvm已经停止运行了,程序员无法解决)
Error的举例: 栈溢出:java.lang.StackOverflowError eg:在main方法中调用main方法(就会一直重复调用main方法,会一直消耗栈内存的空间,也就会发生占内存溢出)
堆溢出:java.lang.OutOfMemoryError(简称为OOM) eg:Inter [] arr=new Integer[1024 * 1024 * 1024];(由于new对象是在堆内存中开辟空间,而堆内存也是有限的,当在堆内存中创建的空间太多的时候,就会产生堆溢出)
Exception其他因编程错误或偶然的外在因素导致的一般性问题,可使用针对性的代码进行处理
Exception的举例空指针访问
试图读取不存在的文件
网络连接中断
数组角标越界
- 广义的异常:Error+Exception
- 狭义的异常:Exception
对于我们遇到的这些错误(也就是异常),我们一般有两种解决的方式:
- 遇到错误就终止程序的运行
- 程序员在编写程序时就考虑到异常的检测,给出错误消息的提醒,以及对异常的处理
-
捕捉异常==处理异常
-
处理异常最理想的时间是在编译期间,但有的错误只有运行时才会发生
- Error我们一般不进行处理
这里我们主要讨论Exception
- 而Exception又分为编译时(编译时Exception)异常和运行时异常(运行时Exception)
-
Error也是运行时异常,也称为非受检异常
异常大家庭关系图见画图笔记
java.lang.Throwable
Error类的全类名java.lang.Error
Exception类的全类名java.lang.Exception
常见的编译时异常(checked) ---------IOException
---------FileNotFoundException
---------ClassNotFoundException
常见的运行时异常(unchecked) ---------NullPointerException(空指针异常)
---------ArrayIndexOutOfBoundsException(数组下标越界)
---------ClassCastException(类型装换异常)
---------NumberFormatException(格式转换异常)
---------InPutMismatchException(Scanner输入格式不兼容
---------ArithmeticException(算数异常)
面试题举例:常见的一场都有哪些?
- 这里我们举例时就要先分为Error和Exception,然后再在Exception里面分为编译时异常和运行时异常其具体分析
try — catch ---- finally
- 这种方式就相当于狼来了,自己感觉可以解决,直接自己解决掉
- 上面的狼就表示异常
throws + 异常类型
- 这种方式就相当于狼来了,但是自己解决不了,就要用throws把这个问题抛给更厉害的人,让厉害一点的人去再过自己的try ------ catch ------ finally去解决这个狼,如果还是解决不了的话就可以继续将这个狼通过throws抛给更加厉害的人去使用他的try ----- catch -----finally去解决.
- 如果抛到最后还是没能将这个异常解决的话,到最后这个异常还是会跑到main方法中来,最后就会交给java虚拟机(jvm)去解决,然后jvm肯定就会报出异常,然后用户看到异常头都大了
- 上面的狼也表示异常
抓抛模型分为两个过程:
- 抛
- 抓
程序正在执行的过程中,一旦出现异常就会在出现异常的代码处生成一个异常类的对象,并将此对象抛出
- java是一门面向对象的语言,java将异常也编写成了一个异常类
- 一旦抛出异常类的对象以后,其后的代码将不再执行
可以理解为异常的处理方式
- try----catch-----finally
- throws
-
空指针异常(NullPointerException)
eg:
int [] arr=null; System.out.println(arr[3]);
- 发生空指针异常就去找对象,看是不是某个为null的对象去调用一了些对象的属性和方法了.
-
数组角标越界(ArrayIndexOutOfBoundException)
eg:
int [] arr=new int[10]; System.out.println(arr[10]);
- 角标越界(IndexOutOfBoundException)有两种:分为数组角标越界和字符串角标越界(StringIndexOutOfBoundException)
- 类型转换异常(ClassCastException)
eg:
Object obj=new Date(); String str=(String)obj;//这里的Data类型的引用变量不能转为String类型,所以在这里会发生类型转换异常
-
NumberFormatException
eg:
String str="123"; str="abc"; int num =Integer.ParseInt(str);//这里由于str字符串中有英文字母,所以会出现格式转换异常
-
InputMisMatchException(eg:Scanner语句输入不匹配)
-
算数异常(ArithMeticException)
eg:
int x=10; int y=0; System.out.println(x/y);//这里由于坟墓分母是0,所以会出现算数异常以上都是一些运行书异常 常见的编译时异常举例:
eg:
public Static void main(String [] args){ File file = new File("hello.txt"); FileInputStream fis= new FileInputStream(file);//FileNotFoundException int data = fis.read(); //IOException while(data!=-1){ System.out.println((char)data); data=fis.read(); //IOException } fis.close(); //关闭输入流 //IOException }
- 上面的FileNotFoundException是IOException的一个子类
- 这个程序在javac.exe编译时就会出错
- 在编译阶段出错也就无法产生.class字节码文件
eg:
try{ //这里是异常发生的语句 }catch(异常类型1 变量名){ //处理异常的方式1 //这里是要对异常进行处理的语句 //这里我们一般使用两个常用的方法对异常进行处理 }catch(异常类型2 变量名){ //异常的处理方式2 }catch(异常类型3 变量名){ //异常的处理方式3 } ... finally{ //一定会执行的代码 }
- try就是起了一个监听目的
- try中就是要包住可能会出现异常的代码(其中可以以包一些没有异常的正常的代码,但尽量少包)
注意:一旦try中可能发生异常的语句真的发生异常之后,就会抛出异常对象,那么try中异常语句后的代码就会不再执行了
- 上面的try-----catch-----finally结构中的finally可以加也可以不加,就和switch语句中的defauly有点像
-
finally是可选的(可以写,也可以不写)
-
使用try将可能出现异常的代码包装起来,一旦出现异常,就会生成一个对应的异常类型的对象,根据此异常的类型去catch中匹配
-
一旦try中的异常对象匹配到某一个catch中时,就进入catch中进行异常的处理,一旦处理完成就跳出当前的try----catch结构(没有写finally的情况),继续执行其后的代码
-
catch中的异常类型如果没有子父类关系,谁声明在上谁声明在下都没有关系(但是如果catch中的异常存在字父类关系,则要求子类一定要定义在父类的上面,否则就会编译报错)
-
catch中常用的异常对象处理方式:
方式一: String(这里的String为返回值) getmessage()
方式二: printStackTrace() (这个函数没有返回值)
- 这两个异常处理方式,方式二更加常用
-
在try结构中声明的变量在出了try结构以后,就不能再被调用了
- 这时我们可以将声明变量进行在try结的外面,只在try结中进行赋值就行了
-
try ------catch----finally结构可以嵌套使用
体会一下通过使用try----catch—finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍旧可能报错(相当于我们使用try----catch----finally将一个编译时可能出现的异常延迟到运行时出现)
try-----catch------finally解决编译时异常:eg:
public static void main(args [] args){ File file=new File("hello.txt); FileInputStream fis=new FileInPutStream(file); // FileNotFoundException(可能回发生) int data = fis.read(); //可能会发生IOException While(data!=1){ System.out.Println((char)data); data=fis.read(); //IOException } fis.close(); //IOException //这里的FileNotFoundException是IOException的一个子类 }try ------catch-----finally中finally的使用
- finally是可选的,可以写也可以不写
- finally中的写的代码是一定会被执行的语句,即使是catch中又出现异常了(这个时候catch后的代码就不会执行了),try中又return语句,catch中有return语句等情况下,都也会执行finally中的语句
- 什么时候用finally"
- 像数据库链接,输入输出流,网络编程中的socket等资源等jvm是不能自动垃圾回收的,我们就需要自己手动的进行资源的释放,此时的资源释放就需要声明在finally中
开发中由于运行时异常比较常见,而且运行时异常在我们编写代码时不可见,所以我们通常不针对运行时异常
编写try-----catch------finally
而针对编译时的异常,我么就一定要考虑异常的处理
异常处理的方式二 throws+异常模型注意:
- “throw+异常模型”,写在方法的声明外,指此方法执行时可能会跑抛出异常
- 一旦当方法执行时,如果出现了异常,仍旧会在出现异常的代码处产生一个异常类的对象,此对象满足throws后面加的异常类型时,就会被抛出,异常代码后续的代码也就不再执行
- throws+异常类型的方式只是将异常抛给了方法的调用者,并没有真正的将异常给处理掉
-
try----catch-----finally才是真正的将异常给处理了
-
throws+异常类型的方式只是将编译时的异常给延期到运行时期再显现出来,这个时候编译肯定是可以通过了,但是运行时期如果我们没有处理的话还是会出现异常,那么这个时候我们当这个异常以运行时异常的类型出现的时候我们就要通过修改代码来彻底解决掉这个异常
子类重写父类方法抛出的异常不能大于父类中被重写的方法抛出的异常类型
- 如果说父类方法抛出了异常,那么子类重写方法抛出的异常类型不大于父类中被重写的方法抛出的异常类型,不大于,自然也可以没有,也就是子类重写方法可以不抛出异常(但是前提要是真的没有产生异常,如果产生了异常的话还是要抛出,或者就使用try-----catch------finally处理异常)
因为在实际的编程中,如果使用到了多态时,这个时候我们编译期间看的是左边(也就是父类类型),而在运行阶段看的是右边(也就是子类重写后的方法),这个时候我们接对多态方式调用的子类重写方法使用try------ catch------finally的方式去直接用IDEA生成一个try----catch------finally来解决这个异常的话,那么由于我们在编译期间看的是左边,所以看的是父类类型,也就是会以父类方法抛出的异常类型为模板生成try----catch-----finally,但是这个时候实际这个方法调用的是子类的方法,也就是这个时候我们产生的是一个子类重写方法抛出的异常,如果这个时候子类可以产生比父类异常还大的异常,那么这个时候这个catch就解决不了这个异常了,所以我们规定了子类重写父类的方法抛出的异常类型只能是比父类抛出的异常类型要小
- 父类被重写的方法不抛出异常时,子类重写的方法也不能抛出异常
- 如果是父类中被重写的方法没有使用throws方式抛出异常,那么在子类中重写的方法也就不能抛出异常
- 那么这个时候如果子类中出现了异常我们要怎么办?
-
- 这个时候如果子类重写方法中出现了异常,那么我们就要使用try----catch-----finally方式来真正的处理掉这个异常,且要保证在catch中也不能出现异常,如果在catch中也出现了异常的话,我们就要使用嵌套结构的try--------catch------finally来处理这个异常,也就是结构套结构
- 如果执行的方法a中有先后调用了几个另外的方法(这几个方法是使用递进关系调用的),这时候我们一般使用throws的方式来一层一层的将这个异常向上级调用的方法中去抛出,直到抛到主方法或者是根据具体的实际情况来决定,最后再通过try----catch----finally方式统一去解决这个异常
- 这里我们要将这个方法抛出的异常一层一层的向上级抛出而不是在这个方法中就进行解决,因为如果我们在这个方法中就解决了这个异常的话,如果这个时候真的发生了异常,这个时候就从发生异常的代码处就不会再执行接下来的代码了,这个时候我们如果是使用throws的方式一层一层的方式向上级抛出时就不会出现这种情况,就不会影响到我们代码运行的功能
- 我们不建议throws与try-----catch-----finally的方式一起使用在一个方法中,在一个可能抛出异常的方法中,如果我们使用try-----catch------finally的方式已经解决了这个方法中出现的异常的话,这个时候我们再使用throws的方式去抛出一个异常,这时候我们又要去调用这个方法的地方去解决这个方法抛出的异常,这个时候就相当于,我们原本已经将异常解决了,但是这个时候我们又抛出了一个异常,这个异常就需要再去调用这个方法的地方去解决,不然就会报错,那么这个时候我们就相当于走了弯路
-
所以我们如果遇到了子类中重写方法中出现了比父类中被重写的方法的异常类型更大的情况时,我们就在这个子类重写方法中使用try-----catch------finally去解决这个异常,不能直接抛出,直接抛出异常编译就会出错
-
在嵌套调用方法时我们就需要对这个最后被调用的方法中一层一层开始抛出异常,一直抛出异常到main方法中或者具体情况而定,然后最后再使用try------catch------finally方式进行异常真正的解决
- 系统自动生成的异常对象
- 手动的生成一个异常对象,并且抛出(throw)
throw手动抛出异常
eg:
class Student{ private int id; public void regist(int id){ if(id>0){ this.id=id; }else{ //现在我们可以选择手动抛出一个异常对象 throw new RuntimeException("您输入的数据不合法");//这里抛出对象的形参是 //String message } } }
- 在上面这个例子中我们抛出了一个运行时的异常(RuntimeException),由于抛出的是一个运行时的异常,也就可以不在这里做异常处理,
- 而如果我们是抛出了一个编译时期的异常的话,我们就要立马去处理这个抛出的异常,也就是要去抛出或者是通过try----catch-----finally的方式去处理这个异常,或者是通过throws+异常类型的方式去处理这个异常,才能保证我们编译的通过,如果是使用了throws+异常类型的方式去处理这个异常的话其实也就是将编译时期的异常拖到了运行时期再让他显现出来,但是最终我们还是要通过改代码来解决了这个异常,或者就直接通过try-----catch------finally的方式直接将这个异常真正的处理掉,
throw是手动抛出异常,也就是创建了一个异常对象,throw是写在方法体中的
throws是抛出异常,向调用这个抛出异常的方法的地方去抛,在方法的声明后面加
eg:
public void feedAnimal()throws ClassCastException{//这里就抛出了一个类型转化异常 ... }在catch中解决异常的方法我们讲了两个:
- getMessage();
- getprintStackTrace();
- 以上两种方法都要我们通过异常类对象去调用
- 这两种方法我们推荐使用getPrintTrace();
手动抛出异常时我们创建异常对象要向这个异常类的构造方法中传递实参,这里我们传递的这个实参最终会传给异常类中的message属性
就是 throw new Runtime Exception(“发生了运行时异常”);
这个时候就是将字符串"发生了运行时异常"当做实参传递给了这个异常对象的message属性中,这时候我们再通过异常对象调用getMessage()方法输出的就是"发生了运行时异常"
用户自定义异常类java官方给了程序员自己定义异常类的方式,我们就可以自己定义一些异常类
如何自定义异常类?这里我们三步走:
第一步:继承现有的异常类结构
- 一般我们继承与RuntimeException或者Exception
注意:如果是继承了RuntimeException,那么可以不用显示的去处理这个异常,如果继承了Exception的话我们就要显示的去处理
-
- 因为如果是继承了RuntimeException的话,那也就表示我们自定义的这个异常也是一个运行时的异常,自然也就不需要处理就可以通过编译,就算发生异常也会是在运行时发生
-
- 若果是继承了Exception的话,那么就表示我们自定义的这个异常是一个编译时的异常,所以我们必须要进行try------catch------finally或者是throws+异常类型的处理,就可以通过编译
第二步:提供静态常量(全局常量):SeriaVersionUID(序列号)
- 其实也就是给这个静态变量随便赋一个long型的数据
eg:
static final long SeriaVersionUID=-7038456464831;
这一步我们会在IO流中细讲(输入流:FileInPutSteam 输出流:FileOutofSteam)
这个静态变量的定义是为了完成序列化(也就是作为这个异常类的身份z号一样)
序列化:就是通过网络将一个电脑的某个类传到另一个电脑中
第三部:提供重载的构造器
一般我们自定义两个构造方法就够了:
一个是无参空构造,另一个是初始化变量message的构造方法
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)