JAVA基础编程——异常处理

异常是所有编程语言中都会存在的一个概念。

异常结构

JAVA中关于异常存在三个关键字:try/catch/finally,异常处理格式为:

try {
    // statement
} [ catch (异常类型 对象) {
    // exception process;
} catch (异常类型 对象) {
    // exception process;
} catch (异常类型 对象) {
    // exception process;
} ... ][finally {
    // statement;
}]

从上面的格式可以看出,异常处理总共存在三种结构:try,try...catch,try...catch...finally。其中try表示异常捕获,catch表示异常处理,finally不管异常是否存在,都会执行。也就是说,finally在这里作为了异常处理的出口,可以用finally来进行log打印。

通常情况下,三种结构都是可以使用的,不过开发中多用finally代码块进行资源释放。但需要注意的是不管是否发生异常,finally代码块都是要执行的。

public class Demo {
    public static void main(String args[]) {
	    System.out.println("Expection start");
		
		try {
		    System.out.println("try 10/0" + 10/0);
		} catch (ArithmeticException e){
		    System.out.println("Exception");
		} finally {
		    System.out.println("finally");
		}
		
		System.out.println("Exception end");
	}
}

结果为:

Expection start
Exception
finally
Exception end

从上面的结果可以看出,try代码块出现了异常,该异常被catch之后,打印了catch的相关处理,该异常已经被捕获,但是还是要执行finally代码块的。

另外上边异常出现时候的打印结果是由用户自定义的,除此之外,还可以使用异常类中提供的printStackTrace方法进行异常信息的完整输出。

public class Demo {
    public static void main(String args[]) {
	    System.out.println("Expection start");
		
		try {
		    System.out.println("try 10/0" + 10/0);
		} catch (ArithmeticException e){
		    e.printStackTrace();
		} finally {
		    System.out.println("finally");
		}
		
		System.out.println("Exception end");
	}
}

此时的打印结果为:

Expection start
java.lang.ArithmeticException: / by zero
        at Demo.main(Demo.java:6)
finally
Exception end

上边的打印说明了是在该文件的第6行出现的,是由于除0导致的。

异常处理流程

之前提到可以用catch来捕获异常,而catch后要跟异常的类型,但是实际使用中,很难预测到出现异常所对应的异常类型。而由于每个异常类都是从最初的父类继承而来的,那么便可以直接使用最初的父类作为接口进行接收。

那么最初的父类是什么,首先看一下两个异常类的继承关系:

//java.lang.ArithmeticException
java.lang.Object
  |-java.lang.Throwable
    |-java.lang.Exception
	  |-java.lang.RuntimeException
	    |-java.lang.ArithmeticException

//java.lang.NumberFormatException
java.lang.Object
  |-java.lang.Throwable
    |-java.lang.Exception
	  |-java.lang.RuntimeException
	    |-java.lang.IllegalArgumentException
		  |-java.lang.NumberFormatException

从上面的继承关系来看,两者都是来自于Exception这个异常类,那么便可以直接使用该类作为catch的异常类型:

public class Demo {
	public static void main(String args[]) {
		System.out.println("Expection start");
		
		try {
			System.out.println("try 10/0" + 10/0);
		} catch (Exception e){
			e.printStackTrace();
		} finally {
			System.out.println("finally");
		}
		
		System.out.println("Exception end");
	}
}

打印的结果跟之前是一样的。

但其实Exception还有父类Throwable,该类有两个子类:

  • Error:指JVM错误,这是的程序并没有执行,无法处理
  • Exception:指程序运行过程中出现的异常,用户可以使用异常处理格式处理

而对于JAVA中异常的处理流程可以用下边的图示:

 上边的图示只是将上面的代码进行了可视化,其实原理是一样的。

而既然所有的异常其父类也为Throwable,那么是否可以使用Throwable作为catch代码块的捕获类型。从原则上来讲是可以的,但是因为用户无法处理Error,因此在实际开发中,只需要关注Exception就可以。

