Java基础:Java异常体系

一、异常体系简介

异常是指由于各种情况,导致程序中断运行的一种指令流,例如:文件找不到、非法参数等等。为了保证程序正常运行,在设计程序之初,我们就要考虑各种异常情况,并正确处理他。异常也是一种对象,java中定义Throwable作为所有异常的超类。Thorwable类(表示可抛出)是所有异常和错误的超类,两个直接子类为Error和Exception,分别表示错误和异常。

Java异常体系结构图,如下

Java基础:Java异常体系_第1张图片

Throwable:包括两个重要的子类:Exception(异常)和Error(错误)。

1.Error(错误):是程序中无法处理的错误,表示运行应用程序中出现了严重的错误。此类错误一般表示代码运行时JVM出现问题。通常有Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。比如说当jvm耗完可用内存时,将出现OutOfMemoryError。此类错误发生时,JVM将终止线程。

这些错误是不可查的,非代码性错误。因此,当此类错误发生时,应用不应该去处理此类错误。

2.Exception(异常):程序本身可以捕获到并且可以处理的异常

Exception异常又分为两类:运行时异常和非运行时异常

1.运行时异常(非受检异常)运行时异常都是RuntimeException类及其子类异常,如NullPointerException、IndexOutOfBoundsException等,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生。

2.非运行时异常(受检异常):非运行时异常是RuntimeException以外的异常,类型上都属于Exception类及其子类。从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。

可查异常与不可查异常:java的所有异常可以分为可查异常(checked exception)和不可查异常(unchecked exception)。

1、可查异常:编译器要求必须处理的异常。正确的程序在运行过程中,经常容易出现的、符合预期的异常情况。一旦发生此类异常,就必须采用某种方式进行处理。除RuntimeException及其子类外,其他的Exception异常都属于可查异常。编译器会检查此类异常,也就是说当编译器检查到应用中的某处可能会此类异常时,将会提示你处理本异常——要么使用try-catch捕获,要么使用throws语句抛出,否则编译不通过。

2、不可查异常:编译器不会进行检查并且不要求必须处理的异常,也就说当程序中出现此类异常时,即使我们没有try-catch捕获它,也没有使用throws抛出该异常,编译也会正常通过。该类异常包括运行时异常(RuntimeException极其子类)和错误(Error)。

二、异常处理流程

在java应用中,异常处理机制分别为抛出异常和捕获异常

抛出异常:当一个方法出现错误而引发异常时,该方法会将异常类型以及异常出现时的程序状态封装为异常对象,并且交给本应用。运行时,应该将寻找处理异常的代码并执行。任何代码都可以通过Throw关键字抛出异常。

捕获异常:一旦方法抛出异常,系统自动根据该异常对象寻找合适异常处理器(Exception Handler)来处理该异常。所谓合适类型的异常处理器指的是异常对象类型和异常处理器类型一致

对于不同的异常,java采用不同的异常处理方式:

1、运行异常将由系统自动抛出,应用本身可以选择处理或者忽略该异常。

2、对于方法中产生的Error,该异常一旦发生JVM将自行处理该异常,因此java允许应用不抛出此类异常。

3、对于所有的可查异常,必须进行捕获或者抛出该方法之外交给上层处理。也就是当一个方法存在异常时,要么使用try-catch捕获,要么使用该方法使用throws将该异常抛调用该方法的上层调用者。

2.1捕获异常

1、try—catch语句

try {

            //可能产生的异常的代码区,也成为监控区

			  }catch (ExceptionType1 e) {
			
			            //捕获并处理try抛出异常类型为ExceptionType1的异常
			
			  }catch(ExceptionType2 e) {
			
			            //捕获并处理try抛出异常类型为ExceptionType2的异常

			  }

监控区一旦发生异常,则会根据当前运行时的信息创建异常对象,并将该异常对象抛出监控区,同时

系统根据该异常对象依次匹配catch子句,若匹配成功(抛出的异常对象的类型和catch子句的异常类的类型或者是该异常类的子类的类型一致),则运行其中catch代码块中的异常处理代码,一旦处理结束,那就意味着整个try-catch结束。含有多个catch子句,一旦其中一个catch子句与抛出的异常对象类型一致时,其他catch子句将不再有匹配异常对象的机会。

