在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美, 在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避 免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。
异常:在Java语言中,将程序执行中发生的不正常情况称为“异常” 。 (开发过程中的语法错误和逻辑错误不是异常)
Java程序在执行过程中所发生的异常事件可分为两类:
Error
:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性 的代码进行处理。
Exception
:其它因编程错误或偶然的外在因素导致的一般性问题,可以使 用针对性的代码进行处理。例如: 空指针访问 、试图读取不存在的文件 、网络连接中断 、数组角标越界对于这些错误,一般有两种解决方法:一是遇到错误就终止程序 的运行。另一种方法是由程序员在编写程序时,就考虑到错误的 检测、错误消息的提示,以及错误的处理。
捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。 比如:除数为0,数组下标越界等
编译时异常和运行时异常
- 运行时异常是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免的。
java.lang.RuntimeException
类及其子类都是运行时异常
- 之前所遇到过的
ClassCastException
、ArrayIndexOutOfBoundsException
、NullPointerException
都属于运行时异常
- 对于这类异常可以不做处理,因为这类异常很普遍,若是全处理,可能会对程序的可读性和运行效率产生影响
编译时异常是指编译器要求必须处理的异常,即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常
对于这类异常,如果程序不处理,可能会带来意想不到的结果
java.lang.RuntimeException
运行时异常
ClassCastException
类转换异常ArrayIndexOutOfBoundsException
数组下标越界异常NullPointerException
空指针异常- NumberFormatException 数字类型异常
java.io.IOException
IO异常
FileNotFoundException
文件没找到异常EOFException
流结束异常
java.lang.ClassNotFoundException
类没找到异常
java.sql.SQLException
sql异常
/**
* 数组下标越界异常
*/
public class ArrayIndexOutOfBoundsException {
public static void main(String[] args) {
int[] arr = new int[5] ;
System.out.println(arr[5]);
}
}
/**
* 空指针异常
*/
public class NullPointerException {
public static void main(String[] args) {
// 创建一个Student对象,将其赋值为bull
Student student = new Student();
student = null ;
student.method();
}
}
class Student {
public void method() {
System.out.println("method.....");
}
}
/**
* 运算条件异常
*/
public class ArithmeticException {
public static void main(String[] args) {
int a = 0 ;
int b = 1 ;
System.out.println(b / a); // 众所周知,0是不能作为除数的
}
}
ps:
在除法算式中,除号后面的数叫做除数,除号前面的数叫做被除数。 如:6÷2=3,其中除号后面的2就是除数,6为被除数
两个数之间相除有两种读法,分别读作“除”和“除以”。被除数读在前用“除以”表示,而除数读在前则用“除”表示。例如“15÷3”读作“15除以3”或读作“3除15”。
public class ClassCastException {
public static void main(String[] args) {
// 使用多态向上转型的性质,创建一个String类对象
Object obj = new String() ;
// 因为Object类是ClassCastException类的父类,编译时不会报错
ClassCastException cce = (ClassCastException) obj ;
System.out.println(cce);
}
}
- 在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母是否为0,数据是否为空,输入的数据是不是数字等。这些我们都可以采用if-else语句进行判断,但是过多的if-else语句会导致代码臃肿,可读性差。因此采用异常处理机制。
Java采用的异常处理机制是将异常处理的代码集中到一起,与正常的代码分开,使程序变得简洁、优雅且易于维护
public class Demo1 {
public static void main(String[] args) {
Demo1 d = new Demo1() ;
d.methodA();
}
public void methodA() {
methodB();
}
public void methodB() {
methodC();
}
public void methodC() {
System.out.println(1 / 0);
}
}
为保证程序的正常执行,代码必须对可能出现的异常进行处理
如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理,如果异常没有在调用者方法中处理,它会继续抛给调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。这一过程被称为
捕获(catch)
异常如果一个异常被抛给了main()方法,且main()方法也没有处理,则程序中止。
程序员通常只能处理Exception,对error也无能为力
try {
可能产生异常的代码块
} catch(异常类型1 变量名1) {
当异常类型1发生时的处置措施
} catch(异常类型2 变量名2) {
当异常类型2发生时的处置措施
}
......
[finally {
无论是否发生异常都必定执行的代码
}]
try
- 捕获异常的第一步就是将
try{...} 语句块
选定捕获异常的范围,将可能出现异常的代码放到try语句块中
catch(异常类型 变量名)
- 在catch语句块中是对异常对象进行处理的代码。
每个try语句块可以伴随 一个或多个catch语句
,用于处理可能产生的不同类型的异常对象
如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可 以用其父类作为catch的参数。
比 如 : 可 以 用
ArithmeticException
类 作 为 参 数 的 地 方 , 就 可 以 用RuntimeException
类作为参数,或者用所有异常的父类Exception类作为参数。 但不能是与ArithmeticException
类无关的异常,如NullPointerException
(catch 中的语句将不会执行)。
捕获异常的有关信息: 与其它对象一样,可以访问一个异常对象的成员变量或调用它的 方法。
getMessage()
:获取异常信息,返回字符串printStackTrace()
:获取异常类名和异常信息,以及异常出 现在程序中的位置。无返回值
finally
捕获异常的最后一步是通过finally语句为异常处理提供一个 统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。
不论在try代码块中是否发生了异常事件,catch语句是否执 行,catch语句是否有异常,catch语句中是否有return, finally块中的语句都会被执行。
finally语句是可选的
finally语句并不影响后面代码的执行
public class Demo2 {
public static void main(String[] args) {
try {
System.out.println(1 / 0);
} catch (ClassCastException e) {
e.printStackTrace();
System.out.println("ClassCastException...");
} catch (ArithmeticException e) {
e.printStackTrace();
System.out.println("ArithmeticException...");
} finally {
System.out.println("finally....");
}
System.out.println("try-catch-finally语句块之外的代码");
}
}
- 当我们声明了多个catch语句块时,
如果catch中的异常类型没有子父类关系,则声明顺序无所谓
;但是当异常类型存在子父类关系时,则要求子类一定声明在父类上面
,否则会报错
前面使用的异常都是
RuntimeException
类或是它的子类,这些类的异常的特 点是:即使没有使用try和catch捕获,Java自己也能捕获,并且编译通过 ( 但运行时会发生异常使得程序运行终止 )。如果抛出的异常是
IOException
等类型的非运行时异常,则必须捕获,否则 编译错误。也就是说,我们必须处理编译时异常,将异常进行捕捉,转化为 运行时异常
示例:
声明抛出异常是Java中处理异常的第二种方式
如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这 种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理, 而由该方法的调用者负责处理。
在方法声明中用throws语句可以声明抛出异常的列表,
throws
后面的异常类型可 以是方法中产生的异常类型,也可以是它的父类。
import java.io.FileInputStream;
import java.io.IOException;
public class Demo3 {
public static void main(String[] args) {
Demo3 t = new Demo3();
try {
t.readFile();
} catch (IOException e) {
e.printStackTrace();
}
}
public void readFile() throws IOException { // 将可能出现的异常抛给调用者处理
FileInputStream in = new FileInputStream("test.txt");
int b;
b = in.read();
while (b != -1) {
System.out.print((char) b);
b = in.read();
}
in.close();
}
}
重写方法不能抛出比被重写方法范围更大的异常类型
。在多态的情况下, 对methodA()方法的调用异常的捕获按父类声明的异常处理。
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并 抛出,也可根据需要使用人工创建并抛出。
首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运 行环境)。
IOException e = new IOException(); throw e
;可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将 会产生语法错误:
throw new String("want to throw")
;
public class Demo4 {
public static void main(String[] args) {
new Demo4().method1();
}
public void method1() {
System.out.println("手动抛出异常");
throw new ClassCastException();
}
}
public class Demo5 {
public static void main(String[] args) {
Student student = new Student();
student.setAge(-20);
System.out.println(student.getAge());
}
}
class Student {
private int age ;
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 0) {
this.age = age;
} else {
throw new RuntimeException("年龄不能为负数");
}
}
}
一般的,用户自定义异常类都是
RuntimeException
类的子类自定义异常类通常需要编写几个重载的构造方法
自定义异常需要提供
serialVersionUID
自定义异常通过
throw
抛出自定义异常的名字非常重要,当异常出现时,可以根据名字判断异常类型
AgeNegativeException
public class AgeNegativeException extends RuntimeException {
static final long serialVersionUID = 14725836945123L ;
public AgeNegativeException() {
}
public AgeNegativeException(String message) {
super(message);
}
}