don't make me think

使用自定义异常类

继承excepiton或者RuntimeException的方式来实现自定义异常类,以便更加好地控制自定义异常,例如:通过异常编码和编码描述等属性来描述项目的自定义异常,而通过自定义异常类就能很好地管理项目的异常。

public final class CRMException extends RuntimeException {
    private String         expCode;// 异常错误编码
    private String         expDesc;// 异常描述
    //其它省略
}
//例如:如果有个自定义异常是描述查询数据为空的时候,则expCode:BA1000 , expDesc:CRM查询不存在数据!

不要将异常直接打印在客户端或页面上

一旦出现异常,我们只要将异常的错误编码呈现给用户,这样用户可以把错误编码交给开发者或管理员去处理。或者将错误代码转换成更通俗易懂的提示返回给用户。

不要利用 Exception 捕捉所有异常

只用Exception来捕获异常虽然不会出错,但却无法对特定的异常进行处理。

public void retrieveObjectById(Long id){
    try{
          ... ...
    }catch(IOException e){
        //仅仅捕捉 IOException
        throw new RuntimeException(IOException in retieveObjectById, e);
    }catch(SQLException e){
        //仅仅捕捉 SQLException
        throw new RuntimeException(SQLException in retieveObjectById, e);
    }catch(Exception e){
        //最后一个使用Exception来处理未预料到的其它异常
        throw new RuntimeException(other Exception in retieveObjectById, e);
    }
}

异常应该尽早抛出(迅速失败)

通过在检测到错误时应该立刻抛出异常来实现迅速失败,可以有效继续执行无用的操作,减少了不必要的对象构建和资源占用。

延迟捕获

在迅速失败的基础上,如果希望程序能从异常中恢复而不是中断时,那么应该抛出异常后交给上级去捕获处理,并提供用户明确信息来引导他们从错误中恢复过来。

提供精确的异常的信息

例如:以下对比IllegalArgumentException 异常的两条异常信息:

消息 1: “Incorrect argument for method”

消息 2: “{expCode}:Illegal value for ${argument}=${value}

第一条消息仅说明了参数是非法的或者不正确,但第二条消息包括了参数名和非法值,而这对于找到错误的原因是很重要的。

正确处理异常

  • 如果需要对异常进行处理,使用throw抛出异常中断程序,而不是使用e.printStackTrace()将异常输出到控制台。而且最好使用e.printStackTrace()打印异常堆栈信息在日志中而不是控制台

  • 对可恢复的情况使用检查型异常,对编程错误使用运行时异常。

  • 不要出现空的catch块

  • 异常尽量不要包含在 for 循环语句块中,因为异常处理会占用系统资源,让你的代码运行缓慢。

  • 为可恢复的错误使用检查型异常,如:可以通过重新询问用户文件名来处理FileNotException;为编程错误使用非检查型错误;为虚拟机的错误使用Error

参考资料

1.有效处理java异常三原则

don't make me think

网站可用性定律

可用性第一定律:别让我思考。一个页面应该一目了然、自我解释

可用性第二定律:点击多少次都没关系,只要每次点击都是无需思考、明确无误的选择。三次无须思考、明确无误的点击相当于一次需要思考的点击。

可用性第三定律:省略不必要的文字,让有用的内容更加突出。

网站使用的三个事实

1.不是阅读而是扫描,只寻找一小部分感兴趣的内容;

2.不作最佳选择,而是满意即可;

3.不追根究底,勉强应付;

4.广告牌设计101法则:为扫描设计,不为阅读设计。

帮助用户快速理解网站的要点

1.在每个页面建立清楚的视觉层次。越重要的部分越突出,逻辑上相关的部分在视觉上也相关(类似或者同一区域),逻辑上包含的部分在视觉上进行嵌套;

2.尽量利用习惯用法(让用户访问更容易 ,保证熟悉感);

3.把页面划分成明确定义的区域 4明显标示可以点击的地方;

4.最大限度降低干扰(避免眼花缭乱和背景噪声);

5.比起添加注释,往往正确的解决方案是拿走一些让人混淆的东西,而不是增加另一些干扰不要太看重人们对新功能的要求。

几件让用户讨厌的事

1.隐藏用户想要的信息,如:隐藏客户服务的电话号码、运费和价格;

2.因为没有按照要求的方式行事而让用户觉得痛苦,如:信用卡号码中间是否要加入空格之类的;

3.向用户询问不必要的超出当前任务的信息,如:向用户询问超出实际需要的更多个人信息且没有提供给用户回报;

4.敷衍和欺骗用户,虚伪的真诚和假意的关心;

5.给用户设置障碍,不得不等待或者浏览多个图片等;

6.网站看上去不专业。

提高用户好感的方式

1.知道人们在网站上想做什么,并让他们明白简易;

