异常

定义:程序不正常的行为或状态
例如:

  • int a = 5/0;
  • 数组访问越界
  • 读取数据,结果该文简不存在

异常分类

java程序常见错误 java程序异常_java


Throwable:所有错误的祖先

Error:系统内部错误或者资源耗尽,不管

Exception:程序有关的异常,重点关注

Exception包括RuntimeException程序自身的错误,如空指针,数组越界等;

非RuntimeException外界相关的错误,如打开一个不存在的文件,加载一个不存在的类。

按照编译器是否辅助检查分类:
Unchecked Exception:编译器不会辅助检查的,需要程序员自己处理的异常,包括Error子类和RuntimeException子类

程序必须处理,以预防为主

Checked Exception:是非RuntimeException子类,编译器要管,如读取一个不存在的文件

此种错误以发生后处理为主,编译器会辅助检查
编译器会检查程序是否为checked exception配置了处理,如果没有处理,会报错。

Error类的异常不用处理,属于资源耗尽等。

异常处理

定义:程序返回到安全状态或是允许用户保存结果,并以适当方式关闭程序
目的:

  • 允许用户及时保存结果
  • 抓住异常,分析异常内容
  • 控制程序返回到安全状态

使用try-catch-finally保护代码正常运行机制
异常结构:try-(可以有多个) ;try-catch-finally ;try-finally
(try必须有,catch和finally至少要有一个)

try里写正常业务逻辑代码
catch:当try发生异常,将执行catch代码,若无异常,则不执行
finally:当try或catch执行结束后,必须要执行finally

public class TryDemo {

	public static void main(String[] args) {
		try
		{
			int a = 5/2; 
			System.out.println("a is " + a);
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
		}
		finally
		{
			System.out.println("Phrase 1 is over");
		}
		
		try
		{
			int a = 5/0; //ArithmeticException
			System.out.println("a is " + a);
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
		}
		finally
		{
			System.out.println("Phrase 2 is over");
		}
		
		try
		{
			int a = 5/0; //ArithmeticException
			System.out.println("a is " + a);
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			int a = 5/0; //ArithmeticException
		}
		finally
		{
			System.out.println("Phrase 3 is over");
		}
	}
}

输出

a is 2
Phrase 1 is over
Phrase 2 is over
Phrase 3 is over
java.lang.ArithmeticException: / by zero
	at TryDemo.main(TryDemo.java:21)
java.lang.ArithmeticException: / by zero
	at TryDemo.main(TryDemo.java:35)
Exception in thread "main" java.lang.ArithmeticException: / by zero
	at TryDemo.main(TryDemo.java:41)

关于catch块:

catch块可以有多个,每个有不同的入口形参,当已发生的异常和某一个catch块中的形参类型一致,那么将执行该catch块中的代码,如果没有一个匹配,catch也不会被触发,最后都进入finally块。
进入catch块后,并不会返回到try发生异常的位置,也不会执行后续的catch块,一个异常只能进入一个catch块。

try
		{
			int a = 5/0;
			System.out.println("a is " + a);
		}
		catch(ArithmeticException e)
		{
			e.printStackTrace();
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
		}		
		finally
		{
			System.out.println("Phrase 2 is over");
		}
catch块的异常匹配是从上而下进行匹配的,所以一般是将小的异常写在前面,而一些大(宽泛)的异常则写在末尾

try-catch-finally每个模块里面也会发生异常,所以也可以在内部继续写一个完整的try结构。

throws方法
方法可能存在异常的语句,但不处理,那么可以使用throws来声明异常;
调用带有throws异常的方法,要么处理这些异常,或者再次向外throws,直到main函数为止。

public class ThrowsDemo
{
	public static void main(String [] args)
	{
		try
		{
			int result = new Test().divide( 3, 1 );
			System.out.println("the 1st result is" + result );
		}
		catch(ArithmeticException ex)
		{
			ex.printStackTrace();
		}
		int result = new Test().divide( 3, 0 );
		System.out.println("the 2nd result is" + result );
	}
}
class Test
{
	//ArithmeticException is a RuntimeException, not checked exception
	public int divide(int x, int y) throws ArithmeticException
	{
		int result = x/y;
		return x/y;
	}
}