而如果开发中对于发生的异常,能够部分猜测到异常的类型,或者想要针对某种特定的异常类型进行特定的处理,不过此时要设置特定的异常处理顺序,一般为小范围在先,大范围在后。

public class Demo {
	public static void main(String args[]) {
		System.out.println("Expection start");
		
		try {
			System.out.println("try 10/0" + 10/0);
		} catch (ArithmeticException  e){
			System.out.println("====================");
			e.printStackTrace();
			System.out.println("====================");
		} catch (Exception e) {
		    e.printStackTrace();
		}
		finally {
			System.out.println("finally");
		}
		
		System.out.println("Exception end");
	}
}

此时打印结果为:

Expection start
====================
java.lang.ArithmeticException: / by zero
        at Demo.main(Demo.java:6)
====================
finally
Exception end

而如果try代码块中出现的不是ArithmeticException类型的异常,就会被后边的catch代码块捕获。

throws

throws关键字主要在方法定义上使用,表示此方法中不进行异常的处理,而是交给被调用处进行处理。

若没有throws关键字修饰:

public class Demo {
	public static void main(String args[]) {
		System.out.println("Expection start");
		
		fun();
		
		System.out.println("Exception end");
	}
	
	public static void fun(){
	    System.out.println("fun try 10/0" + 10/0);
	}
}

这样的写法是OK的,因为在fun中就会对异常进行处理。此时打印结果为:

Expection start
Exception in thread "main" java.lang.ArithmeticException: / by zero
        at Demo.fun(Demo.java:11)
        at Demo.main(Demo.java:5)

由于此时出现异常,直接导致程序结束,屏蔽了最后Exception end的打印。

而如果使用throws关键字修饰:

public class Demo {
	public static void main(String args[]) {
		System.out.println("Expection start");
		
		try {
			fun();
		} catch (ArithmeticException  e){
			System.out.println("====================");
			e.printStackTrace();
			System.out.println("====================");
		} catch (Exception e) {
		    e.printStackTrace();
		}
		finally {
			System.out.println("finally");
		}
		
		System.out.println("Exception end");
	}
	
	public static void fun() throws Exception{
	    System.out.println("fun try 10/0" + 10/0);
	}
}

上边的代码中必须在该函数调用处进行异常处理,否则编译不过。此时的打印结果为:

Expection start
====================
java.lang.ArithmeticException: / by zero
        at Demo.fun(Demo.java:22)
        at Demo.main(Demo.java:6)
====================
finally
Exception end

而此时由于异常已经处理过了,最后的Exception end可以正常打印。

throw

throw表示抛出一个异常。之前的异常都是由JVM自动进行实例化操作的,而使用throw可以手动抛出一个实例化对象。

public class Demo {
	public static void main(String args[]) {
		System.out.println("Expection start");
		
		try {
			throw new Exception("My Exception");
		} catch (Exception e) {
		    e.printStackTrace();
		} finally {
			System.out.println("finally");
		}
		
		System.out.println("Exception end");
	}
}

打印结果为:

Expection start
java.lang.Exception: My Exception
        at Demo.main(Demo.java:6)
finally
Exception end

上面代码使用throw手动抛出了一个实例化对象,并进行异常处理,打印了异常的相关信息。

异常处理标准格式

在实际开发中,可能会使用下面的异常处理格式:

public class Demo {
	public static void main(String args[]) {
		System.out.println("Main start");
		
		try {
			fun();
		} catch (Exception e) {
		    e.printStackTrace();
		} finally {
			System.out.println("Exception process finally");
		}
		
		System.out.println("Main end");
	}
	
	public static void fun() throws Exception {
	    System.out.println("Fun start");
		
		try {
			System.out.println("Fun 10 / 0" + 10 / 0);
		} catch (Exception e) {
			System.out.println("Fun exception throw");
		    throw e;
		} finally {
			System.out.println("Fun end");
		}
	}
}