2.告诉用户她想知道的。知道用户可能有哪些疑问并且给予解答。为用户提供协助,例如打印友好的页面;

3.尽量减少步骤,让用户看得出花了心思,如:给用户邮件发送的是一个跟踪货物的链接而不是货物的跟踪码;

4.容易从错误中回复。如果不确定或没有能力做到用户想要的,就道歉,至少让他们明白你知道你再给他们造成不便。

提高网站可访问性的技巧

1.为每张图片增加合适的alt文本

2.页面使用合适的标题

3.让你的表单配合屏幕阅读器

4.在每页的最前面增加一个“跳转到主要内容”的链接

5.让所有内容都可以通过键盘访问

6.在文本和他们的背景之间设置明显的对比

7.采用一份可访问性良好的模板

网站的关键要素

站点ID 、页面名称、栏目(主导航)、页内导航、 指示器(用户在导航系统的什么位置)、搜索

关于网站的几个重要模块

主页:能传达给用户整体形象,在一眼之内正确无误地回答首次到访者的问题:这是什么网站、网站上有什么、我能在这里做什么、为什么我应该在这里而不是在别的地方 网站口号:有效的信息传达方式,能表达出网站特点

下拉框:不要问:“大部分人喜欢下拉框吗”,正确的问题是“在这个页面,这样的上下文中,这个下拉框以及这些下拉项目和措辞会让可能使用这个网站的大部分人产生一种良好的体验吗?”

导航: 帮助用户找到想要的东西,告诉用户现在身处何处。持久导航在网站的任何地方都看得到这四个元素: 站点ID、实用工具 (sign in、 contact…)、 栏目( products、news、support、about…)、搜索

层级菜单:会告诉用户从主页到当前位置的轨迹,并能让用户在网站中更加容易的回到更高层次的内容。使用 > 对层级进行间隔。加粗最后一个元素,这是你当前的位置

网站测试的作用

争辩人们喜欢什么,既浪费时间又消耗团队精力。而测试通过将讨论对错和个人喜好转移到什么有效,什么无效上。更容易缓和争论,打破僵局。而且测试会让我们看到用户的动机,理解和反应的差异,从而让我们不会坚持认为用户的想法和我们的想法一样。

测试中遇到的典型问题

1.用户不清楚概念

2.他们找不到自己要找的字眼

3.内容太多了

SLF4J不同于其他日志类库,与其它有很大的不同。SLF4J(Simple logging Facade for Java)不是一个真正的日志实现,而是一个抽象层,它允许你在后台使用任意一个日志类库。如果是在编写供内外部都可以使用的API或者通用类库,那么你真不会希望使用你类库的客户端必须使用你选择的日志类库。

如果一个项目已经使用了log4j,而你加载了一个类库,比方说 Apache Active MQ——它依赖于于另外一个日志类库logback,那么你就需要把它也加载进去。但如果Apache Active MQ使用了SLF4J,你可以继续使用你的日志类库而无语忍受加载和维护一个新的日志框架的痛苦。

总的来说,SLF4J使你的代码独立于任意一个特定的日志API,这是一个对于开发API的开发者很好的思想。虽然抽象日志类库的思想已经不是新鲜的事物而且Apache commons logging也已经在使用这种思想了,但现在SLF4J正迅速成为Java世界的日志标准。让我们再看看几个使用SLF4J而不是log4j、logback或者java.util.logging的理由。

SLF4J对比Log4J,logback和java.util.Logging的优势

正如我之前说的,在你的代码中使用SLF4J写日志语句的主要出发点是使得你的程序独立于任意特定的日志类库,依赖于特定类可能需要不同与你已有的配置,并且导致更多维护的麻烦。但除此之外,还要一个SLF4J API的特性使得我坚持使用SLF4J而抛弃我长期间钟爱的Lof4j的理由,是被称为占位符(place holder),在代码中表示为“{}”的特性。占位符是一个非常类似于在String的format()方法中的%s,因为它会在运行时被某个提供的实际字符串所替换。这不仅降低了你代码中字符串连接次数,而且还节省了新建的String对象。即使你可能没需要那些对象,但这个依旧成立,取决于你的生产环境的日志级别,例如在DEBUG或者INFO级别的字符串连接。因为String对象是不可修改的并且它们建立在一个String池中,它们消耗堆内存( heap memory)而且大多数时间他们是不被需要的,例如当你的应用程序在生产环境以ERROR级别运行时候,一个String使用在DEBUG语句就是不被需要的。通过使用SLF4J,你可以在运行时延迟字符串的建立,这意味着只有需要的String对象才被建立。而如果你已经使用log4j,那么你已经对于在if条件中使用debug语句这种变通方案十分熟悉了,但SLF4J的占位符就比这个好用得多。

这是你在Log4j中使用的方案,但肯定这一点都不有趣并且降低了代码可读性因为增加了不必要的繁琐重复代码(boiler-plate code):

