30个精简代码的小技巧,一起学起来!

前言

优化代码,一个很重要的课题。可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对代码的运行效率有什么影响呢?这个问题我是真么考虑的,好比人吃饭,吃一粒米,没用,但是一万粒,十万粒呢,这样的效率就很可观了。

代码优化的目标是:

  • 减小代码体积;
  • 提高代码运行效率。

具体方法:

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

带有final的修饰符的类是不可派生的。在java核心API中,有许多应用final的例子,例如:java.long.String,整个类都是final的。为类指定final修饰符可以让类不可被继承,为方法指定final修饰符可以让方法不被重写。如果指定了一个类为final,则该类所有的方法都是final的。

java编译器会寻找机会内联所有的final方法,内联对于提升java运行效率作用重大,大概能使性能提升50%。

内联:通常是用来消除调用函数时所需要的时间

2.尽量复用对象

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

3.尽可能使用局部变量

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

4.及时关闭流

java编程过程中,进行的数据库连接,I/O流等操作时务必当心,在使用完毕后,应及时关闭流以释放资源。因为这些大对象的操作会造成系统大的开销,会大大影响程序运行效率。

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

明确概念,对方法的调用,即使方法中只有一条语句,也是要加载的。包括创建堆栈。
调用方法时保护现场,方法结束时恢复现场等。如:

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

可以替换为:

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

这样,如果list.size()里的数据有很多时(如2000000左右),会减少很多性能消耗。

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

如:

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

可以替换为:

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

7.慎用异常

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

8.不要在循环中使用try···catch···

应该把它放到最外层。

9.工具类指定初始长度。

如果能估计到待添加的内容长度,为底层以数组方式实现的集合,工具类指定初始长度比如ArrayList、LinkedList、StringBuilder、StringBuffer、HashMap、HashSet等等,

以StringBuilder为例:

(1) StringBuilder() //默认分配16个字符空间
(2) StringBuilder(int size) //默认分配size个字符空间
(3) StringBuilder(String str) //默认分配16个字符+str.length()个空间

可以通过类(不仅仅是StringBuilder)来设定它的初始化容量,这样可以明显提升性能。

比如,StringBuilder吧,length表示当前的StringBuilder能保持的字符数量。因为当StringBuilder达到最大容量的时候,它会将自身容量加到当前的2倍在加2,无论何时,只要StringBuilder达到它的最大容量值,它就会创建一个新的字符数组然后将旧的字符数组里面的内容拷贝到新数组里是一个十分耗时的工作。

比如,一个字符数组大概要放5000个字符而不指定长度,最接近5000的2次幂是4096,那么:

  • 在4096的基础上,在申请8194+2个大小的新数组,加起来相当于共申请了12292的内存空间,如果一开始就指定长度5000或5500的话,就能节省一倍的空间。
  • 把原来的4096个字符拷贝到新的字符数组中,这样不仅浪费内存又降低代码运行效率。

所以,给底层以数组实现的集合、工具类设置一个合理的初始值是不会有错的。

但是,注意,向HashMap这种以数组+链表实现的集合,别把初始值大小和你预估的大小设置的一样,因为一个table上连接一个对象的概率几乎为0。

建议初始大小值设为2的N此幂,如果预估是2000个元素,设置成 new HashMap(128)、new HashMap(256)都可以。

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

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

如:

for(val = 0;val < 100000;val += 5) {
            a = val * 8;
            b = val / 2;
        }

用移位操作可以极大的提升性能,因为在计算机底层,对位的操作是最方便的
可以替换为:

for(val = 0;val < 100000;val += 5) {
            a = val << 3;
            b = val >> 1;
        }

注:移位操作虽然方便,但是可能使代码不太好理解,因此需要加上相应的注释。

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

如:

for(int i = 0;i <= count; i++) {
            Object obj = new Object();
        }

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

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

这样的话,内存中只有一份Object对象引用,每次new Object()的时候,Object对象引用指向不同的Object而已,但是内存中只有一份,就可以节省很多内存空间了。

13. 尽可能使用array

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

14. 不推荐使用Hashtable,Vector,StringBuffer,

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

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

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

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

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

控制资源的使用,通过线程同步来控制资源的并发访问;
控制实例的产生,达到节约资源的目的;
控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信。

17.尽量避免随意使用静态变量

因为当某个对象被定义为static时,gc通常是不会回收这个对象所占有的堆内存的,
如:

public class A {
            private static B b = new B();
        }

此时静态变量b的生命周期与A类相同,如果A类不被卸载,那么引用B指向的B对象会一直存在内存中,直到程序终止。

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

