import java.util.logging.Logger; import java.io.*; class LoggingException extends Exception { //静态的Logger.getLogger()方法创建了一个String参数相关联的Logger对象(通常与错误相关的包名或类名) //该Logger对象会将其输出发送到System.err private static Logger logger= Logger.getLogger("LoggingException"); public LoggingException() { StringWriter trace= new StringWriter(); /*为产生日志记录消息,我们需要获取异常抛出处的栈轨迹,但printStackTrace()不会默认产生字符串 因此,使用重载的printStackTrace(),它接受java.io.PrintWriter对象作为参数,此对象(即trace)传入 后,调用toString()就可以将输出抽取为String。*/ printStackTrace(new PrintWriter(trace)); //向Logger写入的最简单方式就是直接调用与日志记录消息级别相关联的方法,这里为serve() logger.severe(trace.toString()); } } public class test { public static void main(String[] args) { try { throw new LoggingException(); } catch(LoggingException e) { System.err.println("Caught"+e); } try { throw new LoggingException(); } catch(LoggingException e) { System.err.println("Caught"+e); } } } /* 输出: 7月 06, 2021 10:50:24 上午 LoggingException <init> 严重: LoggingException at test.main(test.java:18) CaughtLoggingException 7月 06, 2021 10:50:24 上午 LoggingException <init> 严重: LoggingException at test.main(test.java:26) CaughtLoggingException */
更常见的情形是,我们需要捕获和记录其他人编写的异常:
import java.util.logging.Logger; import java.io.*; class LoggingException { private static Logger logger= Logger.getLogger("LoggingException"); public LoggingException(Exception e) { StringWriter trace= new StringWriter(); e.printStackTrace(new PrintWriter(trace)); logger.severe(trace.toString()); } } public class test { public static void main(String[] args) { try { throw new NullPointerException(); } catch(NullPointerException e) { new LoggingException(e); } } } /* 输出: 7月 06, 2021 11:11:32 上午 LoggingException <init> 严重: java.lang.NullPointerException at test.main(test.java:18) */
printStackTrace()方法所提供的信息可以通过getStackTrace()方法直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。元素0是栈顶元素,并且是调用序列中的第一个方法调用。
import javax.management.RuntimeErrorException; public class test { static void f() { throw new RuntimeException(); } static void g() { f(); } public static void main(String[] args) { g(); } } /* 输出: Exception in thread "main" java.lang.RuntimeException at test.f(test.java:7) at test.g(test.java:11) at test.main(test.java:14) */
RuntimeException和它的子类,在编译器里没有被说明。如果RintimeException没有被捕获而直达main(),那么程序退出前将调用异常的printStackTrace()方法。注意:只能在代码中忽略RuntimeException和它的子类类型的异常,其他类型异常处理都是由1编译器强制实施,究其原因,RuntimeException代表的是编程错误。
class isException extends Exception{} public class test { static int count=0; public static void main(String[] args) { while(true) { try { if(count++==0) throw new isException(); System.out.println("No exception"); } catch(isException e) { System.out.println("isException"); } finally { System.out.println("In finally clause"); if(count==2) { System.out.println("count==2"); break; } } } } } /* 输出: isException In finally clause No exception In finally clause count==2 */
从输出结果中看出,无论异常是否被抛出,finally子句总能被执行。
作用:对于没有垃圾回收和析构函数自动调用机制的语言来说,finally非常重要。它能使程序员保证:无论try块里发生什么,内存总得到释放。但Java有垃圾回收机制,所以内存释放不再是问题。而且Java也没有析构函数可调用。但在以下方面就用得上finally了:
当要把除内存之外的资源恢复到它们初始状态时,就要用到finally子句。这种需要清理的资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关
public class test { public static void f(int i) { System.out.println("Initialization that requires cleanup"); try { System.out.println("Point 1"); if(i==1) return; System.out.println("Point 2"); if(i==2) return; System.out.println("Point 3"); if(i==3) return; System.out.println("end"); return; } finally { System.out.println("Performing cleanup"); } } public static void main(String[] args) { for(int i=1;i<=4;f(i),i++); } } /* 输出: Initialization that requires cleanup Point 1 Performing cleanup Initialization that requires cleanup Point 1 Point 2 Performing cleanup Initialization that requires cleanup Point 1 Point 2 Point 3 Performing cleanup Initialization that requires cleanup Point 1 Point 2 Point 3 end Performing cleanup */
因为finally子句总是会执行,所以在一个方法中可以从多个点返回,而且可以保证重要的清理工作仍旧会执行。