if (logger.isDebugEnabled()) {//logger是log4j的Logger
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}

另一方面,如果你使用SLF4J的话,你可以得到在极简洁的格式的结果,就像以下展示的一样:

logger.debug("Processing trade with id: {} and symbol : {} ", id, symbol);//logger是slf4j的Logger

在SLF4J,我们不需要字符串连接而且不会导致暂时不需要的字符串消耗。取而代之的,我们在一个以占位符和以参数传递实际值的模板格式下写日志信息。你可能会在想万一我有很个参数怎么办?嗯,那么你可以选择使用变量参数版本的日志方法或者用以Object数组传递。这是一个相当的方便和高效方法的打日志方法。记住,在生产最终日志信息的字符串之前,这个方法会检查一个特定的日志级别是不是打开了,这不仅降低了内存消耗而且预先降低了CPU去处理字符串连接命令的时间。这里是使用SLF4J日志方法的代码,来自于slf4j-log4j12-1.6.1.jar中的Log4j的适配器类Log4jLoggerAdapter。

public void debug(String format, Object arg1, Object arg2) {
    if (logger.isDebugEnabled()) {
        FormattingTuple ft = MessageFormatter.format(format, arg1, arg2);
        logger.log(FQCN, Level.DEBUG, ft.getMessage(), ft.getThrowable());
    }
}

同时,我们也很值得知道打日志是对应用程序的性能有着很大影响的,在生产环节上只进行必要的日志记录是我们所建议的。

怎么用SLF4J做Log4J的日志记录

除了以上好处,我想还有一个告诫,就是为了使用SLF4J,你不仅需要包含SLF4J的API jar包,例如 slf4j-api-1.6.1.jar,还需要相关Jar包,这取决于你在后台使用的日志类库。如果你想要使用和Log4J 一起使用SLF4J ,Simple Logging Facade for Java,,你需要包含以下的Jar包在你的classpath中,取决于哪个SLF4J和你在使用的Log4J的版本。例如:

  • slf4j-api-1.6.1.jar – JAR for SLF4J API
  • log4j-1.2.16.jar – JAR for Log4J API
  • slf4j-log4j12-1.6.1.jar – Log4J Adapter for SLF4J

还有,如果你对于使用变量参数版本的日志方法感兴趣的话,那么就导入SLF4J 1.7的版本吧。

总结

总结这次说的,我建议使用SLF4J的而不是直接使用 Log4j, commons logging, logback 或者 java.util.logging 已经足够充分了。

  1. 在你的开源或内部类库中使用SLF4J会使得它独立于任何一个特定的日志实现,这意味着不需要管理多个日志配置或者多个日志类库,你的客户端会很感激这点。
  2. SLF4J提供了基于占位符的日志方法,这通过去除检查isDebugEnabled(), isInfoEnabled()等等,提高了代码可读性。
  3. 通过使用SLF4J的日志方法,你可以延迟构建日志信息(Srting)的开销,直到你真正需要,这对于内存和CPU都是高效的。
  4. 作为附注,更少的暂时的字符串意味着垃圾回收器(Garbage Collector)需要做更好的工作,这意味着你的应用程序有为更好的吞吐量和性能。
  5. 这些好处只是冰山一角,你将在开始使用SL4J和阅读其中代码的时候知道更多的好处。我强烈建议,任何一个新的Java程序员,都应该使用SLF4J做日志而不是使用包括Log4J在内的其他日志API。

原文链接: javarevisited 翻译: ImportNew.com - Jaskey 译文链接: http://www.importnew.com/7450.html

提升

Log4j1.x已经被广泛应用到各个系统及框架中。然后,1.x毕竟太古老,代码很久没有更新。目前,Log4j 1.x的代码已经很难维护,因为它依赖于很多Jdk老版本的api。而Log4j2在1.x上做了很大提升,如:

  • Log4j 2使用了新一代的基于LMAX Disruptor的无锁异步日志系统。在多线程的程序中,异步日志系统吞吐量比Log4j 1.x和logback高10倍,而时间延迟却更低。

  • Log4j 2使用插件机制,更灵活。扩展appenders,Filters,Layouts,Lookups和Pattern Converters将变得更加简单,而不用去更改任何Log4j本身。

  • Log4j 2在底层尽可能使用了Java5提供的对并发及锁支持的工具类。Lo4j 1.x有死锁的bug。Logback中修复了log4j 1.x的很多bug,但是,logback中的有很多类采用同步机制(这种机制导致性能下降)

  • 提供了和slf4j相同的支持变量参数的占位符功能

logger.debug("Hi, {} {}", u.getA(), u.getB());
  • log4j 2的配置变得非常简单。支持xml和json方式配置

所以建议未使用或已使用其它日志技术的可以根据项目的需要考虑替换为Log4j2。