打印结果为:

Main start
Fun start
Fun exception throw
Fun end
java.lang.ArithmeticException: / by zero
        at Demo.fun(Demo.java:20)
        at Demo.main(Demo.java:6)
Exception process finally
Main end

上边的代码主要有以下几点注意:

  • 调用fun时,用户和调用处需要知道fun是否会正常执行,如果Fun start之后打印Fun end表示正常结束,如果存在Fun exception throw,则表明执行异常
  • fun内部不需要处理异常,只需要使用throws将可能出现的异常交给调用处处理即可
  • fun中的catch中可以捕获可能出现的异常,然后执行某些打印或其它处理,然后将该异常抛出即可
  • 上层拿到该异常进行处理
  • 有时用户并不关注fun中的执行细节,只需要知道该过程是否正常执行即可
  • 由于fun已经使用了throws修饰,表明该方法中的异常是由调用处处理的,因此便可以省略catch代码块,异常也会自动抛出到调用处,不过通常不这么使用,因为取消catch代码块就意味着某些处理一块被取消了,可能会出现误解

上边的格式可以用于数据库操作,将数据库操作封装到fun中,外部不需要知道执行细节,只需要知道是否正常执行即可。

assert

assert是断言的意思,即是对程序执行进行强制判断,若判断失败,则Error。

public class Demo {
	public static void main(String args[]) {
		System.out.println("Main start");
		
		try {
			assert(1 + 1 == 3);
		} catch (Exception e) {
		    e.printStackTrace();
		} finally {
			System.out.println("Exception process finally");
		}
		
		System.out.println("Main end");
	}
}

使用下面的语句开启断言:

java -ea Demo

打印结果为:

Main start
Exception process finally
Exception in thread "main" java.lang.AssertionError
        at Demo.main(Demo.java:6)

上面的代码中,使用assert对语句进行断言,并希望用catch捕获可能出现的异常,但catch语句未执行,说明assert失败后并不抛出异常。而开启断言后,断言失败会出现Error。

RuntimeException

RuntimeException类的性质为:程序在编译时不强制要求用户处理异常,用户可以根据需要选择性地进行处理,但是如果没有处理又发生了异常,就交给JVM默认处理。也就是说该类地子类,可以由用户根据需要选择性地进行处理。

比如之前提到的NumberFormatException异常:

//java.lang.NumberFormatException
java.lang.Object
  |-java.lang.Throwable
    |-java.lang.Exception
	  |-java.lang.RuntimeException
	    |-java.lang.IllegalArgumentException
		  |-java.lang.NumberFormatException

而int的包装类Integer类的parseInt方法其定义为:

public static int parseInt(String s)
                    throws NumberFormatException

即该方法可能会抛出NumberFormatException异常,而throws的使用会要求在调用处进行异常处理,而由于RuntimeException类的性质,该方法调用可以写为:

public class Demo {
	public static void main(String args[]) {
		System.out.println("Main start");
		
		System.out.println(Integer.parseInt("100"));
		
		System.out.println("Main end");
	}
}

打印结果为:

Main start
100
Main end

上面的代码中,调用处没有异常处理也会编译通过,这就是由RuntimeException类的性质决定的。而如果一旦出现异常,就交给JVM处理。

自定义异常

既然异常是类,那么便可以进行继承,构建自定义异常:

class MyException extends Exception {
    public MyException(String msg) {
	    super(msg);
	}
}

public class Demo {
	public static void main(String args[]) {
		System.out.println("Main start");
		
		try {
		    throw new MyException("My Exception");
		} catch (Exception e) {
		    System.out.println("Exception catch");
		}
		
		System.out.println("Main end");
	}
}

执行结果为:

Main start
Exception catch
Main end

使用自定义异常可以定义用户自己的异常类型,并进行自定义处理。如果要构建自定义工具的话,可以使用自定义异常进行异常处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值