当应用服务器需要保存更多会话时,如果内存不足,操作系统会把部分数据转移到磁盘里,应用服务器也可能根据MRU(最近频繁使用的会话)算法,把部分不活跃的会话转存到磁盘里,甚至可能抛出内存不足的异常。如果会话要被转存到磁盘,就必须先序列化,在大规模集群中,对对象进行序列化代价是很大的。因此,应及时调用HttpSession的invalidate()方法清除会话。

19. 使用for循环遍历

实现RandomAccess接口的集合比如ArrayList,应当使用for循环而不是foreach来遍历JDK API对于RandomAccess接口的解释是:实现RandomAccess接口用来表明其支持快速随机访问,此接口的主要目的是允许一般的算法更改其行为,从而将其应用到随机或连续访问列表时能够提供良好的性能。

实现RandomAccess接口类实例,加入是随机访问的,使用for循环比foreach效率高;如果不是随机访问的使用foreach效率高。
如:

if(list instanceof RandomAccess) {
                for(int i = 0 ;i < list.size();i++){}
            } else {
                for(List li : list) {
                    System.out.println(li);
                }
            }

foreach底层实现原理就是迭代器(iterator)

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

除非能确定整个方法都是需要进行同步的,否则尽量使用同步代码块,避免对那些不需要同步的代码也进行同步,从而影响效率。

21.将常量声明为 STATIC FINAL

这样在编译运行时就可以把这些内容放入常量池中,避免运行期间计算生成常量的值。另外,将常量的名字进行大写的原因。

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

反射是java提供给用户一个很强大的功能,但是功能强大效率却不是很高。不建议在程序运行过程中频繁是哦那个反射机制,特别是Method的invoke方法。如果确实必要,建议将那些需要通过反射加载的类在项目启动的时候通过反射实例化出一个对象并放入内存。

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

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

24.使用带缓冲的输入,输出流进行I/O操作

带缓冲的输入,输出流即:

BufferedReader,bufferedWrite,BufferedInputStream,BufferedOutputStream

它们可以大大提升I/O的效率

25.合理使用 ArrayList和LinkedList

顺序插入和随机访问比较多的场景使用ArrayList,元素删除和中间插入比较多的场景使用LinkedList理解这两个集合有何不同即可

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

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

  • 违反面相对象的思想,java讲究万物皆对象,太多形参,和java编程思想不和
  • 参数较多会导致出错概率增加

27.字符串变量和字符串常量,equals的时候,将字符串常量写在前面

如:

Strring str = “123”;
if(str.equals(“123”)) {
}

可以替换为:

Strring str = “123”;
if(“123”.equals(str)) {
}

这么做是为了避免空指针的出现(中期项目有讲过)

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

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

29.不要对超出范围的基本数据类型做向下强制转换,得到的结果绝对是错误的。

30.数据类型转为字符串,toString()最快

把一个基本数据类型转为字符串,对象点toString()是最快的方法,对象点valueOf(数据)次之,数据+””最慢,如,想把Integer i转为字符串类型,有三种方式:

  1. i.toString()
  2. i.valueOf(i)
  3. i+””

下面测试

 public static void main(String[] args) {
      int loopTime = 50000;
      Integer i = 0;
      long startTime = System.currentTimeMillis();
      for(int j = 0 ;j < loopTime;j++) {
          String str = String.valueOf(i);
      }

          System.out.println("String.valueOf():"+(System.currentTimeMillis()- startTime) +"ms");
          startTime = System.currentTimeMillis();
          for(int j = 0;j < loopTime;j++) {
              String str = i.toString();
          }
              System.out.println("Integer.toString():"+(System.currentTimeMillis()- startTime) +"ms");

              startTime = System.currentTimeMillis();
              for(int j = 0 ;j < loopTime;j++) {
                  String str = i + "";
              }
                  System.out.println("i+\"\":"+(System.currentTimeMillis()- startTime) +"ms");
              }

结果:

String.valueOf():11ms;
Integer.toString():5ms;
i + "":25ms;

原理是:

  1. String.valueOf()方法调用了Integer.toString()方法,但是在调用前先做了一次空判断;
  2. Integer.toString()是直接调用;
  3. i + “”是使用了StringBuilder实现,先用了append方法拼接,在用toString()获取字符串

原文地址:https://blog.51cto.com/14570694/2465973

时间: 2024-10-11 23:38:25

30个精简代码的小技巧,一起学起来!的相关文章

&lt;转&gt; 30 个有关 Python 的小技巧

目录[+] 1.1 拆箱 1.2 拆箱变量交换 1.3 扩展拆箱(只兼容python3) 1.4 负数索引 1.5 切割列表 1.6 负数索引切割列表 1.7指定步长切割列表 1.8 负数步长切割列表 1.9 列表切割赋值 1.10 命名列表切割方式 1.11 列表以及迭代器的压缩和解压缩 1.12 列表相邻元素压缩器 1.13 在列表中用压缩器和迭代器滑动取值窗口 1.14 用压缩器反转字典 1.15 列表展开 1.16 生成器表达式 1.17 字典推导 1.18 用字典推导反转字典 1.19

