断言
在Java语言中,给出了3种处理系统错误的机制:
那我们应该在什么情况下去使用断言呢?
不应该使用断言向程序的其他部分通告发生了可恢复性的错误,或者,不应该作为程序向用户通告问题的手段,断言只应该用于在测试阶段确定程序内部的错误信息。
在一个具有自我保护能力的程序中,断言很常用,假如确信某个属性符合要求,并且代码的执行非常的依赖这个属性,比如:
double a = Math.sqrt(x);
我们在这里确信x必须是一个正值,因为它是另一个计算的得出的非负结果,或者是某一个方法的参数,而这个方法要求它的调用者只能提供一个正整数。但是为了以防万一,我们还是会对这个参数进行检查:
if(x < 0) {
throw new IllegalArgumentException("x < 0")
;}
但是,有一个问题就是,这段代码会一直保留在程序中,即使测试完毕也不会自动的进行删除,如果程序中含有大量的这样的检查,会严重的影响程序的运行速度。
而断言机制允许在测试期间向代码中插入一些检查语句。当代吗发布的时候,这些插入的检测语句将会自动地移走。
在Java中,断言有两种语法形式:
assert 条件;
assert 条件:表达式;
这两种形式都会对条件进行检测,如果结果为false,就会抛出一个AssertionError异常。在第二种形式中,表达式将会传入AssertionError的构造器,并转换成一个消息字符串。
在上述的程序中,如果我们想使用断言:
assert x >= 0;
//或者将x的实际值传给AssertionError对象
assert x >= : x;
但是在默认情况下,断言是被禁用的,我们可以通过在运行程序的时候输入参数来选择启用:
java -ea MyApp
//or
java -enableassertions MyApp
启动和禁用断言的时候不用重新编译程序,它是类加载器的功能,当断言被禁用的时候,类加载器将会跳过断言代码,所以,不会降低程序的运行速度。
同样的,我们也可以在某个类或整个包中使用断言,比如:
java - ea:MyClass -ea:com.viyoung... MyApp
这个命令将会开启MyClass类以及在com.viyoung包和它的子包中的所有类的断言。 选项 -ea 将会开启默认包中所有类的断言。 也可以使用选项 -disableassertions 或 -da 禁用某个特定类或包的断言:
java -ea: ... -da:MyClass MyApp
有些类不是由类加载器加载,而是直接由虚拟机加载。可以使用这些开关有选择的启用或禁用那些类的断言。
然而,启用和禁用所有断言的 -ea 和 -da 开关不能应用到那些没有类加载器的“系统类”上,对于这些系统类来说,需要使用 -enablesystemassertions/-esa 开关启用断言。
断言和日志的区别在于,断言是一种测试和调试阶段使用的战术性工具;而日志记录是一种在程序的整个生命周期都可以使用的策略性工具。
记录日志
说起日志,大家可能会有点陌生,尤其是刚刚接触Java不久的初级程序员,我们在学习初期进行调试程序的时候回插入一些System.out.println
方法来帮助我们对程序的运行状况进行一个把控和分析,但是如果说,我们解决了这个问题,就需要把这些语句从我们的代码中及时的删除,当遇到其他问题的时候,则需要再次添加,然后解决后再删除,Java中内置了一个包叫做:java.util.logging
包中,在这个包中提供了一系列的API来解决我们的痛点,这些API的优点有许多:
基础日志
如果只是想生成一个简单的日志记录,可以使用全局日志记录器(global logger)并调用其info方法:
Logger.getGloabal().info("This is a Logger Info");
他会在控制台上打印出:
INFO:This is a Logger Info
如果在适当的地方调用
Logger.getGlobal().setLevel(Level.OFF)
会取消所有的日志。
高级日志
上面的日志在我们日常的开发中是不常见的,在一个专业的应用程序中,不要讲所有的日志都记录到一个全局日志记录器中,而是可以自定义日志记录器。
可以调用Logger
类的getLogger()
方法获取记录器:
private static final Logger myLogger = Logger.getLogger("com.viyoung.myapp");
未被任何变量引用的日志记录器都可能会被垃圾回收,为了防止这种情况的发生,所以要用一个静态变量存储日志记录器的一个引用。
与包名类似,日志记录器名也具有层次结构,而且与包名相比,日志记录器的层次结构更强,如果你对某个包设置了日志级别,那么它的子记录器会去继承这个级别。
通常来说,存在以下7个日志记录器级别:
通常来说,只会记录前三个级别,但是也可以设置其他的级别。比如:
logger.setLevel(Level.FINE);
当然,我们还可以使用Level.ALL开启所有级别的记录,或者使用Level.OFF关闭所有级别的记录。
对于所有级别有下面几种记录方法:
logger.warning(message);logger.fine(message);
同时,还可以使用log方法指定级别,例如:
logger.log(Level.FINE, message);
默认的日志配置记录了INFO或更高级别的所有记录,因此,应该使用CONFIG、FINE、FINER和FINESET级别来记录那些有助于诊断,但对于程序员又没有太大意义的调试信息。
默认的日志记录将显示包含日志调用的类名和方法名,如同堆栈所显示的那样,但是如果虚拟机对执行过程进行了优化,就会导致获取不到准确的调用信息,这时我们可以使用logp
方法获得调用类和方法的确切位置:
void logp(Level l, String className, String MethodName, String message);
以及有一些用来追踪执行流的方法:
// 记录一个方法条目void entering(String className, String methodName);//记录一个方法条目,带有一个参数。void entering(String sourceClass, String sourceMethod, Object param1) // 记录一个方法条目,带有一组参数。void entering(String sourceClass, String sourceMethod, Object[] params) //记录一个方法返回。void exiting(String sourceClass, String sourceMethod) // 记录一个方法返回,带有结果对象。 void exiting(String sourceClass, String sourceMethod, Object result)
记录日志的常见用途是记录那些不可预测的异常,可以使用下面的两个方法提供日志记录中包含的异常描述内容:
//正抛出异常的记录。
void throwing(String sourceClass, String sourceMethod, Throwable thrown) //记录带有相关的可抛出信息的消息。void log(Level level, String msg, Throwable thrown)
下节预告
Java自带日志框架的深入了解和应用~