目录
异常引出
* 异常基本介绍
对于程序员来说,编程中遇到大大小小的错误十分常见(应该不会有什么大佬一次写完几百行代码还不会报错吧)。当程序报错时,要么它无法运行,要么运行时会提示一行醒目的红色信息。导致程序不能运行的也许是一个简单的错误,但会让整个大型项目崩溃。所以可以用一些方法让程序在有错误的情况还能继续运行。我们把写代码是遇到的错误称为异常,把这些针对异常的处理方法叫做异常处理。我们先展示一段有异常的代码:
定义:Java语言中,将程序执行中发生的不正常情况称为“异常”(开发中的语法错误和逻辑错误不是异常)。
public class Exception {
public static void main(String[] args) {
//把引用变量person指向空值
Person person = null;
person.getName();
System.out.println("程序运行完毕、、、");
}
}
//含有一个成员变量的Person类
class Person{
private String name = "小黄";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
运行结果如下:
Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Exception.Person.getName()" because "person" is null
at Exception.Exception.main(Exception.java:7)
代码说明:1:以上代码因为把Person类的引用指向了空,所以会报NullPointerException(空指 针异常);
2:我们看到最后的输出语句没有执行,因为遇到异常时,JVM会把异常信息打印出来 (如图),然后终止程序执行;
3:因为一个异常可能导致后续的重要项目停止执行,所以需要异常处理机制。
异常事件分类
程序执行中把可能发生的异常事件分为两大类:
1:Error(错误):JVM无法解决的严重问题:如:JVM系统内部错误,资源耗尽等严重情况。比如:StackOverflowError(栈溢出)和OOM,Error是严重错误,程序会崩溃;
2:Exception(异常):其他因编程错误或偶然的外在元素导致的一般性问题,可以使用针对性的代码进行处理。例如空指针访问,试图读取不存在的文件,网络连接中断等等;
Exception分为两大类:运行时异常(程序运行时发生)和编译时异常(编程时编译器检查出的异常)。
运行时异常和编译时异常的区别
运行时异常:一般指代码中的逻辑错误,在程序运行时发生,编译器不会进行自动提示(不要求必须处理)。会由JVM输出异常信息。
编译时异常:编程时编译器会自动提示的异常,要求必须处理。
异常体系图
说明: 1:可以捕获的异常都被封装在Exception类中,该类继承了Throwable类;
2:运行时异常包含了很多子类异常;
3:常见的子类异常有:NullPointerException(空指针异常), ArrayIndexOutOfBoundsException(数组下标越界异常),ArithmeticException(算数异 常),ClassCastException(类型转换异常),NumberFormatException(数字格式不正 确异常)。
常见的运行时异常
* NullPointerException空指针异常
实例代码如上所示:
说明:当程序试图在需要对象的地方使用null时,抛出该异常;
* ArrayIndexOutOfBoundsException数组下标越界异常
public class Exception02 {
public static void main(String[] args) {
//定义下标最大值为2的数组
int[] array = {1,2,3};
//输出下标为3的数组
System.out.println(array[3]);
}
}
说明:1:用非法索引访问数组时抛出的异常;
2:如果索引为负或大于等于数组的大小,则该索引为非法索引。
* ClassCastException类型转换异常
public class Exception03 {
public static void main(String[] args) {
//向上转型使Person父类引用指向Student子类实例
Person1 person1 = new Student();
//Person1对象接收了Student类的实例,所以可以把Person类的引用强转为Student类
Student student = (Student)person1;
//Person类的引用没有接收Teacher类的实例(Teacher类不是person1对象的实例)
//所以不能把Person类的引用强转为Teacher类
Teacher teacher =(Teacher)person1;
}
}
class Person1{}
class Student extends Person1{}
class Teacher extends Person1{}
说明:当试图将对象转换为不是实例的子类时,抛出该异常。
补充:在对象向下转型中,父类是实例不能强制转换为任意子类实例的,应该先通过向上转型和子类实例产生联系,然后才能通过向下转型转为子类实例,否则会抛出ClassCastException异常。
* ArithmeticException数学运算异常
public class Exception04 {
public static void main(String[] args) {
//o不能做被除数
int a = 1/0;
System.out.println(a);
}
}
说明:当出现异常的运算条件时,抛出此异常。
* NumberFormatException数字格式不正确异常
public class Exception05 {
public static void main(String args[]){
String name = "小黄";
//文字字符串不能通过方法转变为整型变量
int num = Integer.parseInt(name);
}
}
说明:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换成适当格式时,抛出该异常。
异常处理的两种方式
* try--catch方式
public class Exception06 {
public static void main(String[] args) {
try{
//用try代码块把可能出现异常的语句装起来
int n = 1/0;
System.out.println(n);
}catch (ArithmeticException e){
//catch代码块捕获异常,并调用getMessage方法输出异常信息
System.out.println("打印出异常信息:" + e.getMessage());
}finally {
//处理完毕后执行finally代码块的内容
System.out.println("程序继续运行、、、");
}
}
}
运行结果:
打印出异常信息:/ by zero
程序继续运行、、、
说明: 1:该方式由三个代码块组成;
2:用try代码块把程序员认为可能出现异常的代码段括起来;
3:用catch代码块捕获该异常传递给异常类的对象,可以用该对象调用异常类的方法进行 处理,怎么处理看自己;
4: finally代码块进行异常处理后的后续工作,常常进行释放资源的操作;
5:如果try代码块中发生了异常,则异常后的代码不执行,跳到catch代码块;
6:如果try代码块中没有出现异常,则不会执行catch代码块的内容,直接执行finally的内 容;
7:finally代码块可以不写。
8:格式:
try{
//可能发生的异常
}catch(异常类名 异常对象){
//对异常的处理操作
}finally{
//异常处理之后的操作
}
注:异常类名应该和可能发生的异常一致,或者是它的父类;
* throws方式
public class Exception07 {
//指定该方法抛出算数异常
public void age1() throws ArithmeticException{
System.out.println("我的年龄为:" + (1/0));
}
//方法的调用者age2方法处理了该异常
public void age2(){
try {
new Exception07().age1();
System.out.println("说错了,我的年龄是12岁。");
}catch (ArithmeticException e){
System.out.println("调用者捕获了该异常。");
System.out.println(e.getMessage());
}finally {
System.out.println("程序继续运行。。。");
}
}
public static void main(String[] args) {
//调用age2方法
Exception07 exception07 = new Exception07();
exception07.age2();
}
}
运行结果:
调用者捕获了该异常。
/ by zero
程序继续运行。。。
说明:1: 该方式只相当于修饰方法的语句,不影响程序运行;
2:如果一个方法可能出现某种异常,但不确定或不想马上处理该异常,可以用throws显式 的抛出该异常,由该方法的调用者处理;
3:throws使用目的在于声明方法的可能出现的异常,表示当前方法不处理该异常,由此提 醒方法的调用者处理该异常;
4:方法的调用者也可选择try - catch捕获该异常或者继续用throws向上抛出异常;
5:最后一定要有方法捕获该异常进行处理;
6:如果一直不处理该异常,程序会默认向上一级抛出异常,最后会交给JVM处理(JVM为 最高级);
7:JVM会输出异常信息,并中断程序执行;
8:throws后面可以跟多个异常(异常列表),不同的异常用","隔开。
对多个异常的捕获处理
如果程序有多个不同类的异常,可以用 try-catch同时处理,有两种方式:
* 用包含这些异常的父类异常同时捕获
public class Exception08 {
public static void main(String[] args) {
try{
//空指针异常
Person03 person03 = null;
person03.call();
//算数异常
int n = 1/0;
//用父类异常RuntimeException同时捕获
}catch (RuntimeException e){
//输出异常信息
System.out.println(e.getMessage());
}finally {
System.out.println("程序继续执行、、、");
}
}
}
class Person03{
public void call(){
System.out.println("这是个person。。。");
}
}
说明:ArithmeticException和NullPointerException都是运行时异常(RuntimeException)的子 类,因此可以用RuntimeException的对象同时封装两种异常进行处理;
* 用不同的子类异常单独捕获
public class Exception09 {
public static void main(String[] args) {
try {
//算数异常
int n = 1 / 0;
//空指针异常
Person4 person4 = null;
person4.p4();
}
//单独捕获算数异常
catch (ArithmeticException ar){
System.out.println(ar.getMessage());
//单独捕获空指针异常
}catch (NullPointerException un){
System.out.println(un.getMessage());
}finally {
System.out.println("程序继续运行、、");
}
}
}
class Person4{
public void p4(){
System.out.println("这是一个人。");
}
}
说明:该代码分别用子类异常捕获了不同的异常,分别进行处理。
注:要求子类异常写在前面,父类异常写在后面,或者两者都为同级的异常类;
原因:父类异常会一并捕获含有的子类异常,此时再写子类异常没有意义;
异常处理注意事项
1:对于编译异常程序必须处理,比如try catch或throws;
2:对于运行时异常,程序如果没有处理,默认就是throws的处理方式。
3:子类重写了父类的方法时,如果子类和父类都抛出了异常,那么子类的方法抛出的异常必须和父类的方法抛出的异常相同,要么是父类的方法抛出异常的子类;
4:throws和try catch最好二选一,不要同时存在,没有意义;
自定义异常
public class CustomException {
public static void main(String[] args) {
int age = 200;
//如果年龄不在指定区间则抛出自定义异常
if(!(age <= 0 && age >= 100)){
//输出指定异常信息
throw new AgeException("年龄需要在之间0-100之间");
}
System.out.println("你的年龄输入正确");
}
}
//自定义AgeException类继承运行时异常类
class AgeException extends RuntimeException{
//用构造方法调用父类异常的构造方法,接收异常信息
public AgeException(String massage){
super(massage);
}
}
输出结果:
Exception in thread "main" Exception.AgeException: 年龄需要在之间0-100之间
at Exception.CustomException.main(CustomException.java:9)
使用方法:1:自定义个异常类(类名自己写),来继承父类异常;
2:如果继承Exception,属于编译异常;
3:如果继承RuntimeException,属于运行时异常,一般情况我们都是继承 RuntimeException;
4:代码中调用的父类构造方法目的在于打印指定的异常信息。
5:throw后面跟自定义异常对象的实例(或引用),可以同时进行方法的调用。
常用的异常方法
Throwable类提供了很多处理异常的方法,下面列举三个常用的:
* getMessage方法
说明:返回指定异常的详细信息。
* printStackTrace方法
说明:输出异常的名称,和异常的位置。
* toString方法
说明:输出说明异常地址和类型的字符串。
throws和throw的区别
用法 | 格式 | 使用位置 | |
throws | 异常处理的方式 | 关键字加异常列表 | 方法声明处 |
throw | 抛出自定义异常的关键字 | 关键字加自定义异常对象 | 方法体中 |