分成Error和Exception
Error类
描述了Java运行时系统的内部错误和资源耗尽错误。应用程序不应该抛出此种类型的错误。如果出现了这样的内部错误,除了通告给用户,并尽力使程序安全地终止外,再也无能为力
Exception层次结构:最需关注的
RuntimeException 程序错误导致的异常
不是派生于RuntimeException 由于像I/O错误,程序本身没有问题导致的异常
java语言规范 派生于 Error类或RuntimeException类的所有异常称为未检查(unchecked)异常,所有其他异常称为已检查(checked)异常。
以下四种情况自己编写方法时,需要抛出异常
对于可能被他人使用的Java方法,更具异常规范(exception specification),在方法的首部声明这个方法可能抛出异常,如果有多个用逗号隔开
class MyAnimation
{
...
public Image loadImage(String s) throws IOException FileNotFoundException
{
...
}
}
关于子类覆盖超类的方法那一块没看懂
将对象抛出
String readData(Scanner in) thros EOFException
{
...
while(...)
{
if(!in.hasNext())
{
if(n<len)
throw new EOFException();
}
}
...
return S;
}
//还能含有一个字符串参数的构造器
String girpe="Content-length " +len +",Recived" + n ;
throw new EOFException(girpe);
创建一个派生于Exception的类 ,或者派生于Exception子类的类。
一般需要定义两个构造器,一个是默认的,另一个是带有详细描述信息的构造器(超类Throwable的toString方法将会打印这些详细信息)
class FileFormatException extends IOException
{
public FileFormatException() {}
public FileFormatException(String gripe)
{
super(gripe);
}
}
String getMessage()
能获得Throwable对象详细描述信息,即构造时丢进去的String。
如果想捕获异常,以下是最简单的try/catch 语句块
try
{
code
more code
more code
}
catch (ExceptionType e)
{
handler for this type
}
如果try语句块中的任何代码抛出了在catch子句中说明的异常,那么
以下是个简单的例子
public void read(String filename)
{
try
{
InputStrem in = new FileInputStream(filename);
int b;
while((b!=in.read())!=-1)
{
process input;
}
}
catch (IOException exception)
{
exception.printStackTrace();
}
}
对于以上代码
通常最好的选择是什么都不做,而是将异常传递给调用者。如果read方法出现了错误,就让read方法的调用者去操心!如果采用这种处理方式,就必须声明这个方法可能会抛出一个IOException
public void read(String filename) throws IOException
{
InputStrem in = new FileInputStream(filename);
int b;
while((b!=in.read())!=-1)
{
process input;
}
}
基本语句
try
{
}
catch (FileNotFoundException e)
{
}
catch (UnknownHostException e)
{
}
catch (IOException e)
{
}
如果需要获得详细信息
e.getMessage() //详细错误信息
e.getClass().getName() //异常对象的实际类型
如果处理方式一样 合并catch语句
try
{
code..
}
catch (FileNotFoundException | UnknownHostException e)
{
}
捕获异常再次抛出的基本方法
try
{
access the database
}
catch (SQLException e)
{
throw new ServletException("database error: "+e.getMessage());
}
还有一种能不丢失原始异常的方法
try
{
access the database
}
catch (SQLException e)
{
Throwable se=new ServletException("database error");
se.initCause(e);
throw se;
}
当捕获异常时,可以用以下语句重新得到原始异常:
Throwable e = se.getCause();
书上强烈建议这种包装方式,不丢失原异常的细节。
有时只是记录异常
try
{
access the database
}
catch (SQLException e)
{
logger.log(level,message,e);
throw e;
}
基本语法
try
{
}
catch(Exception e)
{
}
finally
{
in.close();
}
无论try中抛出异常,还是catch中抛出异常,总而言之finall总会执行
强烈使用try/catch 和 try/finally语句块单独,而不是基本语法的用法。如下:
InputStream in= ... ;
try
{
try
{
code that might throw exceptions
}
finally
{
in.close();
}
}
catch(IOException e)
{
show error message
}
内层的try只有一个职责,确认关闭输入流。外层的try 用来确保报告出现的错误。
注意:当finally
和catch
包含return
语句,会执行finally
的return
。
注意:finally
也可能抛出异常,而导致本来要catch
抛出的异常被覆盖
假如资源实现了AutoCloseable
/Closeable
接口的类。可以利用带资源的try
语句
try(Resource res=...)
{
work with res
}
try 退出时,会自动调用res.close()。下面给出一个典型的例子。
try (Scanner in=new Scanner(new FileInputStream("/usr/share/dict/words")))
{
while (in.hasNext())
System.out.println(in.next());
}
还能指定多个资源,例如:
try (Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words")),
PrintWriter out=new PrintWriter("out.txt"))
{
while(in.hasNext())
out.println(in.next().toUpperCase());
}
如果.close()
也抛出了异常,但是不会覆盖原来该抛出的异常,而是被抑制,如果你想知道这些被抑制的异常,可以通过getSuppressed方法。
堆栈跟踪(stack trace
)是一个方法调用过程的列表。
比较灵活的方式是使用getStackTrace()
。它会得到StackTraceElement对象的一个数组。
例如:
Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for(StackTraceElement f : frames)
System.out.println(f);
输出
factorial(4):
StackTraceTest.factorial(StackTraceTest.java:8)
StackTraceTest.factorial(StackTraceTest.java:14)
StackTraceTest.factorial(StackTraceTest.java:14)
StackTraceTest.factorial(StackTraceTest.java:14)
StackTraceTest.factorial(StackTraceTest.java:14)
StackTraceTest.factorial(StackTrcaceTest.java:14)
StackTraceTest.main(StackTraceTest.java:23)
能获得文件名,类名,当前执行的代码行号
静态的Thread.getAllStackTrace方法,获得所有线程的堆栈跟踪。下面是例子
Map<Thread,StackTraceElement[]> map=Thread.getAllStackTraces();
for(Thread t : map.keySet())
{
StackTraceElement[] frames=map.get(t);
for(StackTraceElement f : frames)
System.out.println(f);
}
assert关键字有两个表达形式
assert 条件;
//为false ,抛出一个AssertionError异常
assert 条件:表达式;
//表达式传入异常作为一个消息字符串。
日志系统管理着一个名为Logger.global的默认日志记录器,可以用System.out替换它,并通过info方法记录日志信息
Logger.getGlobal().info("File->Open menu item selected");
//print
//三月 15, 2016 7:33:25 下午 log main
//信息: File->Open menu item selected
自动包含了时间,调用的类名和方法。
Logger.gelGlobal().setLevel(Level.OFF)
来取消所有日志
调用getLogger方法可以创建或检索记录器
Logger myLogger= Logger.getLogger("log.zhouyong");
如果对日志设置了日志级别,那么它的子记录器也会继承这个属性
有一下7个日志记录器级别
在默认情况,只记录前三个级别,也可以设置其他级别。例如:
logger.setLevel(Level.FINE)
现在,FINE和更高级别的记录都可以记录下来
另外可以使用Level.ALL 开启所有 Level.OFF 关闭所有
有以下几种记录方式
logger.warning(message);
logger.fine(message);
//同时还可以用log方法指定级别
logger.log(Level.FINE,message);
一般用CONFIG,FINE等记录有助于诊断,但对于程序员没有太大意义的调试信息。
默认的日志记录将显示包含日志调用类名和方法名。但是如果虚拟机进行了优化,可能无法得到准确的信息。此时需要logp方法获得调用类和方法的确切位置,签名如下:
void logp(level l,String className,String methodName,String message)
下面还有一些跟踪执行流的方法
void entering(String className,String methodName)
void entering(String className,String methodName,Object param)
void entering(String className,String methodName,Object[] params)
void exiting(String className,String methodName)
void exiting(String className,String methodName,Object result)
例如:
不知道有什么用。。
记录日志的常用用途是记录那些不可预料的异常。可以使用一下两种方式。
void throwing(String className,String methodName,Throwable t)
void log(Level l,String message ,Throwable t)
典型的用法是:
if(...)
{
IOExcption exception = new IOException("...");
logger.throwing("com.my","read",exception);
throw exception;
//FINER级别
}
还有
try
{
...
}
catch (IOException e)
{
Logger.getLogger("...").log(Level.WARNING,"Reading image",e);
}
默认情况,配置文件存在于:
e/lib/logging.properties
要想使用自己的配置文件 需要
java -Djava.util.logging.config.file=configFile MainClass
修改默认的日志记录级别
.level=INFO
还能修改自己日志记录级别
com.mycompany.myapp.level=FINE
控制台也有输出级别限制
java.util.logging.ConsoleHandler.level=FINE
日志属性由java.util.logging.LogManager类处理。具体看API
日志记录器先会将记录发送到父处理器中,最终的处理器有一个ConsoleHandle
。
对于一个要被记录的日志记录,它的日志记录级别必须高于日志记录器和处理器的阈值。
要想记录FINE级别的日志,就必须修改配置文件中的默认日志记录级别和处理器级别。
另外,还可以绕过配置文件,安装自己的处理器。//控制台处理器
Logger logger=Logger.getLogger("log.zhouyong");
logger.setLevel(Level.FINE);
logger.setUseParentHandlers(false);
Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler);
logger.log(Level.FINE,"dddd");
在默认情况,日志记录器会将记录发送给自己的处理器和父处理器。父处理器就是一般的默认处理器,但是既然我们有了自己的处理器,可以把父处理器关了。免得控制台发送了两次记录
要想将日志发送到别的地方,就需要其他处理器。
FileHandler
FileHandler handler = new FileHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler);
logger.log(Level.FINE,"dddd");
文件在User底下,格式为XML。如下:
<?xml version="1.0" encoding="GBK" standalone="no"?>
<!DOCTYPE log SYSTEM "logger.dtd">
<log>
<record>
<date>2016-03-15T23:40:17</date>
<millis>1458056417956</millis>
<sequence>0</sequence>
<logger>log.zhouyong</logger>
<level>FINE</level>
<class>log</class>
<method>main</method>
<thread>1</thread>
<message>dddd</message>
</record>
</log>
可以通过实现Filter
接口并定义下列方法来自定义过滤器
boolean isLoggable(LogRecord record)
setFilter
方法安装过滤器
ConsoleHandler类
和FileHandler
可以生成文本或 XML格式的日志记录。但是也可以自定义格式。
通过继承Formatter
类,并覆盖一下方法。
String format(LogRecord record)
可以根据自己意愿对记录的信息进行格式化,并返回结果字符串。
然后用setFormatter方法将格式化器安装到处理器中。