本文参与了思否技术征文,欢迎正在阅读的你也加入。
异常
异常的概述
- 异常就是不正常的意思,Java语言中主要是指程序在运行阶段产生的错误
Throwable(可抛出的,可扔出的)
- java.lang.Throwable 类是Java程序所有错误或异常的超类
主要有两个字类
Error
- Error主要描述比较严重的错误
- 无法通过编程来解决的重大的错误
Exception
- Exception主要m描述比较轻量级的错误
- 可以通过编程来解决
Exception类的主要分类
RuntimeException -> 运行时异常,也叫非检测性异常类
- 非检测性异常类就是指b编译阶段无法被编译器检测出来的异常
主要子类
- ArithmeticException -> 算数异常类
- ArrayIndexOutOfBoundsException(间接子类) -> 数组下标异常类
- NullPointerException -> 空指针异常
- ClassCastException -> 类型转换异常
- NumberFormatException(间接子类)-> 数字格式异常
注意
- 当程序的执行过程中产生异常,若没有手动进行处理,则由Java虚拟机采用默认的方式进行处理,默认方式是打印异常名称、异常原因以及异常发生的位置并终止程序,后序代码无法被执行
IOException和其他异常类 -> 其他异常类,也叫做非检测性异常
案例
TestRuntimeException.java
package demo1;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/*
* ArithmeticException -> 算数异常类
ArrayIndexOutOfBoundsException(间接子类) -> 数组下标异常类
NullPointerException -> 空指针异常
ClassCastException -> 类型转换异常
NumberFormatException(间接子类)-> 数字格式异常
*/
public class TestRuntimeException {
public static void main(String[] args) {
// 观察检测性异常
// FileInputStream fis = new FileInputStream("c:/a.txt");
// java.lang.ArithmeticException 算数异常
int a = 10;
int b = 0;
if (b != 0) {
System.out.println(a/b);
}
// java.lang.ArrayIndexOutOfBoundsException 数组下标越界异常
int[] arr = new int[3];
int num = 3;
if (num >= 0 && num < arr.length) {
System.out.println(arr[num]);
}
// java.lang.NullPointerException
String str = null;
if (str != null) {
System.out.println(str.length());
}
// java.lang.ClassCastException
Exception ex = new Exception();
if (ex instanceof IOException) {
IOException ie = (IOException) ex;
}
// java.lang.NumberFormatException
String s = "12be";
if (s.matches("\\d+")) {
System.out.println(Integer.parseInt(s));
}
System.out.println("运行程序结束");
}
}
异常处理
运行时异常的处理方式
- 对于绝大数运行时异常来说,都可以通过条件判断避免异常的发生
异常的捕获
语法格式
try{ 可能产生异常对象的语句块 }catch(异常类型 引用名){ 针对当前异常类型对象的处理语句块 } .... (可以写多个catch) finally{ 无论是否发生异常,都应该执行的语句块 }
注意事项
- 当捕获的结构中有多个catch分支时,切记小范围的异常类型放在大范围的异常类型上面
- 懒人写法:
catch(Exception e) {....}
- 执行流程
try {
a;
b; // 可能产生异常的语句
c;
} catch (Exception e) {
e;
} finally {
f;
}
- 当没有产生异常的时候,程序的执行流程是:a b c f
- 当产生异常时,程序的执行流程是: a b e f
案例
TestExceptionCatch.java
package demo2; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class TestExceptionCatch { public static void main(String[] args) { // 声明引用指向本类的对象,用于读取文件中的内容 FileInputStream fis = null; try { // 随时可能产生文件找不到y异常对象, new FileNotFoundException() fis = new FileInputStream("d:/a.txt"); } catch (FileNotFoundException e) { // 打印异常的名称、异常原因、异常的位置等信息 e.printStackTrace(); } try { fis.close(); } catch (IOException e) { e.printStackTrace(); } catch (NullPointerException e) { // System.out.println("输出"); e.printStackTrace(); } } }
TestFinally.java
package demo3; public class TestFinally { public static void main(String[] args) { int a = 10; int b = 0; try { System.out.println(a/b); } catch (Exception e) { e.printStackTrace(); return ; } finally { System.out.println("无论是否发生异常都会执行"); } System.out.println("程序结束"); } }
java.lang.ArithmeticException: / by zero
无论是否发生异常都会执行
at demo3.TestFinally.main(TestFinally.java:9)
异常的抛出
基本概念
- 某些特殊的场合中,当产生异常后却无法直接处理/不想处理时,此时就可以将异常转移给当前方法的调用者,这就叫异常的抛出
语法格式
- 返回值类型 方法名称(形参列表) throws 异常类型{....}
方法重写的原则
- 要求方法名相同、参数列表相同、返回值类型也相同,从jdk1.5开始允许返回子类类型
- 范围权限不能变小,可以相同或者变大
- 不能抛出更大的异常
注意
- 子类中重写以后的方法可以选择抛出与父类一样的异常、更小的异常、不抛出异常,但是不能抛出更大的异常、不同的异常
案例
A.javapackage demo4; import java.io.IOException; public class A { public void show() throws IOException{ System.out.println("A"); } }
SubA.java
package demo4; import java.io.IOException; import java.io.FileNotFoundException; import javax.print.PrintException; public class SubA extends A{ @Override // public void show() throws IOException { // public void show() throws FileNotFoundException { // public void show() throws PrintException { // public void show() throws Exception { public void show() { } }
显然不能抛出更大的异常
自定义异常
自定义异常的由来
- Java官方库中虽然提供了大量的异常类,但不足以描述现实生活中所有的异常情况。当出现官方库中没有m描述的异常情况,这个时候就需要程序员自定义异常类加以描述,使得异常信息更加具备针对性和可读性
自定义异常的流程
- 自定义类继承自Exception类或者Exception类的子类
- 提供两个版本的构造方法,一个是无参构造方法,另一个是字符串做参数的构造方法
自定义异常 -- >案例1
Person.java
package demo5; public class Person { private String name; private int age; public Person() { super(); } public Person(String name, int age) throws Exception { super(); setName(name); setAge(age); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) throws Exception { if(age > 0 && age < 150) { this.age = age; } else { // System.out.println("年龄不合理"); // 手动产生一个异常对象并抛出 // throw new Exception(); throw new AgeException("年龄不合理!"); } System.out.println("产生异常的效果"); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } }
AgeException.java
package demo5; public class AgeException extends Exception { /** * */ private static final long serialVersionUID = 1L; // 自定义无参的构造方法 public AgeException() { } // 自定义使用字符串作为参数的构造方法 public AgeException(String msg) { super(msg); } }
TestPerson.java
package demo5; public class TestPerson { public static void main(String[] args) { Person p = null; try { p = new Person("张三", -12); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(p); } }
demo5.AgeException: 年龄不合理!
null
at demo5.Person.setAge(Person.java:33)
at demo5.Person.(Person.java:15)
at demo5.TestPerson.main(TestPerson.java:8)
自定义异常 -- > 案例2
Student.java
package demo6; public class Student { private String name; private int id; public Student() { super(); } public Student(String name, int id) throws IDException { super(); setName(name); setId(id); } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) throws IDException { if (id > 0) { this.id = id; } else { // System.out.println("学号不合理"); throw new IDException("学号不合理"); } System.out.println("结束"); } @Override public String toString() { return "Student [name=" + name + ", id=" + id + "]"; } }
IDException.java
package demo6; public class IDException extends Exception { /** * */ private static final long serialVersionUID = 1L; public IDException() { } public IDException(String msg) { super(msg); } }
TestStudent.java
package demo6; public class TestStudent { public static void main(String[] args) throws IDException { /*Student stu = null; try { stu = new Student("小王", -5); } catch (IDException e) { // TODO Auto-generated catch block e.printStackTrace(); }*/ Student stu = new Student("小王", -5); System.out.println(stu); } }
Exception in thread "main" demo6.IDException: 学号不合理
at demo6.Student.setId(Student.java:30)
at demo6.Student.(Student.java:14)
at demo6.TestStudent.main(TestStudent.java:14)
此处有一点要注意,在案例一的TestPerson中,在main函数中,我们使用try....catch语句,异常由内部进行处理,会打印后面的语句
而在案例二的TestStudent中,我们将异常直接交给main函数处理,也就是交给虚拟机处理,所以并不会执行后面的语句
异常对象的抛出
- throw new 异常类型()
例如:
- throw new Exception()
最后简单介绍一下throws和throw的区别
throws和throw的区别
throws
- 用在方法声明后面,跟的是异常类名
- 可以跟多个异常类名,用逗号隔开
- 表示抛出异常,由该方法的调用者来处理
- throws表示出现异常的一种可能性,并不一定会发生这些异常
throw
- 用在方法体内,跟的异常对象名
- 只能抛出一个异常对象名
- 表示抛出异常,由方法体内的语句实现
- throw则是抛出了异常,执行throw则一定抛出了某种异常