try---catch:如果代码运行出现了错误,那么执着catch,但不能影响后边代码运行

public static void main(String[] args) {
		int[] a = new int[5];
		try {
			for (int i = 0; i <10; i++) {
				a[i] = i;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println("----------------");
	}

运行结果:

2、try-catch-finally

 try {

       //可能产生的异常的代码区

   }catch (ExceptionType1 e) {

       //捕获并处理try抛出异常类型为ExceptionType1的异常

   }catch (ExceptionType2 e){

       //捕获并处理try抛出异常类型为ExceptionType2的异常

   }finally{

       //无论是出现异常,finally块中的代码都将被执行

   }

3、try-catch-finally 代码块执行的顺序:

 A)try没有捕获异常时,try代码块中的语句依次被执行,跳过catch。如果存在finally则执行finally代码块,否则执行后续代码。

      B)try捕获到异常时,如果没有与之匹配的catch子句,则该异常交给JVM处理。如果存在finally,则其中的代码仍然被执行,但是finally之后的代码不会被执行。

      C)try捕获到异常时,如果存在与之匹配的catch,则跳到该catch代码块执行处理。如果存在finally则执行finally代码块,执行完finally代码块之后继续执行后续代码;否则直接执行后续代码。另外注意,try代码块出现异常之后的代码不会被执行。(见下图:)

Java基础:Java异常体系_第2张图片

4、 try、catch、finally三个语句块应注意的问题

第一:try、catch、finally三个语句块均不能单独使用,三者可以组成 try...catch...finally、try...catch、try...finally三种结构,catch语句可以有一个或多个,finally语句最多一个。

    第二:try、catch、finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。

    第三:多个catch块时候,最多只会匹配其中一个异常类且只会执行该catch块代码,而不会再执行其它的catch块,且匹配catch语句的顺序为从上到下,也可能所有的catch都没执行。

      第四:先Catch子类异常再Catch父类异常。

Java基础:Java异常体系_第3张图片

Java基础:Java异常体系_第4张图片

主要:

finally 异常中总能执行的代码区

final 用于定义常量的关键字

finalize() 垃圾回收,该方法是系统调用,不用程序员手动调用

2.2抛出异常

1、throws抛出异常

如果一个方法可能抛出异常,但是没有能力处理该异常或者需要通过该异常向上层汇报处理结果,可以在方法声明时使用throws来抛出异常。这就相当于计算机硬件发生损坏,但是计算机本身无法处理,就将该异常交给维修人员来处理。

publicmethodName throws Exception1,Exception2….(params){}

其中Exception1,Exception2…为异常列表一旦该方法中某行代码抛出异常,则该异常将由调用该方法的上层方法处理。如果上层方法无法处理,可以继续将该异常向上层抛。

