提高java代码执行效率的技巧

        对于细节问题我一直认识不怎么深刻,改变我这个看法的是很多年前玩dnf的时候,辛辛苦苦刷来一身的史诗装备,最后却发现打出的伤害并没有想象中那么令人满意,甚至令人感到有点沮丧。后来舍友告诉我,你要注意细节打造,各方面细节都关注起来,强化附魔徽章该搞的都搞起来,结果自然是好的,收效很不错。对于代码优化这块也是一样。然而很多人会说,这种小问题有必要吗?你要知道小虾米吃的多了鲸鱼也是能喂饱的!

        一个个小细节优化起来,对于代码的维护以及运行效率绝对是大大有益处的。

代码优化细节

1.尽量指定类、方法的final修饰符

   带有final修饰符的类是不可派生的。在java核心API中,有多应用的例子,例如java.lang.String,整个类都是final。为类指定final修饰符让类可以不被继承,为方法指定final修饰符了可以让方法不被重写。如果指定了一个类为final,则该类的所有方法都是final的。java编译器会寻找机会内联所有的final 方法,内联对于提升java运行效率作用重大,具体参加java运行期优化。此举能使性能提升平均百分之五十以上。

2.尽量重用对象

    特别是String对象的使用,出现字符串连接时应该使用StringBuilder/StringVuffer代替。由于java虚拟机不仅要花时间生成对象,以后可能还要花更多时间对这些对象和垃圾进行回收和处理,因此生成过多对象将会给程序的性能带来很大影响。

3.尽量可能使用局部变量

     调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快,其他变量,如静态变量、实例变量等都在堆中创建,速度较慢。另外栈中创建的变量,随着方法的运行结束,这些内容就没了,不需要额外的垃圾回收。

4.及时关闭流

     java编译过程中进行数据库连接、I/O流操作时务必小心,在使用完毕后,及时关闭以释放资源。因为对这些大对象的操作会造成系统大的开销,稍有不慎将会导致严重的后果。

5.尽量减少对变量的重复计算

     明确一个概念,对方法的调用,即使方法中只有一句语句,也是有消耗的,包括创建栈帧、调用方法时保护现场、调用方法完毕时恢复现场等。所以例如下面的操作:

for(int i=0;i<list.size();i++){....}

   建议替换为:

for(int i=0, int length=list.size(); i<length;i++){....}

这样,在list.size()很大的时候,就减少了很多的消耗;

6.尽量采用懒加载的策略,即在需要的时候才创建

例如:

String str="aaa";
if(i==1 )
{
list.add(str);
}

建议替换为:
if(i==1)
{
String str="aaa";
list.add(str);
}

7.慎用异常 

       异常对性能不利。抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为fillnStackTrace()的本地同步方法,fillnStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于处理错误处理,不应该用来控制程序流程。

8.不要在循环中使用try……catch……,应该把其放在最外层

9.如果能估计到待添加内容的长度,为底层以数组方式实现的集合、工具类指定初始长度

10.当复制大量数据时,使用System.arraycopy()命令

11.乘法除法使用移位操作

例如:
for(val=0; val<10000; val+=5)
{
a=val*8;
b=val/2;
}

用位移操作可以极大地提高性能,因为在计算机底层,对为的操作是最方便的、最快的,因此建议修改为:
for (val=0; val<10000; val+=5)
{
a=val << 3
b=val >> 1
}
唯一操作一般不太好理解,使用过程中最好加上注释;

12.循环内不要不断创建对象引用

例如:
for (int I=1; I<=count; I++)
{
Object obj=new Object();
}

这种做法会导致内存中有count份Object对象引用的存在,count很大的话,就耗费内存了,更改为:

Object obj=null;
for( int I=0; I<=count; I++){
object=new Object();
}

这样的话,内存中只有一份Object对象的引用,每次new Object()的时候,object对象引用指向不同的Object罢了,只是内存中只有一份,这样就大大节省了内存空间了;

13.基于效率和类型检查的考虑,应该尽可能使用array,无法确定数组的大小时才使用ArrayList

14.尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销

15.不要将数组声明为public static final

     因为这毫无意义,这样只是定义了引用为static final,数组的内容还是可以随意改变的,将数组声明为public更是一个安全漏洞,这意味着这个数组可以被外部类所改变。

16.尽量在合适的场合使用单例

     使用单例可以减轻加载的负担,缩短加载时间、提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:

      (1)控制资源的使用,通过线程同步来控制资源的并发访问;

      (2)控制实例的产生,以达到节约资源的目的;

      (3) 控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或者线程之间实现通信

18.及时清除不需要的会话

      当会话不需要的时候,应当及时调用HtpSession的invalidate()方法清除会话;

19.实现RandomAccess接口的集合比如ArrayList,应当使用最普通的for循环而不是foreach循环来遍历

       这是JDK推荐给用户的。JDK API对于RandomAccess接口的解释是:实现RandomAccess接口用来表明其支持快速随机访问,此接口的主要目的是允许一般的算法更改其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。实际经验表明,实现RandomAccess接口的类实例,假如是随机访问的,使用普通for循环效率将高于使用foreach循环;反过来,如果是顺序访问的,则使用Iterator会效率更高。可以使用类似如下的代码作判断:

if (list instanceof RandomAccess)
{ for (int i = 0; i < list.size(); i++){}
}else{
Iterator<?> iterator = list.iterable(); 
while (iterator.hasNext()){iterator.next()}
}

       foreach循环的底层实现原理就是迭代器Iterator,参见Java语法糖1:可变长度参数以及foreach循环原理。所以后半句”反过来,如果是顺序访问的,则使用Iterator会效率更高”的意思就是顺序访问的那些类实例,使用foreach循环去遍历。

20.使用同步代码块替代同步方法

       这点在多线程模块中的synchronized锁方法块一文中已经讲得很清楚了,除非能确定一整个方法都是需要进行同步的,否则尽量使用同步代码块,避免对那些不需要进行同步的代码也进行了同步,影响了代码执行效率。

21.将常量声明为static final,并以大写命名

        这样在编译期间就可以把这些内容放入常量池中,避免运行期间计算生成常量的值。另外,将常量的名字以大写命名也可以方便区分出常量与变量;

22.不要创建一些不使用的对象,不要导入一些不使用的类

23.程序运行过程中避免使用反射

        请参见反射;反射是java提供给用户的一个很强大的功能,功能强大意味着效率不高。不建议在程序运行过程中尤其是频繁使用反射机制,特别是Method的invoke方法,如果确实有必要,一种建议性的做法是将那些需要通过反射加载的类在项目启动的时候通过反射实例化出一个对象并放入内存。因为用户只关心在交互的时候获取最快的响应速度,并不关心醒目启动的时候花多久的时间。

24.使用数据库连接池和线程池

        这两个池都是用于重用对象的,前者可以避免频繁地打开和关闭链接,后者可以避免频繁的创建和销毁线程。

25.使用带缓冲的输入输出流进行IO操作

        带缓冲的输入流和输出流,即BufferedReader,BufferedWriter、BufferedInputStream、BufferedOutputStream,这可以极大地提升IO效率;

26.顺序插入和随机访问较多的场景使用ArrayList,元素删除和中间插入比较多的场景使用LinkedList这个,理解ArrayList和LinkedList的原理就知道了

27.不要让public方法中有太多的形参

public方法即对外提供的方法,如果给这些方法太多形参的话主要有两点坏处:

1、违反了面向对象的编程思想,Java讲求一切都是对象,太多的形参,和面向对象的编程思想并不契合

2、参数太多势必导致方法调用的出错概率增加

至于这个”太多”指的是多少个,3、4个吧。比如我们用JDBC写一个insertStudentInfo方法,有10个学生信息字段要插如Student表中,可以把这10个参数封装在一个实体类中,作为insert方法的形参。

28.字符串变量和字符串常量eauals的时候将字符串常量写在前面

String str = "123";
if (str.equals("123")) {...}
建议修改为:

String str = "123";
if ("123".equals(str))
{
...
}

这样做主要是可以避免空指针异常

30.不要对数组使用toString()方法

public static void main(String[] args)
{ int[] is = new int[]{1, 2, 3};
System.out.println(is.toString());
}

结果是:
[I@18a992f

本意是想打印出数组内容,却有可能因为数组引用is为空导致空指针异常。
不过虽然对数组toString()没有意义,但是对集合toString()是可以打印出集合里面的内容的,
因为集合的父类AbstractCollecttions重写了Object的toString()的方法。

32.公共的集合类中不使用的数据一定要及时remove掉

        如果一个集合类是共用的(也就是说不是方法里面的属性),那么这个集合里面的元素是不会自动释放的,因为始终有引用指向它们。所以,如果共用集合里面的某些数据不使用而不去remove掉它们,那么将会造成这个共用集合不断增大,使得系统有内存泄漏的隐患。

33.把一个基本数据类型转为字符串,基本数据类型.toString()是最快的方式、String.valueOf(数据)、数据+ 最慢

34.使用最有效率的方式去遍历map

public static void main(String[] args)
{
HashMap<String, String> hm = new HashMap<String, String>();
hm.put("111", "222");Set<Map.Entry<String, String>> entrySet = hm.entrySet();
Iterator<Map.Entry<String, String>> iter = entrySet.iterator(); while (iter.hasNext())
{
Map.Entry<String, String> entry = iter.next();
System.out.println(entry.getKey() + "	" + entry.getValue());
}
}

如果你只想遍历一下这个Map的key值,那用“Set keySet = hm.keySet();”会比较适合一些

35.对资源的close()建议分开操作

比如有这么一段代码:
try{
xxx.close();
yyy.close();
}catch(Exceotion e){
....
}

建议修改为:
try{
xxx.close();
}catch(Exception e){
....
}

try{
yyy.close();
}catch(Exception e){
....
}

   虽然这样做有些麻烦,却能避免资源泄漏。万一xxx.close();抛异常了。那么就进入catch 中了,yyy.close()不会执行,yy这块儿资源就不会回收了,一只占用着这样的代码一多,是可能引起资源句柄泄漏的。而改为上面的写法之后,就保证了无论如何xxx和yyy都会被close掉;

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值