异常处理中的继承问题
一个方法如果被覆盖,覆盖它的方法必须抛出相同的异常,或者异常的子类;
如果父类的方法抛出多个异常,那么重写的子类方法必须抛出那些异常的子集,也就是不能抛出新的异常

自定义异常

Exception类是所有异常的父类
Exception继承自Throwable类,同时有一个兄弟Error(一般是更严重的问题, 一般是系统层面的,无需程序处理),程序只需要处理Exception

自定义异常,需要继承Exception类或其子类

  • 继承自Exception,就变成Checked Exception
  • 继承自Runtime Exception,就变成Unchecked Exception

自定义重点在于构造函数

  • 调用父类Exception的message构造函数
  • 可以自定义自己的成员变量

在程序中采用throw主动抛出异常

实例1

public class MyException extends Exception {

	private String returnCode ;  
	private String returnMsg;  
	
	public MyException() {
		super();
	}

	public MyException(String returnMsg) {
		super(returnMsg);
		this.returnMsg = returnMsg;
	}

	public MyException(String returnCode, String returnMsg) {
		super();
		this.returnCode = returnCode;
		this.returnMsg = returnMsg;
	}

	public String getReturnCode() {
		return returnCode;
	}

	public String getreturnMsg() {
		return returnMsg;
	}
}
public class MyExceptionTest {
	public static void testException() throws MyException {  
       throw new MyException("10001", "The reason of myException");  
         //在方法内部程序中,抛出异常采用throw关键字,在方法头部声明中,声明异常采用throws关键字
    }  
	
	public static void main(String[] args) {

		//MyExceptionTest.testException();
		//在main函数中,程序调用了一个声明MyException异常的方法,但没有处理
		//因为MyException继承Exception,属于非RuntimeException即CheckedException,因此编译器会检查到程序没有处理报错
		//解决办法采用try结构或main函数也throws MyException
		try {
			MyExceptionTest.testException();
		} catch (MyException e) {
			e.printStackTrace();
			System.out.println("returnCode:"+e.getReturnCode());
			System.out.println("returnMsg:"+e.getreturnMsg());
		}
	}
}

实例2

public class DivideByMinusException extends Exception {
	int divisor;
	public DivideByMinusException(String msg, int divisor)
	{
		super(msg);
		this.divisor = divisor;
	}
	public int getDevisor()
	{
		return this.getDevisor();
	}
}
public class Student {
	
	public int divide(int x, int y) 
	{
		return x/y;
	}
	
	public static void main(String[] args) throws DivideByMinusException{
		Student newton = new Student();
		//newton.divide2(5, 0);
		newton.divide5(5, -2);
	}	
	
	public int divide2(int x, int y)
	{
		int result;
		try
		{
			result = x/y;
			System.out.println("result is " + result);
		}
		catch(ArithmeticException ex)
		{
			System.out.println(ex.getMessage());
			return 0;
		}
		catch(Exception ex)
		{
			ex.printStackTrace();
			return 0;
		}
		return result;
	}
	
	//ArithmeticException is a unchecked exception,编译器可以不管
	public int divide3(int x, int y) throws ArithmeticException
	{		
		return x/y;
	}
	
	public int divide4(int x, int y) 
	{		
//		try
//		{
//			return divide3(x,y);
//		}
//		catch(ArithmeticException ex)
//		{
//			ex.printStackTrace();
//			return 0;
//		}
		return divide3(x,y);  //尽管divide3报告异常,divide4无需处理,因为这个异常是unchecked exception
		//如果调用divide5(x,y);  那么就需要做try catch处理 因为它抛出checked exception
	}
	
	public int divide5(int x, int y) throws DivideByMinusException
	{		
		try
		{
			if(y<0)
			{
				throw new DivideByMinusException("The divisor is negative", y);
			}
			return divide3(x,y);
		}
		catch(ArithmeticException ex)
		{
			ex.printStackTrace();
			return 0;
		}
	}
}

资料来源:
[1]Java核心技术—华东师范大学MOOC