throws:向外抛出异常,让之后一个接受到该异常的方法区处理

	public static void main(String[] args) {
		
		Test test = new Test();
		try {
			test.run();
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		System.out.println("--------------");
		
	}
	
	public void run() throws CloneNotSupportedException {
		this.sun();
	}
	
	public void sun() throws CloneNotSupportedException {
		User user01 = new User();
		User user02 = user01.clone();
		System.out.println(user01);
		System.out.println(user02);
		user01.ChangedPerson("李四");
		System.out.println(user01.getPerson());
		System.out.println(user02.getPerson());
	}

2、throw抛出异常

在方法内,用throw来抛出一个Throwable类型的异常。一旦遇到到throw语句,后面的代码将不被执行。然后,便是进行异常处理——包含该异常的try-catch最终处理,也可以向上层抛出。注意我们只能抛出Throwable类和其子类的对象。

throw newExceptionType;

比如我们可以抛出:throw new Exception();

也有时候我们也需要在catch中抛出异常,这也是允许的,比如说:

Try{
//可能会发生异常的代码

}catch(Exceptione){
      throw newException(e);

}

3、throws和throw的区别

throws关键字

定义一个方法的时候可以使用throws关键字声明。使用throws关键字声明的方法表示此方法不处理异常,而交给方法调用处进行处理。

throws关键字格式:

public class ThrowsDemo01 {
		 public static void main(String args[]){
		        Math m = new Math() ;        // 实例化Math类对象
		        try{
		            System.out.println("除法操作:" + m.div(10,2)) ;
		        }catch(Exception e){
		            e.printStackTrace() ;    // 打印异常
		        }
		    }
	}
	class Math{
	    public int div(int i,int j) throws Exception{    // 定义除法操作,如果有异常,则交给被调用处处理
	        int temp = i / j ;    // 计算,但是此处有可能出现异常
	        return temp ;
	    }
	};

因为div使用了throws关键字声明,所以调用此方法的时候,方法必须进行异常处理。通过try...catch;

如果在主方法的声明也使用了throws关键字呢,那么是不是意味着主方法也可以不处理异常。

public class Shu {


	public static void main(String args[]) throws Exception{
        Math m = new Math() ;        // 实例化Math类对象
        System.out.println("除法操作:" + m.div(10,0)) ;
        System.out.println("我还可以输出");
    }
}
class Math{
public int div(int i,int j) throws Exception{    // 定义除法操作,如果有异常,则交给被调用处处理
    int temp = i / j ;    // 计算,但是此处有可能出现异常
    return temp ;
}
};

在本程序中,主方法不处理任何异常,而交给JAVA中最大头JVM,所以如果在主方法使用了throws关键字,则表示一切异常交给JVM进行处理。

throw关键字

throw关键字作用是抛出一个异常,抛出的时候是一个异常类的实例化对象,

在异常处理中,try语句要捕获的是一个异常对象,那么此异常对象也可以自己抛出。

package methoud;
public class ThisDemo06{
    public static void main(String args[]){
        try{
            throw new Exception("自己抛着玩的。") ;    // 抛出异常的实例化对象
        }catch(Exception e){
            System.out.println(e) ;
        }
    }
};

 Java基础:Java异常体系_第5张图片

4、 常异总结

此部分可以api文档中进行查阅,这里仅做参考。

常见异常:

java.lang.IllegalAccessError:违法访问错误。当一个应用试图访问、修改某个类的域(Field)或者调用其方法,但是又违反域或方法的可见性声明,则抛出该异常。

java.lang.InstantiationError:实例化错误。当一个应用试图通过Java的new操作符构造一个抽象类或者接口时抛出该异常.

java.lang.OutOfMemoryError:内存不足错误。当可用内存不足以让Java虚拟机分配给一个对象时抛出该错误。
java.lang.StackOverflowError:堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出或者陷入死循环时抛出该错误。

java.lang.ClassCastException:类造型异常。假设有类A和B(A不是B的父类或子类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。
java.lang.ClassNotFoundException:找不到类异常。当应用试图根据字符串形式的类名构造类,而在遍历CLASSPAH之后找不到对应名称的class文件时,抛出该异常。

java.lang.ArithmeticException:算术条件异常。譬如:整数除零等。
java.lang.ArrayIndexOutOfBoundsException:数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

java.lang.IndexOutOfBoundsException:索引越界异常。当访问某个序列的索引值小于0或大于等于序列大小时,抛出该异常。
java.lang.InstantiationException:实例化异常。当试图通过newInstance()方法创建某个类的实例,而该类是一个抽象类或接口时,抛出该异常。

java.lang.NoSuchFieldException:属性不存在异常。当访问某个类的不存在的属性时抛出该异常。
java.lang.NoSuchMethodException:方法不存在异常。当访问某个类的不存在的方法时抛出该异常。
java.lang.NullPointerException:空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。
java.lang.NumberFormatException:数字格式异常。当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。

java.lang.StringIndexOutOfBoundsException:字符串索引越界异常。当使用索引值访问某个字符串中的字符,而该索引值小于0或大于等于序列大小时,抛出该异常。

其他异常:
java.lang.AbstractMethodError:抽象方法错误。当应用试图调用抽象方法时抛出。
java.lang.AssertionError:断言错。用来指示一个断言失败的情况。
java.lang.ClassCircularityError:类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。
java.lang.ClassFormatError:类格式错误。当Java虚拟机试图从一个文件中读取Java类,而检测到该文件的内容不符合类的有效格式时抛出。
java.lang.Error:错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。
java.lang.ExceptionInInitializerError:初始化程序错误。当执行一个类的静态初始化程序的过程中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static语句段。
java.lang.IncompatibleClassChangeError:不兼容的类变化错误。当正在执行的方法所依赖的类定义发生了不兼容的改变时,抛出该异常。一般在修改了应用中的某些类的声明定义而没有对整个应用重新编译而直接运行的情况下,容易引发该错误。
java.lang.InternalError:内部错误。用于指示Java虚拟机发生了内部错误。
java.lang.LinkageError:链接错误。该错误及其所有子类指示某个类依赖于另外一些类,在该类编译之后,被依赖的类改变了其类定义而没有重新编译所有的类,进而引发错误的情况。
java.lang.NoClassDefFoundError:未找到类定义错误。当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。
java.lang.NoSuchFieldError:域不存在错误。当应用试图访问或者修改某类的某个域,而该类的定义中没有该域的定义时抛出该错误。
java.lang.NoSuchMethodError:方法不存在错误。当应用试图调用某类的某个方法,而该类的定义中没有该方法的定义时抛出该错误。
java.lang.ThreadDeath:线程结束。当调用Thread类的stop方法时抛出该错误,用于指示线程结束。
java.lang.UnknownError:未知错误。用于指示Java虚拟机发生了未知严重错误的情况。
java.lang.UnsatisfiedLinkError:未满足的链接错误。当Java虚拟机未找到某个类的声明为native方法的本机语言定义时抛出。
java.lang.UnsupportedClassVersionError:不支持的类版本错误。当Java虚拟机试图从读取某个类文件,但是发现该文件的主、次版本号不被当前Java虚拟机支持的时候,抛出该错误。
java.lang.VerifyError:验证错误。当验证器检测到某个类文件中存在内部不兼容或者安全问题时抛出该错误。
java.lang.VirtualMachineError:虚拟机错误。用于指示虚拟机被破坏或者继续执行操作所需的资源不足的情况。
java.lang.ArrayStoreException:数组存储异常。当向数组中存放非数组声明类型对象时抛出。
java.lang.CloneNotSupportedException:不支持克隆异常。当没有实现Cloneable接口或者不支持克隆方法时,调用其clone()方法则抛出该异常。
java.lang.EnumConstantNotPresentException:枚举常量不存在异常。当应用试图通过名称和枚举类型访问一个枚举对象,但该枚举对象并不包含常量时,抛出该异常。
java.lang.Exception:根异常。用以描述应用程序希望捕获的情况。
java.lang.IllegalAccessException:违法的访问异常。当应用试图通过反射方式创建某个类的实例、访问该类属性、调用该类方法,而当时又无法访问类的、属性的、方法的或构造方法的定义时抛出该异常。
java.lang.IllegalMonitorStateException:违法的监控状态异常。当某个线程试图等待一个自己并不拥有的对象(O)的监控器或者通知其他线程等待该对象(O)的监控器时,抛出该异常。
java.lang.IllegalStateException:违法的状态异常。当在Java环境和应用尚未处于某个方法的合法调用状态,而调用了该方法时,抛出该异常。
java.lang.IllegalThreadStateException:违法的线程状态异常。当县城尚未处于某个方法的合法调用状态,而调用了该方法时,抛出异常。
java.lang.InterruptedException:被中止异常。当某个线程处于长时间的等待、休眠或其他暂停状态,而此时其他的线程通过Thread的interrupt方法终止该线程时抛出该异常。
java.lang.NegativeArraySizeException:数组大小为负值异常。当使用负数大小值创建数组时抛出该异常。
java.lang.SecurityException:安全异常。由安全管理器抛出,用于指示违反安全情况的异常。
java.lang.TypeNotPresentException:类型不存在异常。

你可能感兴趣的:(java,开发语言,后端)