Effective Java读书笔记十九(Java Tips.Day.19)

TIP 51 当心字符串连接的性能

不要对字符串用+连接,这个大家都懂的,String是不可变对象,每次连接都会额外创建一个对象,造成不必要的开销。如果连接数量很多,性能就会显著下降。

请使用StringBuffer或StringBuilder来处理这类需求。

如果在多线程并发的环境下,请使用StringBuffer类,因为它是线程安全的。如果你能确定运行环境是单线程,可以使用StringBuilder。


TIP 52 通过接口引用对象

如果有合适的接口类型存在,那么对于参数、返回值、变量和域来说,就都应该使用接口类型进行声明。

如果没有合适的接口,那么使用合适的父类(往往是抽象类)来进行声明。

只有当真正需要利用构造器创建具体的对象时,才真正需要引用这个对象的类。这也是我们一直强调的,面向接口编程。


TIP 53 接口优先于反射

反射机制非常强大,给定一个Class实例,可以获得所有构造器、方法和域实例,不仅可以获取所有域的值,还可以使用Method.invoke调用所有的方法。

然而,反射也有其缺点:

  • 丧失了编译时类型检查的好处,包括异常检查。如果尝试用反射调用根本不存在的方法,在运行时将会失败,但在编译时完全可以通过。
  • 执行反射访问所需要的代码非常笨拙、冗长。使用过反射的朋友都会有体会。
  • 性能损失。相比普通的方法调用,反射调用方法会慢上许多许多。

通常,普通应用程序在运行时不应该以反射方式访问对象。


TIP 54 谨慎使用本地方法

Java程序可以使用JNI来调用本地方法。

在Android 应用/游戏开发中,JNI的使用非常普遍,比如使用cocos2dx游戏引擎开发的游戏,所有的游戏内容都是在本地代码中实现(C++/Lua)。

然而,在更广泛的领域,使用本地方法来提高性能的方法不值得提倡。

使用本地方法的缺点:

  • 本地语言不是安全的,比如C++,所以使用本地方法的应用程序也可能会受到内存毁坏错误的影响。
  • 因为本地语言是与平台相关的,一旦使用,应用程序也不再是可以自由移植的。
  • 更难调试。在进入和退出本地代码时,需要相关的固定开销。
  • 更难维护。在需要对接Java与本地代码的部分,非常麻烦而且容易出错。

TIP 55 谨慎地进行优化

有三条与优化相关的格言:

William A. Wulf[Wulf72]:

很多计算上的过失都被归咎于效率(没有必要达到的效率),而不是任何其它的原因——-甚至包括盲目的做傻事。

More computing sins are committed in the name of efficiency (without necessarily
achieving it) than for any other single reason—including blind stupidity.

Donald E.Knuth[Knuth74]:

不要去计较效率上的一些小小的损失,在97%的情况下,不成熟的优化才是一切问题的根源。

We should forget about small efficiencies, say about 97% of the time: premature
optimization is the root of all evil.

M. A .Jackson[Jackson75]:

在优化方面,我们应该遵循两条规则:
规则1:不要优化
规则2(仅针对专家):还是不要进行优化——也就是说,在你还没有绝对清晰的未优化方案之前,请不要进行优化。

We follow two rules in the matter of optimization:
Rule 1. Don’t do it.
Rule 2 (for experts only). Don’t do it yet—that is, not until you have a
perfectly clear and unoptimized solution.

在第三条的规则2中,我差点以为中文版翻译错误,所以干脆找到英文版,然后把原文贴上来。然后发现,翻译的并没有错误:在你还没有绝对清晰的未优化方案之前,请不要进行优化。


这些格言比Java早出现了20年,它们讲述了关于优化的深刻真理:优化的弊大于利,特别是不成熟的优化。在优化的过程中,产生的软件可能既不快速、也不正确,而且难以修正。

  • 不要因为性能而牺牲合理的结构,要努力编写好的程序,而不是快的程序。因为性能问题可以后期优化,而重构一个糟糕的结构,往往是个噩梦。
  • 努力避免那些限制性能的设计决策。当一个系统设计完成后,最难以更改的组件是那些指定了模块之间的交互关系、以及模块与外界交互关系的组件,包括API、线路层(wire-level)、以及永久数据格式。它们在后期难以改变,而且很可能成为拖慢系统性能的罪魁祸首。
  • 要考虑API设计决策的性能后果。比如,使公有类成为可变的,可能会导致大量的保护性拷贝(TIP 39)。另外可以参考TIP 16 和TIP 52。
  • 一旦实现了一个清晰、简明、结构良好的实现,那么就到了考虑优化的时刻,如果你对程序的性能还不满意。
  • 但是,在每次试图做优化之前和优化之后,要对性能进行测量和对比,然后确定真正需要优化的部分。很多人认为,程序把80%的时间花在20%的代码上了,所以,首先要定位到这20%的代码。
  • 如果可能,请使用专门的性能测试工具。

总之,不要费力去编写快速的程序,应该努力地编写好的程序, 速度自然会随之而来。


TIP 56 遵守普遍接受的命名惯例

Java有一整套很好的命名惯例,其中有很多命名惯例包含在了 《The Java Language Specification》中。
一般来说,命名惯例分为两大类:字面的(typographical)和语法的(grammatical)。

下面是一些字面惯例的例子:

Package —— com.google.inject, org.joda.time.format
Class or Interface ——Timer, FutureTask, LinkedHashMap, HttpServlet
Method or Field —— remove, ensureCapacity, getCrc
Constant Field —— MIN_VALUE, NEGATIVE_INFINITY
Local Variable —— i, xref, houseNumber
Type Parameter —— T, E, K, V, X, T1, T2

更多的规范,大家可以阅读《The Java Language Specification》, 另外,阿里编程规范也值得参考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值