异常处理

一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误。

 

异常处理正确的做法: 

低层次的方法抛出异常,让高层次的方法去捕获异常并通告用户发生了错误

  

未检查(unchecked)异常:

派生于Error类或RuntimeException类的所有异常

 

异常处理方式:

声明异常(推荐做法),即在方法上声明可能发生的已检查异常,交给上层方法去处理。

捕获异常,可以捕获后对异常进行包装,抛给上层方法;或者对异常进行适当处理后不抛出。

一般原则:应该捕获那些知道如何处理的异常,而将那些不知道怎样处理的异常传递出去

 

子类中覆盖了超类的一个方法,子类方法中声明的已检查异常不能超过超类方法中声明的异常范围。

不允许在子类的throws说明符中出现超过超类方法所列出的异常类范围:

如,Runnable中的run()就不能抛出任何已检查的异常,必须捕获所有checkedException并处理。

 

catch块捕获异常后如何处理?

1. 直接处理异常

2. 直接抛出,或者包装后抛出,或者抛出自定义异常

3. 异常链,通过堆栈记录所有异常信息

 

finally 确保系统资源被释放

finally中如果包含return语句:

方法返回的是finally中return的值(try块中return的值被覆盖了)。

 

处理异常的几个例子:

sample1:资源释放与异常捕捉分开写,结构更清晰

public static void main(String[] args) {
	try {
		InputStream in = null;
		try {
			in = new FileInputStream(new File(""));
			//read data from stream
		} finally {
			in.close();
		}
	} catch(Exception e) {
		e.printStackTrace();
	}
}

 

sample2:InterruptedException的两种处理方式

public static void t1() {
	Thread t = new Thread(new Runnable() {
		int i = 1;
		@Override
		public void run() {
			//循环检测线程是否已被中断
			while(!Thread.currentThread().isInterrupted() && i<1000) {
				System.out.println(i++);
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					//设置中断标记,下次检测此标记结束循环
					Thread.currentThread().interrupt();
				}
			}
				
		}
	});
	t.start();
	for(int i=0;i<10000;i++){System.out.print("");}
		
	t.interrupt();
}

public static void t2() {
	Thread t = new Thread(new Runnable() {
		int i = 1;
		@Override
		public void run() {
			try {
				while(i<1000) {
					System.out.println(i++);
					Thread.sleep(100);
				}
			} catch (InterruptedException e) {
				//没有状态检测的情况下,打印异常,直接退出
				e.printStackTrace();
			}
		}
	});
	t.start();
	for(int i=0;i<10000;i++){System.out.print("");}
		
	t.interrupt();
}

 

sample3, 自定义异常

public class AppException extends Throwable {

	private static final long serialVersionUID = 9168776604038242476L;

	public AppException() {
		super();
	}
	
	//异常消息+异常链
	public AppException(String message, Throwable cause) {
		super(message, cause);
	}
	
	//异常消息【如果不想打印出异常链,就使用这个构造方法】
	public AppException(String message) {
		super(message);
	}
	
	//异常链
	public AppException(Throwable cause) {
		super(cause);
	}
}

 

 

未捕获异常处理器

线程的run方法不能抛出任何被检测的异常, 这些异常必须被处理。

但是,仍然会发生一些不被检测的异常,最终导致线程终止。

现在就要开始处理这些未捕获的异常。

 

Java对这些unchecked异常(RuntimeException、Error)是怎么处理的呢?

在线程死亡之前,异常被传递到一个用于未捕获异常的处理器。

该处理器必须实现一个接口:Thread.UncaughtExceptionHandler

 

从Java SE 5.0起, 可以用setUncaughtExceptionHandler方法为任何线程安装一个处理器;

Thread类的静态方法setDefaultUncaughtExceptionHandler为所有线程安装一个默认的处理器。

 

独立线程默认的处理器就是该线程的ThreadGroup对象。默认所有线程都在同一个线程组。

 

ThreadGroup类实现Thread.UncaughtExceptionHandler接口,uncaughtException方法做如下操作:
1)如果该线程组有父线程组,那么父线程组的uncaughtException方法被调用。
2)否则,如果Thread设置了默认处理器,则调用该处理器。
3)否则,如果Throwable是ThreadDeath的一个实例(由stop方法生成),什么都不做。

