Core Java (十七) 异常

异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器。

如果某个方法不能够采用正常的途径完成它的任务,就可以通过另外一个路径退出方法。这时,方法不会继续执行,也不返回任何值,而是抛出一个封装了错误信息的对象,异常处理机制开始搜索能偶处理这种异常的异常处理器。


异常对象全部派生自Thorwable类。

下面的图表是OCJP的考试要求的几个异常,其实异常比这个要多得多,这只是一个简化的图,表明了各个异常之间的层次关系。



IOException:只有checked异常才有必要throws,即只有IOException异常才有必要thorws

Error:任何程序代码都有抛出从Error继承的异常的能力,而我们对其没有任何控制能力。

RuntimeException:我们应该避免从RuntimeException继承的异常的发生。


总之,一个方法必须声明所有可能抛出的checked异常,而unchecked异常要么不可控制(Error),要么就应该避免发生(RuntimeException)。


抛出异常

声明已检查(checked,也就是IOException类及其子类)异常

public FileInputStream(String name) throws FileNotFoundException{
}

而已检查异常又分为两种,存在,或者不存在。

对于一个已经存在的异常,

  1. 找到一个合适的异常类
  2. 创建这个类的一个对象
  3. 将对象抛出
String readData(Scanner in) throws EOFException{
    	...
    	while(...){
    		if(!in.hasNext()){
    			if(n < len)
    				thorw new EOFException();
    		}
    	}
    	return s;
    }

对于不存在的异常,要首先自己定义类。
  1. 创建一个合适的异常类
  2. 创建这个类的一个对象
  3. 将对象抛出
定义一个异常类通常要有两个构造器,一个是无参数的,一个是有String参数的。
class FileFormatException extends IOException{
	public FileFormatException(){}
	public FileFormatException(String gripe){
		super(gripe);
	}
这样,就可以抛出自定义的异常了。
String readData(BufferedReader in) throws FileFormatException{
    	...
    	while(...){
    		if(ch == -1){
    			if(n < len)
    				thorw new FileFormatException();
    		}
    	}
    	return s;
    }

捕获异常

捕获异常的一般格式

try{
    	//code
    }
    catch(ExceptionType e){
    	//handler for this type
    }


java.lang.throwable里面有一个getMessage方法,用来获得Throwable对象的详细信息。


catch放的位置也有讲究,如下情况:

由于FileNotFoundException继承自IOException,所以必须把IOException放到后面,否则编译器会报错(Unreachable catch block for FileNotFoundException. It is already handled by the catch block for IOException)。

package com.xujin;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

public class Test 
{ 
    public static void main(String [] args){ 
    	Test test = new Test();
    	test.read("fo.in");
    }
    
    public void read(String fileName){
    	try{
    		InputStream in = new FileInputStream(fileName);
    		int b;
    		while((b = in.read()) != -1){
    			System.out.print(b);
    		}
    	}
    	
    	catch(FileNotFoundException e){
    		e.printStackTrace();
    		System.out.println(e.getMessage());    
    		System.out.println(e.getClass().getName());
    	}
    	
    	catch(IOException e){
    		e.printStackTrace();
    		System.out.println(e.getMessage());   
    		System.out.println(e.getClass().getName());
    	}
    	
    	
    }
    
   
} 

结果如下;


再次抛出异常

package com.xujin;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

public class Test 
{ 
    public static void main(String [] args) throws IOException{ 
    	Test test = new Test();
    	test.read("fo.in");
    }
    
    public void read(String fileName) throws IOException{
    	try{
    		InputStream in = new FileInputStream(fileName);
    		int b;
    		while((b = in.read()) != -1){
    			System.out.print(b);
    		}
    	}	
    	
    	catch(IOException e){
    		throw new IOException("ERROR, IOException, " + e.getMessage());
    	}   	
    	
    }    
   
} 




Finally子句

当程序获得了一些本地资源,如果还没有来得及释放就产生异常了,那么就产生了资源回收的问题。

最笨的解决办法是在处理异常的代码catch中回收资源,但这样比较乏味。

这就要使用Finally字句,不管是否有异常被捕获,finally子句都要执行。

如下例子:

不管是否有异常,in.close()都要执行。

public void read(String fileName) throws IOException{
    	InputStream in = new FileInputStream(fileName);
    	try{    		
    		int b;
    		while((b = in.read()) != -1){
    			System.out.print(b);
    		}
    	}	
    	
    	catch(IOException e){
    		 throw new IOException("ERROR, IOException, " + e.getMessage());
    	}
    	
    	finally{
    		in.close();
    	}
    	
    }    


为了提高代码的清晰度,我们可以这样做:

内层try/catch语句的作用是确保关闭输入流,外层try/catch语句的作用是确保报告出现的错误。

public void read(String fileName) throws IOException{
    	InputStream in = new FileInputStream(fileName);
    	try{    		
    		try{
    			int b;
        		while((b = in.read()) != -1){
        			System.out.print(b);
        		}
    		}    		
    		
    		finally{
        		in.close();
        	}
    	}	
    	
    	catch(IOException e){
    		 throw new IOException("ERROR, IOException, " + e.getMessage());
    	}   	
    }    


注意:

  1. 不要在finally子句中使用return语句,因为这样会造成真正的返回值被覆盖的错误。
  2. finally字句中千万不要抛出异常,否则整个try/catch块会抛出假异常(即finally子句中抛出的异常)。

分析堆栈跟踪元素

堆栈跟踪是一个方法调用过程的列表,它包含了程序执行过程中方法调用的特定位置。

getStackTrace方法获得一个StackTraceElement类型的数组,我们可以逐个元素的进行分析。

getStackTrace
public StackTraceElement[] getStackTrace()
Provides programmatic access to the stack trace information printed by  printStackTrace(). Returns an array of stack trace elements, each representing one stack frame. The zeroth element of the array (assuming the array's length is non-zero) represents the top of the stack, which is the last method invocation in the sequence. Typically, this is the point at which this throwable was created and thrown. The last element of the array (assuming the array's length is non-zero) represents the bottom of the stack, which is the first method invocation in the sequence.

Some virtual machines may, under some circumstances, omit one or more stack frames from the stack trace. In the extreme case, a virtual machine that has no stack trace information concerning this throwable is permitted to return a zero-length array from this method. Generally speaking, the array returned by this method will contain one element for every frame that would be printed by printStackTrace. Writes to the returned array do not affect future calls to this method.

Returns:
an array of stack trace elements representing the stack trace pertaining to this throwable.
Since:
1.4

getAllStackTraces方法,产生所有线程的堆栈跟踪。

getAllStackTraces
public static Map<Thread,StackTraceElement[]> getAllStackTraces()
Returns a map of stack traces for all live threads. The map keys are threads and each map value is an array of  StackTraceElement that represents the stack dump of the corresponding  Thread. The returned stack traces are in the format specified for the  getStackTrace method.

The threads may be executing while this method is called. The stack trace of each thread only represents a snapshot and each stack trace may be obtained at different time. A zero-length array will be returned in the map value if the virtual machine has no stack trace information about a thread.

If there is a security manager, then the security manager's checkPermission method is called with a RuntimePermission("getStackTrace") permission as well as RuntimePermission("modifyThreadGroup") permission to see if it is ok to get the stack trace of all threads.

Returns:
Map from  Thread to an array of  StackTraceElement that represents the stack trace of the corresponding thread.
Throws:
SecurityException - if a security manager exists and its  checkPermission method doesn't allow getting the stack trace of thread.
Since:
1.5
See Also:
getStackTrace()SecurityManager.checkPermission(java.security.Permission)RuntimePermissionThrowable.getStackTrace()


StackTraceElement方法具有toString方法,可以直接打印出堆栈的状态信息。


public class StackTraceTest
{
   /**
      Computes the factorial of a number
      @param n a nonnegative integer
      @return n! = 1 * 2 * . . . * n
   */
   public static int factorial(int n)
   {
      System.out.println("factorial(" + n + "):");
      Throwable t = new Throwable();
      StackTraceElement[] frames = t.getStackTrace();
      for (StackTraceElement f : frames)
         System.out.println(f);
      int r;
      if (n <= 1) r = 1;
      else r = n * factorial(n - 1);
      System.out.println("return " + r);
      return r;         
   }

   public static void main(String[] args)
   {
      Scanner in = new Scanner(System.in);
      System.out.print("Enter n: ");
      int n = in.nextInt();
      factorial(n);
   }
}



OCJP考纲中的异常类解析

ArrayIndexOutOfBoundsException

java.lang.ArrayIndexOutOfBoundsException

数组索引越界异常。当对数组的索引值为负数或大于等于数组大小时抛出。

    	int[] i = new int[10];
    	System.out.print(i[12]);//java.lang.ArrayIndexOutOfBoundsException: 12

ClassCastException

java.lang.ClassCastException

类造型异常。假设有类A和B(A是B的父类),O是A的实例,那么当强制将O构造为类B的实例时抛出该异常。该异常经常被称为强制类型转换异常。

Thrown to indicate that the code has attempted to cast an object to a subclass of which it is not an instance. 

例如:

    	Object x = new Integer(0);
    	System.out.println((String)x);//Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

 IllegalArgumentException

java.lang.IllegalArgumentException

违法的参数异常。一个方法被传递了非法的或者不合适的参数,而调用了该方法时,抛出该异常。

Thrown to indicate that a method has been passed an illegal or inappropriate argument.


 IllegalStateException

java.lang.IllegalStateException

违法的状态异常。当在Java环境和应用尚未处于某个方法的合法调用状态,而调用了该方法时,抛出该异常。


NullPointerException

java.lang.NullPointerException

空指针异常。当应用试图在要求使用对象的地方使用了null时,抛出该异常。譬如:调用null对象的实例方法、访问null对象的属性、计算null对象的长度、使用throw语句抛出null等等。

    	String b = null;
    	System.out.print(b.hashCode());//Exception in thread "main" java.lang.NullPointerException

NumberFormatException

java.lang.NumberFormatException

数字格式异常。当试图将一个String转换为指定的数字类型,而该字符串确不满足数字类型要求的格式时,抛出该异常。

    	System.out.println(Integer.parseInt("!123"));//Exception in thread "main" java.lang.NumberFormatException: For input string: "!123"

AssertionError

java.lang.AssertionError

断言错。用来指示一个断言失败的情况。


 ExceptionInInitializerError

java.lang.ExceptionInInitializerError

初始化程序错误。当执行一个类的静态初始化程序的过程中,发生了异常时抛出。静态初始化程序是指直接包含于类中的static语句段


StackOverflowError  

java.lang.StackOverflowError

堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。


NoClassDefFoundError

java.lang.NoClassDefFoundError

未找到类定义错误。当Java虚拟机或者类装载器试图实例化某个类,而找不到该类的定义时抛出该错误。




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值