在开发任何Java应用的时候,我们都会谈及optimization
——优化的概念。作为一个开发者,我们应该尽可能地保证自己写的代码干净、没有缺陷,并且尽可能地考虑性能问题。
为此,笔者总结了以下11个你肯定会用到的Java代码性能优化的技巧。编程学习资料点击免费领取
我们在定义一个方式的时候,应该考虑到一个方法不应该太长,它就应该是专门是来执行单一功能的。这样其实对维护和性能都有好处。
一方面,从维护角度来说,适当长度的方法易读性更强,更容易理解;另一方面,在类的加载和方法调用的过程中,方法会被加载到内存中。如果一个方法太大,处理起来就需要消耗额外的内存和CPU周期。我们应该学会在恰当的逻辑点上将一个长方法拆开。
对于这个优化点,大家应该很熟悉了。但是实际在写代码的时候,还是if-else一撸到底。
这样做的话,其实也会影响性能。因为JVM必须对条件进行比较。如果在for、while等循环语句中使用同样的条件,情况会变得更糟糕。
如果我们的业务逻辑中有很多的条件,我们可以尝试着将这些条件分组并且返回一个布尔值,然后再将其用于if语句。
另外,如果可能的话,我们可以考虑使用switch语句来代替多个if-else。switch语句比if-else有性能优势。 下面我们看一个例子:
if (condition1) {
if (condition2) {
if (condition3 || condition4) { execute ..}
else { execute..}
复制代码
对比上面这段代码,合适的做法应该如下:
boolean result = (condition1 && condition2) && (condition3 || condition4)
复制代码
用Java5的foreach风格来写循环确实很方便很简洁,看起来就很酷!
但是有的时候耍酷是要付出性能的代价的。
例如:
for (String str: strs) {
. . .
}
复制代码
每次运行代码,如果strs是Iterable
的,你将会创建一个新的Iterator
对象。这样做会导致更多内存的消耗。
如果你对性能有着极致的追求
,那么还是建议你使用原始的写法:
int size = strs.size();
for (int i = 0; i < size; i++) {
String value = strs.get(i);
. . .
}
复制代码
在对任何集合进行迭代时,要事先得到集合的大小,而不是在迭代过程中得到它——这样避免多次调用size()方法。
下面请看这个例子:
List eleList = getData();
for (int i = 0; i < eleList.size(); i++) { execute code ..}
复制代码
对比上面这段代码,合适的做法应该如下:
List objList = getData();
int size = objList.size();
for (int i = 0; i < size; i++) { execute code ..}
复制代码
+
号拼接字符串从JDK5开始,Java编译器就做了优化,使用+
号拼接字符串,编译器编译后实际就自动优化为使用StringBuilder。
而且String是final类,用String创建的对象无法重复使用。因此,如果我们需要连续拼接,使用+
号拼接字符串将导致创建多个String对象,从而会占用更多的堆内存。
一般来说,当字符串不多的时候,+
号与StringBuilder的拼接效率其实相差无几;但是如果涉及到单线程循环拼接
的时候,我们最好还是使用StringBuilder以保证性能上的优化。
下面请看一个例子:
String str = "sample";
for (int i = 0; i < count; i++) {
str = str + "-" + i;
}
复制代码
更合适的做法如下:
StringBuilder stringBuilder = new StringBuilder("sample");
for (int i = 0; i < count; i++) {
stringBuilder.append("-");
stringBuilder.append(i);
}
复制代码
因为基本类型存储在栈内存中,而对象存储在堆内存中。如果可以的话,我们应该尽可能使用基本类型而非对象,因为栈内存的访问速度比堆内存快。
因此在某些情况下,定义一个变量或者数组,我们可以使用int而非Integer,double而非Double。
BigDecimal类提供了精确的小数值,过度使用这个对象会对性能造成影响,特别是当这个对象被用来在循环中计算某些数值时。
BigDecimal在进行计算时要比long或double占用更多的内存。如果精度不受限制,或者我们确认计算值的范围不会超过long或double,我们可以避免使用BigDecimal,而使用long或double,并进行适当的转换。
有一些类在应用程序中承载着数据,这些对象的创建开销很大,我们应该避免多次创建。
比如说,数据库连接对象,系统配置对象,或者是用户登录的会话对象。这些对象在创建的时候占用了大量资源,我们应该选择重用这些对象,而不是再次创建。
对于这些"代价昂贵"的对象,我们尽可能使用单例模式来创建单一实例,并在需要的地方重用它。
现在应该比较少用JDBC API进行SQL查询了,但是我觉得还是有必要了解一下。
对于参数化查询,PreparedStatement比Statement更有优势,因为PreparedStatement对象被编译一次并执行多次。Statement对象在每次被调用时都会被编译和执行。
此外,PreparedStatement对象是安全的,可以避免SQL注入攻击。
这个建议应该是很普遍的,但是很多代码忽略了这一点。我们在创建调试信息的时候,应该先检查一下当前的日志级别。
否则你可能会无意之间创建一条无用的日志信息。 请看例子:
log.debug("User [" + userName + "] called method X with [" + i + "]");
log.debug(String.format("User [%s] called method X with [%d]", userName, i));
复制代码
在这种情况下,我们需要执行所有必要的步骤去创建日志信息,而不知道程序到底会不会使用该日志信息。
更进一步,如果说这种日志信息的创建涉及到更多资源的占用呢?
所以最好是先检查一下当前的日志级别,请看:
if(log.isDebugEnabled()) {
log.debug("User [" + userName + " ] called method X with [" + i + "] ");
}
复制代码
有时,我们需要写SQL来获取数据。此时我们应该避免选择所有数据库列,只选择我们需要的数据库列。
选择太多的列会导致数据库查询执行的延迟,也会增加网络流量。
请看示例:
select * from books where book_id = 6;
复制代码
对此,我建议这么写:
select book_title, book_desc, book_price from books where book_id = 6;
复制代码
很多人认为性能优化是一个复杂的话题,需要大量的经验和知识,这在一定程度上是对的。
我们开发一个应用程序并且期望获得尽可能好的性能并不是一件容易的事情。但是,即使你不是性能调优专家,也可以采取一些简单的方法来提高性能。