一个方法不仅需要告诉编译器将要返回什么值,还要告诉编译器有可能发生什么错误。
异常处理正确的做法:
低层次的方法抛出异常,让高层次的方法去捕获异常并通告用户发生了错误
未检查(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);
}
}