异常分类
Throwable
Error
Exception
IOException
Runtime Exception
Error:Java运行时系统的内部错误以及资源耗尽的错误。程序中基本不用处理,如果出现了该类错误,告知用户并安全退出即可。
RuntimeException:程序编写中的错误,不需要try…catch捕获,修改程序即可避免的错误。也叫uncheckedException,所谓的unchecked是指编译过程中,编译器无法检测出来的错误。比如:数组越界、访问空指针。
IOException:类中抛出的、需要try…catch捕获的大多是这类异常。对应的也成为checkedException,编译器会强制检查是否处理的异常。
抛出异常
声明受查异常:在方法签名后,用关键字throws接异常类名。
抛出异常:在方法代码中,用关键字throw接异常对象。
自定义异常类:创建一个继承自Exception或Exception子类的自定义类,规范化的包含两个构造器,一个无参无内容构造器,一个含参数调用super(String)构造器。
public class Main {
public class myException extends Exception {
public myException ( ) { }
public myException ( Integer i, String s) {
super ( "文件名长" + String . valueOf ( i) + " 文件名为" + s + " WEONG!" ) ;
}
}
public void myPrint ( String filename) throws myException {
if ( filename. length ( ) > 5 ) {
throw new myException ( filename. length ( ) , filename) ;
}
return ;
}
public static void main ( String [ ] args) {
Main m = new Main ( ) ;
try {
m. myPrint ( "abcdef" ) ;
} catch ( myException e) {
System . out. println ( e. getMessage ( ) ) ;
}
}
}
捕获异常
public static void main ( String [ ] args) {
File f = new File ( "a.txt" ) ;
try {
FileInputStream fin = new FileInputStream ( f) ;
} catch ( FileNotFoundException e) {
e. printStackTrace ( ) ;
}
}
两种执行结果: try块中没有异常,跳过catch块; try块中遇到异常,跳过剩余try块语句,执行catch块。
应用程序中,不可避免的会用到文件操作IO流或者是数据库连接,诸如此类开销比较大的资源。这个时候就需要在使用完后,手动进行资源的及时销毁,避免内存泄露。try…catch有三种写法可以完成上述功能。
public static void main ( String [ ] args) {
File f = new File ( "a.txt" ) ;
try {
FileInputStream fin = new FileInputStream ( f) ;
fin. close ( ) ;
} catch ( FileNotFoundException e) {
e. printStackTrace ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
但如果在对象创建之后,遇到了异常跳入某个catch块中,这样close的代码就执行不到。由此引出了finally块,即无论try…catch如何执行,最后一定会执行finally块。
public static void main ( String [ ] args) {
File f = new File ( "a.txt" ) ;
FileInputStream fin = null ;
try {
fin = new FileInputStream ( f) ;
} catch ( FileNotFoundException e) {
e. printStackTrace ( ) ;
} finally {
try {
fin. close ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
试想如果变量在创建的时候就报异常,导致变量没有创建仍是空值,就会在finally块中报空值引用错误。而且这种写法,在finally块中遇到异常时,还需要再使用一次try…catch,略显臃肿。
public static void main ( String [ ] args) {
File f = new File ( "a.txt" ) ;
try ( FileInputStream fin = new FileInputStream ( f) ) {
. . .
} catch ( FileNotFoundException e) {
e. printStackTrace ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
try-with-resources写法的原理,会在try块退出的时候执行一次资源关闭。实测不会出现空指针情况,而且写法简洁,最为推荐。
异常链:在实际的开发背景中,还会在catch块中再次抛出异常。用来改变异常类型,或者使用自定的详述的异常类型。也可以用Java提供的initCause(e)和getCause(),来构建一个因果的异常链。
堆栈轨迹元素:常用的e.printStackTrace()方法打印方法调用过程,也可以使用e.getStackTrace()返回一个StackTraceElement[]数组,用来逐个分析异常文件名和代码行数。
断言
断言是在开发测试阶段使用,用来确定每一部分的前置条件是否满足,并以此来确定程序错误出现的区间,以便快速定位BUG。相较于异常的优势在于,可以在运行的时候选择是否执行断言,并且不用重新编译。
public static void main ( String [ ] args) {
File f = null ;
assert f != null : f;
System . out. println ( f. getName ( ) ) ;
}
assert有两种形式: assert 条件; assert 条件 : 表达式;
Exception in thread "main" java. lang. AssertionError: null
at Main . main ( Main . java: 23 )
启用断言:在IDEA中修改运行配置,加入-ea的参数即可。我开始还以为在程序参数中加,但是发现程序参数是主函数的args形参。应该调出VM参数,加入-ea。同时可以用-ea:或者-da:启用或抑制某个包中的断言。还有就是在程序中使用ClassLoader类加载器的时候,手动启用或是移除断言。
记录日志
在JDK1.3以前,日志都是直接print到STDOUT流中。后来出现了log4j,以及性能更优的slf4j框架。
基本日志:日志记录器有7个级别,分别为:SEVERE>WARNING>INFO>CONFIG>FINE>FINER>FINEST。
public static void main ( String [ ] args) {
int a = 1 ;
Logger . getGlobal ( ) . info ( "赋值a为1。" ) ;
}
高级日志:也就是使用自定义的日志记录器。还可以有不同类型的日志,比如entering函数步入以及exiting函数退出。
public class Main {
private static final Logger mylog = Logger . getLogger ( "com.mylog" ) ;
public static void main ( String [ ] args) {
int a = 1 ;
mylog. warning ( "a赋值为1" ) ;
}
}
public class Main {
private static final Logger mylog = Logger . getLogger ( "com.mylog" ) ;
public static void main ( String [ ] args) {
mylog. setLevel ( Level . FINER) ;
mylog. setUseParentHandlers ( false ) ;
Handler handler = new ConsoleHandler ( ) ;
handler. setLevel ( Level . FINER) ;
mylog. addHandler ( handler) ;
mylog. entering ( "com" , "main" ) ;
int a = 1 ;
mylog. exiting ( "com" , "read" , a) ;
}
}
处理器:日志记录器会将记录发送到日志处理器中,由处理器决定如何输出日志。默认情况下,会发送到ConsoleHandler中,输出到System.err中。(关于System.err与System.out相似,为标准错误输出,体现在编译器中就是输出内容为红色)值得注意的是,自定义处理器时,会默认将日志再传一份给命名为“”的父处理器,这也是为什么要设置setUseParentHandlers为false,目的是只有一遍输出即可。记录器有记录级别,同时处理器也有处理级别,所以能显示的日志为两者的最小阈值。类似的,还可以将日志输出到文件或者流数据中,分别对应了FileHandler和StreamHandler。
public class Main {
private static final Logger mylog = Logger . getLogger ( "com.mylog" ) ;
public static void main ( String [ ] args) {
try {
Handler handler = new FileHandler ( "src/1.log" ) ;
mylog. addHandler ( handler) ;
mylog. setLevel ( Level . WARNING) ;
mylog. warning ( "BUG!!" ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
文件处理器配置参数(java.util.logging.Handler) 作用 append 追加文件末尾或是每个程序打开一个文件 limit 文件最大允许大小,超过则新建 pattern 文件路径以及文件名模式(如%g循环数值) count 文件名循环数(1不循环)
过滤器:每个记录器和处理器都可以有一个过滤器,默认情况下是根据日志的级别过滤。如果想要自定义的话,可以实现Filter接口,重写isLoggable方法,使用setFilter绑定。
格式化器:同样的,自定义时需要实现Formatter接口,用setFormatter绑定。
修改配置文件:文件路径在jdk/conf/logging.properties中,修改相应字段,程序中的修改优先级更高。可以指定自定义部件属性值,也可以直接修改全局值。
try {
Properties properties = new Properties ( ) ;
FileInputStream fileInputStream = new FileInputStream ( "1.txt" ) ;
properties. load ( fileInputStream) ;
String name = properties. getProperty ( "name" ) ;
System . out. println ( name) ;
} catch ( Exception e) {
e. printStackTrace ( ) ;
}
本地化:使用logmessages资源包,显示信息必须用包中定义的,可以完成不同语言的映射,适用于全球化编程。
本文中的日志均使用的是java.util.logging自带的框架,可以使用功能更为强大的log4j或者是commons-logging。
调试技巧
日志代理:new对象的时候,使用匿名类重写要使用的方法,调用super方法,加入日志信息。
-verbose启动VM,可以看到类的加载路径。-Xlint对常见错误的检查。
Windows下打开bin下的jconsole监控内存、线程、类加载等情况。
jmap工具查看堆中变量值。(使用jps命令查看运行的所有java程序及其虚拟标识符)
PS
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)