30个有关Python的小技巧

从我开始学习python的时候,我就开始自己总结一个python小技巧的集合.后来当我什么时候在Stack Overflow或者在某个开源软件里看到一段很酷代码的时候,我就很惊讶:原来还能这么做!,当时我会努力的自己尝试一下这段代码,直到我懂了它的整体思路以后,我就把这段代码加到我的集合里.这篇博客其实就是这个集合整理后一部分的公开亮相.如果你已经是个python大牛,那么基本上你应该知道这里面的大多数用法了,但我想你应该也能发现一些你不知道的新技巧.而如果你之前是一个c,c++,java的程序

最实用的代码重构小技巧排行榜TOP10

这次我们抛开JAVA虚拟机源码这些相对底层的东西,来与各位探讨一下几个代码重构的小技巧,这些内容部分来自于书籍当中,部分来自于项目当中的一些实践经验.如果猿友们曾经用过这种手法,也不妨参与到文章的留言当中,将你的小心得.小体会共享与他人,也可以拿来冲击LZ自己定义的排行榜,LZ不甚欢迎. 重构的手法有很多种,相对而言,一篇文章的涵盖量自然是无法提到所有,LZ这里也只能提出一些平时会经常使用的一些手法,像一些比较高端的手法,各位有兴趣的可以去找一些专门的书籍涉猎. 本文部分重构小技巧可能主要与JA

优化C/C++代码的小技巧

说明: 无意看到一篇小短文,猜测作者应该是一个图形学领域的程序员或专家,介绍了在光线(射线)追踪程序中是如何优化C/C++代码的.倒也有一些参考意义,当然有的地方我并不赞同或者说我也不完全理解,原文在此,我的粗糙翻译如下: 1. 牢记Ahmdal定律 funccost表示是函数func的运行时间百分比,funcspeedup是你优化后函数的运行系数: 所以,如果函数TriangleIntersect()占用40%的运行时间,而在你优化后使它运行快了两倍,那么你的程序运行能够快了25%: 这意味着

优化C/C++代码的小技巧(转)

源:http://www.cnblogs.com/lizhenghn/p/3969531.html 说明: 无意看到一篇小短文,猜测作者应该是一个图形学领域的程序员或专家,介绍了在光线(射线)追踪程序中是如何优化C/C++代码的.倒也有一些参考意义,当然有的地方我并不赞同或者说我也不完全理解,原文在此,我的粗糙翻译如下: 1. 牢记Ahmdal定律 funccost表示是函数func的运行时间百分比,funcspeedup是你优化后函数的运行系数: 所以,如果函数TriangleIntersec

phpstorm用正则删除PHP代码空行小技巧

有很多小伙伴会遇到代码空行特别多,但是一行一行删除肯定很烦躁,这时候就需要用到批量删除空行. 怎么批量删除空行呢? 我的办法是用正则把所有空行找到,然后一键全部替换. 首先把Match Case和Regex打上勾 第一个搜索框就填写正则规则:^\n 匹配到所有空行之后,点击[Replace all]即可. 替换效果如下:

js脚本代码调试小技巧

以前写js代码调试代码查看数据是否正确的时候不知道F12(开发者工具),都是alert(xxx)或者console.log(xxx), 现在知道还可以用document.write或者try...catch或者window.onerror 还发现一个好用的东西,JSON.stringify(xxx),如果参数为对象,会显示对象的内容,不用用循环什么的了,对数组或数据对象之类的挺有用,像下面弹窗就是一个数据对象作为JSON.stringify参数的结果

响应式设计代码(小技巧)

1.媒体查询,包含将帮助您快速开始自己的多屏幕网站开发的示例. 2.前5项功能(width.height.device-width.device-height和orientation)是最有用的. 3.为移动电话.平板电脑和桌面计算机提供不同的式样 4.添加带有这些设置的视口<meta>标签能够确保智能手机和平板电脑遵守媒体查询中的宽度设置. 此外,将初始数值设置为1也能够确保iPhone.iPad和Pod touch在横向正确地应用宽度设置. 5.省略高度使得你能够在较小的屏幕上将图像缩小而

胡扯一点代码实现小技巧啦啦啦~ x

一. for(int t=m; t; t--) a[++n] = b[t]; // 循环中间的 t:非零即真,遇到 t=0 才停下 int ans(0); // 效果等价于 ans=0 二. 开数组的时候我们都喜欢从1号开始使用,而把0号元素冷落了 但是开了就是开了,0号闲着也是闲着,不如把它合理的利用起来! 所以,可以用a[0]来计数, a[0]++,a[0]--什么的, 回收利用我最强! (未完待续?)