4)否则,线程的名字以及Throwable的栈踪迹被输出到System.err上。

 

/**
 * 实现接口Thread.UncaughtExceptionHandler
 * 未捕获异常处理器
 */
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

	static {
		//为所有线程设置默认处理器【对线程池中的线程,此设置无效】
		Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
	}

	public static void main(String[] args) {
		int i = Integer.parseInt("Hello");
		System.out.println(i);
	}

	@Override
	public void uncaughtException(Thread t, Throwable e) {
		//处理未被捕获的异常
		if(e!=null)
			logToFile(e);
	}
	public static void logToFile(Throwable t) {
		try {
			File log = new File("exception.log");
			FileWriter fw = new FileWriter(log, true);
			PrintWriter pw = new PrintWriter(fw,true);
			t.printStackTrace(pw);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

 

 

线程池中未捕获的异常消失了

 

线程池中未捕获异常的处理(即catch块没有捕获到的异常)

普通线程池,如singlePool,cachedPool,fixedPool

如果通过execute()执行任务,未检查异常会被自动抛出

如果通过submit()提交任务,异常只能通过future.get()被调用时才会抛出,如果没有调用get(),异常将不会出现,即异常消失了!

 

对于任务调度线程池执行任务时发生的异常,如scheduledPool

execute() 不会抛出异常信息,如果catch块没有捕获,则发生的异常将消失

submit(), schedule() 异常信息将在get()被调用时抛出

 

还有一种更好的处理方式:对上述2种不同的线程池都可以获取到异常。

线程池对未捕获异常的处理提供了外部接口,通过覆盖afterExecute()来自定义未捕获异常的处理行为:

  第一步,自定义线程池类,继承concurrent包中某个线程池即可;

  第二步,覆盖afterExecute(),处理未捕获的异常。

 

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;


public class MyThreadPoolExecutor extends ThreadPoolExecutor {
	
	public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
			long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> blockingQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, blockingQueue);
	}
	
	//处理未捕获异常
	protected void afterExecute(Runnable r, Throwable t) {
	     super.afterExecute(r, t);
	     if (t == null && r instanceof Future) {
	       try {
	         Object result = ((Future) r).get();
	       } catch (CancellationException ce) {
	           t = ce;
	       } catch (ExecutionException ee) {
	           t = ee.getCause();
	       } catch (InterruptedException ie) {
	           Thread.currentThread().interrupt(); // ignore/reset
	       }
	     }
	     if (t != null)
	    	 ExcRecorder.logAll(t);
	  }
}

class MyScheduledThreadPoolExecutor extends ScheduledThreadPoolExecutor {

	public MyScheduledThreadPoolExecutor(int corePoolSize) {
		super(corePoolSize);
	}
	
	//处理未捕获异常
	protected void afterExecute(Runnable r, Throwable t) {
	     super.afterExecute(r, t);
	     if (t == null && r instanceof Future) {
	       try {
	         Object result = ((Future) r).get();
	       } catch (CancellationException ce) {
	           t = ce;
	       } catch (ExecutionException ee) {
	           t = ee.getCause();
	       } catch (InterruptedException ie) {
	           Thread.currentThread().interrupt(); // ignore/reset
	       }
	     }
	     if (t != null)
	    	 ExcRecorder.logAll(t);
	  }
}

class ExcRecorder {

	public static void logCosole(Throwable t) {
		t.printStackTrace();
	}
	
	public static void logToFile(Throwable t) {
		try {
			File log = new File("exception.log");
			FileWriter fw = new FileWriter(log, true);
			PrintWriter pw = new PrintWriter(fw,true);
			t.printStackTrace(pw);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void listStackTace(Throwable t) {
		StringBuilder builder = new StringBuilder();
		@SuppressWarnings("restriction")
		String newLine = java.security.AccessController.doPrivileged(
				new sun.security.action.GetPropertyAction("line.separator"));
		builder.append(t.getClass().getName()+":"+t.getMessage()).append(newLine);
		for(StackTraceElement st : t.getStackTrace()) {
				builder.append("\t");
				builder.append(st.toString());
				builder.append(newLine);
		}
		System.out.println(builder.toString());
	}
	
	public static void logAll(Throwable t) {
		logCosole(t);
		logToFile(t);
		listStackTace(t);
	}
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值