一、什么是异常
从字面上讲,就是不正常的现实就是异常。
程序中的异常也是要在程序运行中才会偶尔发生。如果程序还没有运行,编译就报错,这种不叫异常,这种叫编译错误,通常是语法上的错误
二、java中异常
Java 提供了两类主要的异常:runtime exception 和checked exception。所有的checked exception 是从java.lang.Exception 类衍生出来的,而
runtime exception 则是从java.lang.RuntimeException 或java.lang.Error类衍生出来的。它们的不同之处表现在两方面:机制上和逻辑上
所有异常必须处理!在程序里可以抛异常,但是决不能把异常抛给最终用户。
BUG 在程序员手里还不是BUG,但是一旦交付,那么就是BUG
2.1、机制上
runtime exceptions:
- 在定义方法时不需要声明会抛出runtime exception
- 在调用这个方法时不需要捕获这个runtime exception
- runtime exception 是从java.lang.RuntimeException 或
- java.lang.Error 类衍生出来的。
checked exceptions:
- 定义方法时必须声明所有可能会抛出的checked exception
- 在调用这个方法时,必须捕获它的checked exception,不然就得
- 把它的exception 传递下去
- checked exception 是从java.lang.Exception 类衍生出来的
2.2、逻辑上
从逻辑的角度来说,checked exceptions 和runtime exception 是有不同的使用目的的。
checked exception 用来指示一种调用方能够直接处理的异常情况。checked exception 迫使你捕获它并处理这种异常情况。
而runtime exception 则用来指示一种调用方本身无法处理或恢复的程序错误。
三、程序中看异常
3.1、例一
package com.pb.demo6; import java.util.Scanner; /* * 求2个整数的商 * 当异常发生时,程序如何处理 */ public class Test1 { public static void main(String[] args) { //从键盘获得输入 Scanner input=new Scanner(System.in); System.out.println("请输入被除数:"); int num1=input.nextInt(); System.out.println("请输入除数:"); int num2=input.nextInt(); int result=num1/num2; System.out.println(num1+"与"+num2+"的商: "+result); System.out.println("谢谢使用!"); } }
当输入除数不是0时,正常运行,但当输入的除数为0时,或者字符串时,就会报出异常:
请输入被除数: 2 请输入除数: 0 Exception in thread "main" java.lang.ArithmeticException: / by zero at com.pb.demo6.Test1.main(Test1.java:18)
请输入被除数: ffd Exception in thread "main" java.util.InputMismatchException
同时也发现有异常发生时,最后的输出语句并没有执行
四、常见异常
java.lang.ArithmeticExecption
算术异常类。当出现异常的运算条件时,抛出此异常。比如程序中出现了“除以零”这样的运算,就会出这样的异常。对这种异常,大家就要好好检查一下自己程序中,涉及到数学运算的地方,公式是不是有不妥了
java.lang.NullPointerException
空指针异常类。简单地说就是调用了未经初始化的对象或者是不存在的对象。当应用程序试图在需要对象的地方使用null 时,抛出该异常这种情况包括:
- 调用null 对象的实例方法
- 访问或修改null 对象的字段
- 将null 作为一个数组,获得其长度
- 将null 作为一个数组,访问或修改其时间片
- 将null 作为Throwable 值抛出
应用程序应该抛出该类的实例,指示其他对null 对象的非法使用
java.lang.ClassNotFoundException
找不到类异常。当应用程序试图使用以下方法通过字符串名加载类时,抛出该异常:
- Class 类中的forName 方法
- ClassLoader 类中的findSystemClass 方法
- ClassLoader 类中的loadClass 方法
java.lang.ArrayIndexOutOfBoundsException
数组下标越界异常。用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引
现在程序中大多都有对数组的操作,因此在调用数组的时候一定要认真检查,看自己调用的下标是不是超出了数组的范围,一般来说,显示(即直接用常数当下标)调用不太容易出这样的错,但隐式(即用变量表示下标)调用就经常出错了,还有一种情况,是程序中定义的数组的长度是通过某些特定方法决定的,不是事先声明的,这个时候,最好先查看一下数组的length,以免出现这个异常
java.lang.IllegalArgumentException
方法的参数错误异常。抛出的异常表明向方法传递了一个不合法或不正确的参数
类库中的方法在一些情况下都会引发这样的错误,比如音量调节方法中的音量参数,如果写成负数就会出现这个异常,再比如g.setColor(int red,intgreen,int blue)这个方法中的三个值,如果有超过255 的也会出现这个异常因此一旦发现这个异常,我们要做的,就是赶紧去检查一下方法调用中的参数传递是不是出现了错误
java.lang.IllegalAccessException
没有访问权限异常。当应用程序试图反射性地创建一个实例(而不是数组)、设置或获取一个字段,或者调用一个方法,但当前正在执行的方法无法访问指定类、字段、方法或构造方法的定义时,抛出该异常对程序中用了package 的情况下要注意这个异常
java.lang.IncompatibleClassChangeError
不兼容的类变化错误。当正在执行的方法所依赖的类定义,发生了不兼容的改变时,抛出该错误一般在修改了应用中的某些类的声明定义,而没有对整个应用重新编译,而直接运行的情况下,容易引发该错误
java.lang.InstantiationError
实例化错误。当应用程序试图使用Java 的new 结构,来实例化一个抽象类或一个接口时,抛出该异常通常由编译器捕获此错误,如果类定义中存在不兼容的更改,则此错误
将只可能发生在运行时
java.lang.LinkageError
链接错误。该错误及其所有子类指示一个类在一定程度上依赖于另一个类;但是,在编译前一个类之后,后一个类发生了不相容的改变
java.lang.StackOverflowError
堆栈溢出错误。当一个应用递归调用的层次太深,而导致堆栈溢出时,抛出该错误
java.lang.NumberFormatException
数字格式异常。当试图将一个String 转换为指定的数字类型,而该字符串却不满足数字类型要求的格式时,抛出该异常
java.lang.RuntimeException
运行时异常。RuntimeException 是那些可能在Java 虚拟机正常运行期间抛出的异常的超类可能在执行方法期间抛出但未被捕获的RuntimeException 的任何子类,都无需在throws 子句中进行声明
java.io.IOException
输入输出异常。当发生某种I/O 异常时,抛出此异常。此类是失败或中断的I/O 操作生成的异常的通用类
java.io.FileNotFoundException
文件未找到异常。当试图打开指定路径名表示的文件失败时,抛出此异常
在不存在具有指定路径名的文件时,此异常将由FileInputStream、FileOutputStream 和RandomAccessFile 构造方法抛出。如果该文件存在,但是由于某些原因不可访问,比如试图打开一个只读文件进行写入,则此时这些构造方法仍然会抛出该异常
java.io.EOFException
文件已结束异常。当输入过程中意外到达文件或流的末尾时,抛出此异常
此异常主要被数据输入流用来表明到达流的末尾。注意,其他许多输入操作返回一个特殊值表示到达流的末尾,而不是抛出异常
java.lang.InterruptedException
被中止异常。当线程在活动之前,或活动期间处于正在等待、休眠或占用状态,且该线程被中断时,抛出该异常有时候,一种方法可能希望,测试当前线程是否已被中断,如果已被中断,则立即抛出此异常
五、异常处理
5.1、语法格式:
try{ // 有可能出现异常的语句 }catch(异常类 异常对象){ // 编写异常的处理语句 }[ catch(异常类 异常对象){ // 编写异常的处理语句 } catch(异常类 异常对象){ // 编写异常的处理语句 } …. ] [finally{ 一定会运行到的程序代码 ; }
主要搭配:try-catch、try-catch-finally、try-finally三种形式
5.2、多个异常
如果同时有多个catch块,Exception 必须放在最后一个catch块中
package com.pb.demo6; import java.util.InputMismatchException; import java.util.Scanner; import javax.naming.AuthenticationException; /* * 求2个整数的商 * 当异常发生时,程序如何处理 */ public class Test1 { public static void main(String[] args) { //从键盘获得输入 try{ Scanner input=new Scanner(System.in); System.out.println("请输入被除数:"); int num1=input.nextInt(); System.out.println("请输入除数:"); int num2=input.nextInt(); int result=num1/num2; System.out.println(num1+"与"+num2+"的商: "+result); }catch(InputMismatchException e){ System.out.println("输入的不是数字"); e.printStackTrace(); }catch(ArithmeticException e){ System.out.println("除数不能为0"); e.printStackTrace(); }catch(Exception e){ System.out.println("其它错误!"); e.printStackTrace(); }finally{ System.out.println("谢谢使用!"); } } }
请输入被除数: 2 请输入除数: 0 除数不能为0 java.lang.ArithmeticException: / by zero at com.pb.demo6.Test1.main(Test1.java:22) 谢谢使用!
在finally中的语句,不管出不出异常都会被执行.
但如果在catch块中:执行System.exit(0);或者System.exit(-1);程序将直接结束,finally中的语句将不会执行
5.3、Java的异常处理机制
在整个java的异常处理中,实际上也是按照面向对象的方式进行处理,处理的步骤如下:
- 一旦产生异常,则首先会产生一个异常类的实例化对象;
- 在try语句中对此异常对象进行捕捉;
- 产生的异常对象与catch语句中的各个异常类型进行匹配,如果匹配成功,则执行catch语句中的代码
六、抛出异常
6.1、throws关键字
在定义一个方法的时候可以使用throws关键字声明,使用throws声明的方法表示此方法不处理异常,而交给方法的调用处进行处理 。
throws使用格式
public 返回值类型 方法名称(参数列表…) throws 异常类{}
package com.pb.demo6; import java.util.InputMismatchException; import java.util.Scanner; import javax.naming.AuthenticationException; /* * 求2个整数的商 * 当异常发生时,程序如何处理 */ public class Test2 { public static void main(String[] args) { try { divide(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } //除法运算 public static void divide () throws Exception{ //从键盘获得输入 Scanner input=new Scanner(System.in); System.out.println("请输入被除数:"); int num1=input.nextInt(); System.out.println("请输入除数:"); int num2=input.nextInt(); int result=num1/num2; System.out.println(num1+"与"+num2+"的商: "+result); } }
6.2、throw关键字
与throws不同的是,可以直接使用throw抛出一个异常。抛出的时候直接抛出异常类的实例化对象即可
package com.pb.demo6; /* * 人类 */ public class Person { private String name; private String sex; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) throws Exception { if(sex.equals("男") || sex.equals("女")){ this.sex = sex; }else{ throw new Exception("性别只能是男或者女"); } } public int getAge() { return age; } public void setAge(int age) { if(age>=0 && age<=150){ this.age = age; }else{ try { throw new Exception("年龄只能在0-150岁之间!"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void printSelf(){ System.out.println("姓名:"+this.name+" 性别:"+this.sex+" 年龄:"+this.age); } }
package com.pb.demo6; public class PersonTest { public static void main(String[] args) { Person p=new Person(); try { p.setName("张三"); p.setSex("不男不女"); p.setAge(200); p.printSelf(); } catch (Exception e) { e.getMessage(); e.printStackTrace(); } } }
七、自定义异常
在Java中已经提供了大量的异常类,但是这些异常类有些时候也很难满足开发者的要求,所以此时用户可以根据自己的需要定义自己的异常类,定义异常类,只需要继承Exception类即可。
当然可以继承其它的如:Exception,Throwable,RuntimeException及其子类,其中继承Exception,Throwable,效果,如果不要求调用者一定要处理抛出的异常可以继承RuntimeException及其子类
调用时只需要throw new 自定义异常名(信息)
package com.pb.demo6; public class MyException extends Exception { public MyException() { super(); // TODO Auto-generated constructor stub } public MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); // TODO Auto-generated constructor stub } public MyException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public MyException(String message) { super(message); // TODO Auto-generated constructor stub } public MyException(Throwable cause) { super(cause); // TODO Auto-generated constructor stub } }
package com.pb.demo6; /* * 人类 */ public class Person { private String name; private String sex; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) throws Exception { if(sex.equals("男") || sex.equals("女")){ this.sex = sex; }else{ throw new MyException("性别只能是男或者女"); } } public int getAge() { return age; } public void setAge(int age) { if(age>=0 && age<=150){ this.age = age; }else{ try { throw new MyException("年龄只能在0-150岁之间!"); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void printSelf(){ System.out.println("姓名:"+this.name+" 性别:"+this.sex+" 年龄:"+this.age); } }
package com.pb.demo6; public class PersonTest { public static void main(String[] args) { Person p=new Person(); try { p.setName("张三"); p.setSex("不男不女"); p.setAge(200); p.printSelf(); } catch (Exception e) { e.getMessage(); e.printStackTrace(); } } }
com.pb.demo6.MyException: 性别只能是男或者女
这时获得的异常就是我们自定义的异常