目录
1.异常的概念
异常是指程序运行或者编译的过程导致程序中断的原因,异常包含资源异常,逻辑异常,输入输出异常,运行时异常,语法异常等,在Java中异常定义在java.lang.Throwable及其子类Exception和Error中,Exception处理的是程序中可修改的异常,包含运行时异常,输入输出异常等,Error是程序出现严重错误导致的异常,包含栈溢出和内存溢出等。
2.常见的异常
2.1 算术异常
默认在除法中分母不为0,如果分母为零时就会在运行时报错:ArithmeticException
public class Main{
public static void main(String[] args) {
int a = 10;
int b = 0;
System.out.println(a/b);//10/0 算术异常
}
}
2.2 数组越界异常
数组越界异常是因为数组通过下标访问了不属于当前程序的内存空间而导致的异常:ArrayIndexOutOfBoundsException
public class Main{
public static void main(String[] args) {
int[] arryas = {1,2,3};
System.out.println(arryas[3]);//数组访问越界
}
}
2.3 空引用异常
引用为null时,默认是不指向任何内存空间的,使用null访问内存就会报出异常:NullPointerException
public class Main{
public static void main(String[] args) {
int[] a = null;
System.out.println(a[0]);//使用null访问内存空间
}
}
2.4 不支持克隆异常
使用clone方法需要使用Cloneable这个接口,使用后重写Object中的clone方法,如果在类中不使用这个接口,就会报错不支持克隆异常:CloneNotSupportedException
class T /*implements Cloneable*/{
int a =10;
@Override
protected Object clone() throws CloneNotSupportedException{
return super.clone();
}
}
public class Main{
public static void main(String[] args) throws CloneNotSupportedException{
T t = new T();
T t1 = (T)t.clone();
}
2.5 文件打不开异常
当需要打开的文件通过文件名搜索找不到时,就会报出异常:
class Config{
String name = "test.java";
void openFile(String name) throws FileNotFoundException{
if(!(this.name.equals(name)))//名字不相等说明文件不存在
throw new FileNotFoundException("找不到此文件" + name);//抛出异常
else System.out.println("打开成功:" + this.name);//成功打开
}
}
public class Main{
public static void main(String[] args) throws FileNotFoundException{
Config c = new Config();//实例对象
c.openFile("test.txt");//调用openFile方法
}
}
3.异常相关的关键字
与异常相关的关键字常见的有以下5个:throws,throw,try,catch,finally;throws关键字用于异常的声明,throw关键字是用于抛出异常,异常的原因根据程序设置,try,catch关键字是处理异常,finally关键字通常用于资源的处理。
3.1 throws关键字
throws关键字可以用于一些编译时异常的处理,声明异常原因,提示后续程序可能出现异常,可以使用try-catch捕获和处理异常。
例如:声明算术异常,在方法后使用throws + 异常原因
//声明算术异常
public static void main(String[] args) throws ArithmeticException{
}
例如:声明不支持克隆异常
//声明不支持克隆异常
public static void main(String[] args) throws CloneNotSupportedException{
}
例如:声明栈溢出错误异常
//声明栈溢出错误异常
public static void main(String[] args) throws StackOverflowError{
}
3.2 throw关键字
throw关键字用于手动抛出异常,根据条件使用throw + 异常对象,输出异常,方便后续修改异常。
例子:抛出算术异常。
public class Main{
//除法
public static int Div(int x,int y){
if(y == 0)//使用throw抛出算术异常
throw new ArithmeticException("分母为零,不可计算!");//参数部分为异常实际原因
//可以输入错误原因,不输入系统会默认不输出错误信息
else return x / y;
}
public static void main(String[] args) {
int a = 10;
int b = 0;
int r = Div(a,b);
}
}
例子:抛出空引用异常。
public class Main{
public static void print_chars(char[] chars){
if(chars == null) //抛出空引用异常
throw new NullPointerException("chars是空引用,不可以使用空引用访问!");
//抛出异常后不再执行后续语句
for (int i = 0; i < chars.length; i++) {
System.out.print(chars[i] + " ");
}
}
public static void main(String[] args) {
char[] chars = null;//chaars赋值空引用
print_chars(chars);//调用方法打印数组
}
}
3.3 try - catch - finally
当一个方法声明异常后,可以在方法内部使用try - catch捕获和处理异常,将可能出现异常的语句在try语句内,使用catch语句捕获异常,catch的参数是异常的类型,在语句内可以调用方法输出错误原因,最后在finally语句内回收资源,完成异常的处理。
例如:处理数组越界异常。
public class Main{
//声明异常:数组访问可能越界
public static void main(String[] args) throws ArrayIndexOutOfBoundsException{
int[] arrays = new int[10];//开辟10个int类型空间大小的数组
Scanner scanner = new Scanner(System.in);
try {
//初始化
for (int i = 0; i <= arrays.length; i++) {
arrays[i] = scanner.nextInt();
}
//catch的参数是 异常类型 + 变量名(通常使用e)
}catch (ArrayIndexOutOfBoundsException e){
//可以使用方法输出错误原因后终止程序,也可以不输出
//系统会处理好异常后执行后面语句
//如果进入此语句内说明存在异常,可以提示异常
System.out.println("发现数组越界异常,系统正在处理中.....");
}finally{
scanner.close();//释放scanner引用指向的对象
}
//最后提示
System.out.println("完成arrays数组的初始化,并处理了异常");
}
}
如果发现异常后不想系统默认处理,可以在发现异常后调用e.printStackTrace(),会将异常的原因输出,方便及时修改异常。
public class Main{
//声明异常:数组访问可能越界
public static void main(String[] args) throws ArrayIndexOutOfBoundsException{
int[] arrays = new int[10];//开辟10个int类型空间大小的数组
Scanner scanner = new Scanner(System.in);
try {
//初始化
for (int i = 0; i <= arrays.length; i++) {
arrays[i] = scanner.nextInt();
}
//catch的参数是 异常类型 + 变量名(通常使用e)
}catch (ArrayIndexOutOfBoundsException e){
//直接输出错误原因
e.printStackTrace();
}finally{
scanner.close();//释放scanner引用指向的对象
//最后提示
System.out.println("完成arrays数组的初始化,并处理了异常");
}
}
}
找到异常的原因的访问10下标的空间,在初始化时,将for循环的判断条件修改为i < arrays.lengrh,防止数组越界,对程序可能出现的异常进行了完善。
public class Main{
//声明异常:数组访问可能越界
public static void main(String[] args) throws ArrayIndexOutOfBoundsException{
int[] arrays = new int[10];//开辟10个int类型空间大小的数组
Scanner scanner = new Scanner(System.in);
try {
//初始化:修改判断条件
for (int i = 0; i < arrays.length; i++) {
arrays[i] = scanner.nextInt();
}
//catch的参数是 异常类型 + 变量名(通常使用e)
}catch (ArrayIndexOutOfBoundsException e){
//直接输出错误原因
e.printStackTrace();
}finally{
scanner.close();//释放scanner引用指向的对象
//最后提示
System.out.println("完成arrays数组的初始化!");
}
}
}
此时系统就不会报出异常,以上finally语句的执行与try-catch语句是否捕获了异常无关,使用了finally语句,最后都是会执行的。
4.自定义异常
一个异常通常表示一个对应的类,每一个异常都有自己的.java为后缀的文件,当使用当前的异常不符合当前处理的问题,就可以使用自定义异常。
例如:实现一个密码输入异常。
1.定义一个PassWordException的类,继承于Exception这个类,在类中定义构造方法,并完成父类的构造。
2.定义一个密码类,定义一个密码变量并赋值,类中可以定义一个匹配方法,如果匹配失败,可以抛出异常原因,使用自定义的类。
class PassWord{
//密码
String password = "888888";
//声明异常
public void Match(String password) throws PassWordException{
//密码不匹配,输出原因
if(!(this.password.equals(password)))
throw new PassWordException("密码匹配失败!");
else System.out.println("匹配成功!");
}
}
3.实例化对象,调用Match方法匹配。
class PassWord{
//密码
String password = "888888";
//声明异常
public void Match(String password) throws PassWordException{
//密码不匹配,输出原因
if(!(this.password.equals(password)))
throw new PassWordException("密码匹配失败!");
else System.out.println("匹配成功!");
}
//声明异常
public static void main(String[] args) throws PassWordException{
//实例化对象
PassWord passWord = new PassWord();
//匹配密码
passWord.Match("666666");//匹配失败,使用自定义异常,抛出异常原因
}
}
发现异常原因后就可以使用try-catch处理异常,上面抛出异常的位置在Match和main中都有抛出,优先在Match方法中处理。
class PassWord{
//密码
String password = "888888";
//声明异常
public void Match(String password) throws PassWordException{
try {
if (!(this.password.equals(password)))
throw new PassWordException("密码匹配失败!");
else System.out.println("匹配成功!");
}catch (PassWordException e){
//处理异常
}finally{
System.out.println("异常已处理!");
}
}
之所以在方法中处理异常,是因为异常的处理顺序是先从第一次出现异常的方法中查找,在方法中没有处理异常,就会跳转到调用此方法的方法中查找,如果还是没有找到,就以此类推,找到main方法中,如果main方法中还未处理异常,就会交给JVM(Java虚拟机)处理,因此当发现异常需要处理时,优先在出现异常的方法中处理异常。