java中的异常
1.异常介绍
异常:程序运行过程中发生的一些问题[非正常状态]。Exception。【程序出现异常都是正常现象】
在Java中将很多常见的异常(问题)进行总结和抽象的描述。在JDK中,Java封装好了很多很多常见的一些异常类。我们在开发中程序会发生各种各样的异常(问题),需要根据这些类名以及相关的报错信息进行分析。、
2.异常的体系结构
在进行问题过程中,形成了异常的体系结构:
Throwable:它是异常的顶层父类(它的父类是Object)。
Error:它是错误的问题顶层父类。
Exception:它是一些常见的异常问题的顶层父类。
我们讲解的异常主要针对的Exception这类问题。而针对Error问题,这时就必须对程序的架构、设计、代码进行重新的调整。
3.异常的应用
3.1通过异常显示告诉使用者问题
// 定义圆
class Circle{
// 半径
private double radius;
// 圆周率
private static double PI = Math.PI;
// 定义构造函数
public Circle( double radius ){
// 健壮性判断。函数接收数据,在应用之前,需要判断,防止非法数据
if( radius <= 0 ){
/*
* 这里需要使用Java中的异常手动的告诉对方,传递的数据有问题,并且程序不再运行
* 需要使用Java中已经存在的某个异常类对象,通过这个对象将问题抛给当前的调用者
* 如果在程序中需要将异常问题抛给使用者,必须使用throw关键字
* 抛出异常的格式:
* throw new 异常类("异常的信息");
*/
throw new IllegalArgumentException( "圆的半径不能小于零!!!" );
}
this.radius = radius;
}
// 计算面积
public double getArea(){
return PI * radius * radius ;
}
}
public class ExceptionDemo {
public static void main(String[] args) {
Circle c = new Circle( -2 );
double area = c.getArea();
System.out.println(area);
}
}
3.2 异常的抛出和捕获
两个角色:
1、函数的定义者:如果是自己在定义函数(功能)时,如果发现程序中有问题,并且这个问题不是自己代码造成的,而是由于调用者数据等其他原因造成。这时我们就可以在自己的函数中,将这个问题抛给调用这个函数的使用者。
2、函数的调用者:如果我们调用别人的函数,被调用的函数中有异常问题抛出来,我们可以在调用语句的地方去截获这个问题,然后将这个问题在自己调用的过程中进行处理掉。
抛出throw关键字:在程序中遇到问题,可以找到问题对应的异常类,然后使用throw 将问题对象抛出给调用者。
捕获处理异常:如果调用别人的程序,遇到异常,这时我们尽可能将异常捕获掉(有时有些问题我们必须将其给其他使用者继续暴漏出去)。
抛异常的格式:
throw new 异常类名( ”异常的信息” );
package com.click369.test2;
/**
* 创建整数数组
* @author Administrator
*/
public class CreateIntArray {
private int size; //保存数组大小的变量
public CreateIntArray(int num){
if(num<=0){
//抛出异常
/*
抛异常的格式:
throw new 具体异常类名( ”异常的信息” );
*/
throw new RuntimeException("数组大小不能小于等于0");
}
this.size=num;
}
/**
* 得到int型的数组
* @return
*/
public int[] getArray(){
int arr[]=new int[size];
return arr;
}
}
3.3 捕获异常
捕获异常的格式:
try{
可能有异常的代码(调用语句);
}catch( 异常类名 变量名 ){
处理异常的代码;
}
public class ExceptionDemo {
public static void main(String[] args) {
try{
// 书写可能有异常的代码
Circle c = new Circle( -2 );
double area = c.getArea();
System.out.println(area);
}catch( IllegalArgumentException e ){
/*
* 捕获到异常之后,应该如何处理的代码书写位置
* 经常在catch中,使用e.printStackTrace() 打印异常信息
* 同时可能会将异常进行其他的转换。
*/
System.out.println("异常啦!!!!");
e.printStackTrace();
//System.err.println("111111111111111");
}
System.out.println(".......");
}
}
4.异常的分类
关于Java中的异常分类:
Exception:它是异常的顶层父类。如果在程序中直接看到Exception表明当前的异常属于编译时期会被检测的异常(编译时期异常)。
RuntimeException:它属于运行时期异常(编译的时候不会被检测)。大部分情况下,我们都会使用运行时期异常。
如果我们在程序中,使用throw关键字,抛出了编译时期会被检测的异常,这时必须有严格解决方案,否则程序无法编译通过。但是如果我们使用了RuntimeException异常,编译的时候,编译器不检测,因此不用考虑编译的问题,但是运行的过程中我们使用throw关键字将其抛出,这些异常也会抛给调用者。
/*
* 演示 Exception和RuntimeException两个异常
*/
class Demo{
// 只算正数的和值
public int getSum( int a , int b ){
if( a <= 0 || b <= 0 ){
// 告诉使用者,数据有问题 , 抛出的运行的异常
throw new RuntimeException("传递的数据只能正数!!!");
}
return a + b;
}
/*
* 计算商
* 在定义函数的过程中,函数中有编译时期的异常,
* 那么我们就必须在自己的函数中对异常做处理,不处理自己的函数无法编译通过。
* 如果自己的函数中的异常,必须让调用者知道,这时可以在函数上使用throws 关键字,对异常进行声明
* 如果这个异常不需要对方知道,可以将异常在自己的函数中捕获。
*/
public int div( int a , int b) throws Exception {
// 保证除数不能为零
if( b == 0 ){
throw new Exception("除数不能为零!!!");
}
return a / b;
}
}
public class ExceptionDemo2 {
public static void main(String[] args) throws Exception {
Demo d = new Demo();
int sum = d.getSum(1, 4);
System.out.println(sum);
int div = d.div(12, 0);
System.out.println(div);
}
}
在程序中遇到不管是什么异常:
处理方案都是两种:
1、在函数上使用throws关键字,将函数中的问题直接声明出去。
2、在函数中使用try-catch代码将问题进行捕获。
注意:只要是代码中出现编译时期的异常,不管是在定义函数时函数中的编译异常,还是作为调用者,被调用的函数上有编译异常。都需要给出对应的处理方案。
大部分情况下:
定义函数时,如果函数中有编译异常, 我们都会使用throws关键字将异常显示声明在函数上,目的是让调用者知道。
如果是调用函数,那么肯定在编写调用代码的时候,就会知道函数上有问题,这时作为调用者,一般都会使用try-catch处理这个异常。当然也可以在函数上将异常继续声明给其他的调用者。
5.自定义异常
本身在JDK中,已经存在很多的类来表示不同的异常问题。
ArrayIndexOutOfBoundsException:数组角标越界异常
NullPoniterException:空指针异常
ClassCastException:类型转换异常
......
后期开发中,难免还会遇到一些异常问题,但是这些问题在JDK中是没有对应的异常类对其进行的描述。
这时就需要我们开发者自己认为的根据异常定义相关的异常类。然后在程序中应用这些自己定义的异常类。
自定义异常,就是在定义一个类。只不过这个类不是普通的(一般的)类,要想一个类能够表示某中异常,这时定义好的这个类,必须去继承JDK中异常存在的某个异常类(Exception、RuntimeException)。
/*
* 自定义异常
*/
// 1、定义类,继承Exception或RuntimeException
public class RadiusException extends Exception{
// 2、在类中提供构造函数
public RadiusException(){}
public RadiusException(String message){
super(message);
}
}
异常类的书写步骤:
1.让当前类继承Exception或RuntimeException。
2.在类中提供构造函数(提供空参数构造函数,和可以接收一个字符串的构造函数)。
6.异常的细节
6.1 捕获的其他组合
常用:
try{
可能会有异常的代码
}catch( 异常类名 变量名 ){
处理捕获的异常
}
其他的组合方式:
try{
可能会有异常的代码
}catch( 异常类名 变量名 ){
处理捕获的异常
}finally{
永远都会被执行的代码
}
/*
* 演示try-catch-finally
*/
class Demo4{
public int show( int x ) {
try{
if( x % 2 == 0 ){
throw new RuntimeException("x % 2 == 0 ");
}
if( x % 3 == 0 ){
throw new Exception(" x % 3 == 0 ");
}
return 1;
}catch( Exception e){
/*
* 上面的代码发生了异常,被catch捕获到了,
* 程序开始执行catch中的代码,本身应该是将catch中的return 2
* 执行完成,将数字2作为返回值。但是由于程序有finally代码块
* 而finally代码块是永远都需要被执行的代码。所以本身应该执行return 2 的时候
* JVM去执行的finally代码块,但是在finally中有个return 3 ,导致将finally中的
* 3 作为返回值返回了。
*/
System.out.println(".....");
return 2;
}finally{
return 3;
}
//return return 4;
}
}
public class ExceptionDemo4 {
public static void main(String[] args) {
Demo4 d = new Demo4();
int x = d.show(5);
System.out.println("over..... x = " + x);
}
}
try-finally组合:这个组合,其实并没有真正的去捕获异常。因此这种组合不能解决编译时期的异常问题。这种组合的出现仅仅是为了保证程序中的某些代码不管有没有异常,都一定会被执行的。
try-catch-catch-catch...... {} finally{}一个try对应多个catch代码块
当我们的try中的可能发生异常的代码中存在很多个异常的时候,需要根据每个异常给出不同的处理方案,这时就需要书写多个catch,将不同的异常分别捕获到,在catch中书写具体的每个异常的处理办法。
6.2 方法复写中的异常问题
如果子类复写父类的方法:
1、如果父类的方法上没有使用throws声明异常,子类复写完的方法上也不能使用throws关键字声明异常。
2、如果父类上使用throws声明了异常,这时子类复写完之后可以不声明异常。
3、如果父类上使用throws声明了多个异常,子类可以使用throws声明多个异常中的某几个。
4、如果父类上使用throws声明了异常,子类复写的方法上可以使用throws声明当前父类声明的这个异常的子类异常。
总结:
1.error和exception有什么区别?
Error(错误)表示系统级的错误和程序不必处理的异常,是java运行环境中的内部错误或者硬件问题。比如:内存资源不足等。对于这种错误,程序基本无能为力,除了退出运行外别无选择,它是由Java虚拟机抛出的。
Exception(违例)表示需要捕捉或者需要程序进行处理的异常,它处理的是因为程序设计的瑕疵而引起的问题或者在外的输入等引起的一般性问题,是程序必须处理的。
2.运行时异常与非运行时异常的区别?
java运行时异常是可能在java虚拟机正常工作时抛出的异常。
java提供了两种异常机制。一种是运行时异常(RuntimeExepction),一种是检查式异常(checked execption)(非运行时异常)。
检查式异常:我们经常遇到的IO异常及sql异常就属于检查式异常。对于这种异常,java编译器要求我们必须对出现的这些异常进行catch 所以 面对这种异常不管我们是否愿意,只能自己去写一堆catch来捕捉这些异常。
运行时异常:我们可以不处理。当出现这样的异常时,总是由虚拟机接管。比如:我们从来没有人去处理过NullPointerException异常,它就是运行时异常,并且这种异常还是最常见的异常之一。
常见五种运行时异常:
ClassCastException(类转换异常)
IndexOutOfBoundsException(数组越界)
NullPointerException(空指针)
ArrayStoreException(数据存储异常,操作数组时类型不一致)
还有IO操作的BufferOverflowException异常
3.finally语句到底是在return之前还是之后执行?
finally块的语句在try或catch中的return语句执行之后返回之前执行且finally里的修改语句可能影响也可能不影响try或catch中 return已经确定的返回值,若finally里也有return语句则覆盖try或catch中的return语句直接返回。
4.java异常处理流程
1、当程序运行时出现异常之后,那么会由JVM自动根据异常的类型实例化一个与之类型匹配的异常类对象,(此处用户不用去关系 new ,由系统自动负责处理)
2、产生异常对象之后会判断当前语句是否存在异常处理,如果没有,那么交给JVM进行默认的异常处理,处理方式为,输出异常信息,并结束程序调用。
3、如果存在异常捕获操作,那么由try语句来捕获产生的异常类实例化对象,而后与每一个try语句后的每一个catch进行比较,如果有合适的捕获类型,则使用当前的catch语句进行异常的处理,如果不匹配,则继续向下匹配其他的catch 。
4、不管最后异常处理时候能够匹配,都要向后执行,如果程序中存在finally语句,那么就先执行finally中的代码,但是执行完毕后需要根据之前的catch匹配结果来决定如何执行,如果之前成功捕获异常,那么就继续执行finally之后的代码,如果之前没有成功捕获,那么将此异常交给JVM进行默认处理(输出异常信息,结束程序运行)