异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error;如果你用System.out.println(11/0),那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException 的异常。
这就极大的方便程序员修改代码。
检查性异常(编译时异常):最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
注意:编译时异常和运行时异常,都是发生在运行阶段。编译阶段异常是不会发生的。
那么编译时异常因为什么而得名?
因为编译时异常必须在编译(编写)阶段预先处理,如果不处理编译器就会报错,因此而得名。所有异常都是发生在运行阶段,因为只有程序运行阶段才可以new对象。而异常的发生就是new异常对象
错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
所有的异常类是从 java.lang.Exception 类继承的子类。
Exception 类是 Throwable 类的子类。除了Exception类外,
Throwable还有一个子类Error 。
Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
Error 用来指示运行时环境发生的错误。例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
Java 语言定义了一些异常类在 java.lang 标准包中。
标准运行时异常类的子类是最常见的异常类。由于 java.lang 包是默认加载到所有的 Java 程序的,所以大部分从运行时异常类继承而来的异常都可以直接使用。
Java 根据各个类库也定义了一些其他的异常,下面的表中列出了 Java 的非检查性异常:
异常 | 描述 |
---|---|
ArithmeticException | 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException | 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ArrayStoreException | 试图将错误类型的对象存储到一个对象数组时抛出的异常。 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException | 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException | 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException | 线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException | 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NegativeArraySizeException | 如果应用程序试图创建大小为负的数组,则抛出该异常。 |
NullPointerException | 当应用程序试图在需要对象的地方使用 null 时,抛出该异常 |
NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
SecurityException | 由安全管理器抛出的异常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException | 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。 |
UnsupportedOperationException | 当不支持请求的操作时,抛出该异常。 |
下面的表中列出了 Java 定义在 java.lang 包中的检查性异常类。
异常 | 描述 |
---|---|
ClassNotFoundException | 应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException | 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。 |
IllegalAccessException | 拒绝访问一个类的时候,抛出该异常。 |
InstantiationException | 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException | 一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException | 请求的变量不存在 |
NoSuchMethodException | 请求的方法不存在 |
注意:运行时异常是不需要程序员手动处理的。
public class MyException {
public static void main(String[] args) {
int a=10;
int b=0;
//实际上JVM在执行到此处的时候,会new 异常对象:new ArithmeticException("/ by zero")
//并且JVM将new的异常对象抛出,打印输出信息到控制台了
int c=a/b;
System.out.println(a+"/"+b+"="+c);
}
}
结果:
为什么要使用异常处理机制?
异常处理机制的作用是争抢程序的健壮性,就是使异常发生的时候不会影响程序的正常运行。那么怎么做到,异常发生了也不影响程序的执行?
两种方式:
1,抛出异常。
用throw和throws关键字
2,捕获异常
用try..catch
一个方法不处理这个异常,而是调用层次向上传递,谁调用这个方法,这个异常就由谁来处理。这就是抛出异常。
throw : 将产生的异常抛出(强调的是动作),抛出的既可以是异常的引用,也可以是异常对象。(位置: 方法体内)
throws : 如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。用它修饰的方法向调用者表明该方法可能会抛出异常(可以是一种类型,也可以是多种类型,用逗号隔开)(位置: 写在方法名 或方法名列表之后 ,在方法体之前。)
注意 : 调用可能会抛出异常的方法,必须添加try-catch代码块尝试去捕获异常 或者 添加throws 声明 来将异常 抛出给更上一层的调用者进行处理,这里需要注意一个细节:新的异常包含原始异常的所有信息,根据这个我们可以去追溯最初异常发生的位置,
注意:只要异常没有捕获,采用上报的方式,此方法的后续代码是不会执行的,只有在处理后没有发生异常才会执行,发生异常会报发出的异常信息
1、写法上 : throw 在方法体内使用,throws 函数名后或者参数列表后方法体前
2、意义 : throw 强调动作,而throws 表示一种倾向、可能但不一定实际发生
3、throws 后面跟的是异常类,可以一个,可以多个,多个用逗号隔开。
throw 后跟的是异常对象,或者异常对象的引用。
4,throw不能单独使用,必须和throws或者try..catch联合使用
throws可以单独使用
5,throw后面不能跟代码,和return一样。
会报以下错误:java: 无法访问的语句
就是把发生的异常对象给捕获(打印到控制台上)
try{
// 程序代码
}catch(ExceptionName(异常名) e1)
{
//Catch 块
}
使用try...catch方式:
注意:1,catch后面的小括号的类型可以是具体的异常类型,也可以是异常类型的父类型
2,catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的 调 试。
3,catch写多个的时候,从上到下,必须遵守从小到大的原则
1,使用异常需要注意的地方
public class ExceptionTest01 {
public static void main(String[] args) {
//main方法中调用doSome方法
//因为doSome()方法声明的时候有:throws ClassNotFoundException
//我们在调用doSome()方法的时候必须对这种异常进行预先的处理。
//如果不处理,编译器就会报错
doSome();
}
/**
* doSome()方法声明的时候有:throws ClassNotFoundException
* 表示这个方法在执行的过程可能会有ClassNotFoundException异常发生
* 它的直接父类时Exception,是编译时异常
* @throws ClassNotFoundException
*/
public static void doSome() throws ClassNotFoundException{
System.out.println("doSome!!!");
}
}
结果:
java: 未报告的异常错误java.lang.ClassNotFoundException; 必须对其进行捕获或声明以便抛出
解决方法1:
注意:一般不建议在main方法上使用throws,因为这个异常如果真正发生了,一定会抛给JVM。JVM只有终止,一般main方法中建议使用try...catch进行捕捉。main就不继续忘上抛了。
//在要调用声明异常方法的,方法声明的后面继续抛出 同一个异常
public static void main(String[] args) throws ClassNotFoundException {
//main方法中调用doSome方法
//因为doSome()方法声明的时候有:throws ClassNotFoundException
//我们在调用doSome()方法的时候必须对这种异常进行预先的处理。
//如果不处理,编译器就会报错
doSome();
}
解决办法2:
public class ExceptionTest01 {
public static void main(String[] args) {
//main方法中调用doSome方法
//因为doSome()方法声明的时候有:throws ClassNotFoundException
//我们在调用doSome()方法的时候必须对这种异常进行预先的处理。
//如果不处理,编译器就会报错
try {
doSome();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
发生异常时:
public class ExceptionTest02 {
public static void main(String[] args) {
try {
doSome();
} catch (FileNotFoundException e) {
e.printStackTrace();
System.out.println("异常捕获到");
}
System.out.println("main方法(开关)");
}
public static void doSome() throws FileNotFoundException {
//如果不处理异常也会编译错误
doOther();
System.out.println("执行doSome方法");
}
public static void doOther() throws FileNotFoundException {
//如果不处理异常会编译错误
doAny();
System.out.println("执行doOther方法");
}
public static void doAny() throws FileNotFoundException {
new FileInputStream("C:\\Bin\\qb.pak\\a");
System.out.println("执行doAny方法");
}
}
未发生异常时:
finally使用规则:
1,finally不能单独使用只能和try或者try...catch一起使用
2,finally一般用于资源释放,无论程序是否异常,最后都要资源释放(意思就是无论是否有异常一定会执行finally里面的代码)
使用try-catch-finally时,必须遵循以下几条规则:
①必须在 try 之后添加 catch 或 finally 块,try 块后可同时接 catch 和 finally 块,但至少有一个块。(就是说try不能单独使用)
② 若代码同时使用 catch 和 finally 块,则必须将 catch 块放在 try 块之后。
finally
异常机制中还有一个重要的部分,就是finally, catch后面可以跟finally语句,语法如下所示
try{
//可能抛出异常
}catch(Exception e){
//捕获异常
}finally{
//不管有无异常都执行
}
代码示例:
没有异常:
public class ExceptionTest03 {
public static void main(String[] args) {
try {
System.out.println("try的内部");
}catch (Exception e){
System.out.println("catch内部");
}finally {
System.out.println("finally的内部");
}
}
}
结果:try的内部
finally的内部
有异常:
public class ExceptionTest04 {
public static void main(String[] args) {
try {
doSome();
System.out.println("try的内部");
} catch (FileNotFoundException e) {
System.out.println("catch的内部");
}finally{
System.out.println("finally的内部");
}
}
public static void doSome() throws FileNotFoundException {
new FileInputStream("C:\\Bin\\qb.pak\\a");
}
}
结果:catch的内部
finally的内部
代码是严格按照从上到下的顺序执行,但是finally会在方法结束前执行,也就是说在方法结束之前,如果有finally方法一定会在return执行前被执行,因为return执行方法就结束了。
public class ExceptionTest05 {
public static void main(String[] args) {
try{
System.out.println("try的内部");
return;
}finally {
System.out.println("finally内部");
}
}
}
结果:try的内部
finally内部
要注意以下情况:
public class ExceptionTest03 {
public static void main(String[] args) {
try {
return;
System.out.println("try的内部");//编译不通过
}finally {
System.out.println("finally的内部");
}
}
}
注意以下两种情况:
public class ExceptionTest05 {
public static void main(String[] args) {
System.out.println( ExceptionTest05.m());
}
public static int m(){
int s=10;
try{
return s;
}finally {
return ++s;
}
}
}
结果:11
这说明代码是一定按照严格的从上到下的顺序执行的。
public class ExceptionTest05 {
public static void main(String[] args) {
System.out.println( ExceptionTest05.m());
}
public static int m(){
int s=10;
try{
return s;
}finally {
++s;
}
}
}
结果:10
自定义异常分为两步:
1,编写一个类继承Exception或者RuntimeException.
2,提供两个构造方法,一个无参的,一个带有String参数的。
注意:
1,所有异常都是Throwable的子类
2,如果你希望写一个检查时异常类,则需要 继承Exception。
3,如果你希望写一个运行时异常类,则需要 继承RuntimeException.。
代码示例:
public class MyException extends Exception{
public MyException(){
}
public MyException(String s){
//调用父类的有参构造来打印信息。如果不写默认是无参无法打印你想要的信息
super(s);
}
}
class ExceptionTest06{
public static void main(String[] args) {
try {
doSome(20);
} catch (MyException e) {
e.printStackTrace();
}
}
public static int doSome(int a) throws MyException{
if(a>10){
throw new MyException("a不能大于10");
}
return a;
}
}
1,throw在抛出编译时异常时不能单独使用,必须和throws或者try..catch联合使用
throws可以单独使用(其实异常处理机制是对编译时异常的处理,运行时异常不需要程序员处理)
2,throw后面不能跟代码,和return一样。
会报以下错误:java: 无法访问的语句
代码演示:
抛出运行时异常对象
public class ExceptionTest04 {
public static void main(String[] args) {
throw new NullPointerException();
}
}
抛出编译时异常:
错误演示:
public class ExceptionTest04 {
public static void main(String[] args) {
throw new ClassNotFoundException();//编译错误
}
}
正确演示:1,
public class ExceptionTest04 {
public static void main(String[] args) {
try {
throw new ClassNotFoundException();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
正确演示:2.
public class ExceptionTest04 {
public static void main(String[] args) throws ClassNotFoundException {
throw new ClassNotFoundException();//编译错误
}
}
用throw抛出编译时异常的时候:
public class ExceptionTest04 {
public static void main(String[] args) {
int a=1;
try {
doSome();
//没有异常时发生
System.out.println("try的内部");
//当使用throw抛出编译时异常的时候,一定会执行catch后面的语句
throw new FileNotFoundException();
} catch (FileNotFoundException e) {
//此时就算没有异常也会执行
System.out.println("异常发生");
}
}
public static void doSome() throws FileNotFoundException {
new FileInputStream("C:\\Bin\\qb.pak");//此时时没有错的
}
}
doSome()没有异常结果:try的内部
异常发生
doSome()有异常结果:异常发生
public class ExceptionTest04 {
public static void main(String[] args) {
try {
doSome();
System.out.println("try的内部");
//当抛出不一样的异常时需要,对异常进行处理
throw new ClassNotFoundException();
} catch (FileNotFoundException e) {
//此时就算没有异常也会执行
System.out.println("异常发生");
} catch (ClassNotFoundException e) {
System.out.println("异常2发生");
}
}
public static void doSome() throws FileNotFoundException {
new FileInputStream("C:\\Bin\\qb.pak");//此时时没有错的
}
}
doSome()没有异常结果:try的内部
异常2发生
doSome()有异常结果:异常发生