代码优化最重要的做用应该是避免未知的错误,所以在写代码的时候,从源头开始注意各类细节,权衡并使用最优的选择,将会很大程度上避免出现未知的错误,从长远看也极大的下降了工做量。因此说代码优化的目标是减少代码体积、提升代码运行效率。优化是无止境的,本文也只给出整理的一些常见优化建议。java
(1)尽可能指定类、方法的 final 修饰符。程序员
带有 final 修饰符的类是不可派生的。在 Java 核心 API 中,有许多应用 final 的例子,例如 java.lang.String,整个类都是 final 的。为类指定 final 修饰符可让类不能够被继承,为方法指定 final 修饰符可让方法不能够被重写。若是指定了一个类为 final,则该类全部的方法都是 final 的。Java 编译器会寻找机会内联全部的 final 方法,内联对于提高 Java 运行效率做用重大,具体能够查阅 Java 运行期优化相关资料,此举可以使性能平均提升 50%。算法
(2)尽可能重用对象。sql
特别是 String 对象的使用,出现字符串链接时应该使用 StringBuilder/StringBuffer 代替。因为 Java 虚拟机不只要花时间生成对象,之后可能还须要花时间对这些对象进行垃圾回收和处理,所以生成过多的对象将会给程序的性能带来很大的影响。数据库
(3)尽量使用局部变量。编程
调用方法时传递的参数以及在调用中建立的临时变量都保存在栈中,速度较快,其余变量,如静态变量、实例变量等,都在堆中建立,速度较慢。另外,栈中建立的变量,随着方法的运行结束,这些内容就没了,不须要额外的垃圾回收。数组
(4)及时关闭流。安全
Java 编程过程当中,进行数据库链接、I/O 流操做时务必当心,在使用完毕后,及时关闭以释放资源。由于对这些大对象的操做会形成系统大的开销,稍有不慎,将会致使严重的后果。性能优化
(6)尽可能采用懒加载的策略,即在须要的时候才建立。服务器
这个原则其实就是节约,具体样例以下。
(7)慎用异常。
异常对性能不利,抛出异常首先要建立一个新的对象,Throwable 接口的构造函数调用名为 fillInStackTrace() 的本地同步方法,fillInStackTrace() 方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java 虚拟机就必须调整调用堆栈,由于在处理过程当中建立了一个新的对象。异常只能用于错误处理,不该该用来控制程序流程。
(8)不要在循环中使用 try-catch,应该把其放在最外层。
根据网友们提出的意见,这一点我认为值得商榷,其实分业务场景吧,有些场景须要循环终止,有些只是为了忽略当此循环处理。
(9)若是能估计到待添加的内容长度,为底层以数组方式实现的集合、工具类指定初始长度。
好比 ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet 等,以 StringBuilder 为例,StringBuilder() 构造方法默认分配 16 个字符的空间,StringBuilder(int size) 构造方法默认分配 size 个字符的空间,StringBuilder(String str) 构造方法默认分配 16 个字符加 str.length() 个字符空间,因此能够经过类的构造方法来设定它的初始化容量,这样能够明显地提高性能。
(10)当复制大量数据时,使用 System.arraycopy() 命令。
这个确定你们都没有疑问的,性能优化的实现而已。
(11)乘法和除法使用移位操做。
用移位操做能够极大地提升性能,由于在计算机底层,对位的操做是最方便、最快的,可是移位操做虽然快,可能会使代码不太好理解,所以最好加上相应的注释。
(12)循环内不要不断建立对象引用。
见以下案例解释分析缘由。
(13)基于效率和类型检查的考虑,应该尽量使用 array,没法肯定数组大小时才使用 ArrayList。
(14)尽可能使用 HashMap、ArrayList、StringBuilder,除非线程安全须要,不然不推荐使用 Hashtable、Vector、StringBuffer,后三者因为使用同步机制而致使了性能开销。
(15)不要将数组声明为 public static final。
由于这毫无心义,这样只是定义了引用为 static final,数组的内容仍是能够随意改变的,将数组声明为 public 更是一个安全漏洞,这意味着这个数组能够被外部类所改变。
(16)尽可能在合适的场合使用单例。
使用单例能够减轻加载的负担、缩短加载的时间、提升加载的效率,但并非全部地方都适用于单例,简单来讲,单例主要适用于如下三个方面:
控制资源的使用,经过线程同步来控制资源的并发访问;
控制实例的产生,以达到节约资源的目的;
控制数据的共享,在不创建直接关联的条件下,让多个不相关的进程或线程之间实现通讯;
(17)尽可能避免随意使用静态变量。
由于当某个对象被定义为 static 的变量所引用,那么 gc 一般是不会回收这个对象所占有的堆内存的。
(18)及时清除再也不须要的会话。
为了清除再也不活动的会话,许多应用服务器都有默认的会话超时时间,通常为 30 分钟。当应用服务器须要保存更多的会话时,若是内存不足,那么操做系统会把部分数据转移到磁盘,应用服务器也可能根据MRU(最近最频繁使用)算法把部分不活跃的会话转储到磁盘,甚至可能抛出内存不足的异常。若是会话要被转储到磁盘,那么必需要先被序列化,在大规模集群中,对对象进行序列化的代价是很昂贵的。所以,当会话再也不须要时,应当及时调用 HttpSession 的 invalidate() 方法清除会话。
(19)实现 RandomAccess 接口的集合(好比 ArrayList)应当使用最普通的 for 循环而不是 foreach 循环来遍历。
这是 JDK 推荐给用户的,JDK API 对于 RandomAccess 接口的解释是实现 RandomAccess 接口用来代表其支持快速随机访问,此接口的主要目的是容许通常的算法更改其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。实际经验代表,实现 RandomAccess 接口的类实例,假如是随机访问的,使用普通 for 循环效率将高于使用 foreach 循环,反过来,若是是顺序访问的,则使用 Iterator 会效率更高。
(20)使用同步代码块替代同步方法。
尽可能使用同步代码块,避免对那些不须要进行同步的代码也进行了同步,影响了代码执行效率。
(21)将常量声明为 static final,并以大写命名。
这样在编译期间就能够把这些内容放入常量池中,避免运行期间计算生成常量的值。另外,将常量的名字以大写命名也能够方便区分出常量与变量。
(22)不要建立一些不使用的对象,不要导入一些不使用的类。
这毫无心义,若是代码中出现 "The value of the local variable i is not used"、"The import java.util is never used",那么请删除这些无用的内容,虽然说没啥影响,可是有些时候编译期会报错,譬如没 import 用到的类被删掉了。
(23)程序运行过程当中避免使用反射。
不建议在程序运行过程当中使用,除非万不得已,尤为是频繁使用反射机制,特别是 Method 的 invoke 方法,若是确实有必要,一种建议性的作法是将那些须要经过反射加载的类在项目启动的时候经过反射实例化出一个对象并放入内存,用户只关心和对端交互的时候获取最快的响应速度,并不关心对端的项目启动花多久时间。
(24)使用数据库链接池和线程池。
这两个池都是用于重用对象的,前者能够避免频繁地打开和关闭链接,后者能够避免频繁地建立和销毁线程。
(25)使用带缓冲的输入输出流进行 IO 操做。
带缓冲的输入输出流,即 BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,这能够极大地提高 IO 效率。
(26)顺序插入和随机访问比较多的场景使用 ArrayList,元素删除和中间插入比较多的场景使用 LinkedList。
(27)不要让 public 方法中有太多的形参。
public 方法即对外提供的方法,若是给这些方法太多形参的话主要坏处是违反了面向对象的编程思想,Java 讲求一切都是对象,太多的形参和面向对象的编程思想并不契合,参数太多势必致使方法调用的出错几率增长。
(28)字符串变量和字符串常量 equals 的时候将字符串常量写在前面,这样能够避免空指针。
(29)建议使用 if (i == 1) 而不是 if (1 == i) 的方式。
由于有可能 == 会误写成 =,而在 C/C++ 中 if (i = 1) 是会出问题的,而 Java 会在编译时报错 "Type mismatch: cannot convert from int to boolean",可是,尽管Java的 if (i == 1) 和 if (1 == i) 在语义上没有任何区别,从阅读习惯上讲,建议使用前者会更好些。
(30)不要对数组使用 toString() 方法。
本意是想打印出数组内容,却打出来的是对象信息,甚至有可能由于数组引用为空而致使空指针异常。对于集合 toString() 是能够打印出集合里面的内容的,由于集合的父类 AbstractCollections<E> 重写了 Object 的 toString() 方法。
(31)不要对超出范围的基本数据类型作向下强制转型。
这很明确,譬如 long 转 int 是会存在潜在风险的。
(32)公用的集合类中不使用的数据必定要及时 remove 掉。
若是一个集合类是公用的(也就是说不是方法里面的属性),那么这个集合里面的元素是不会自动释放的,由于始终有引用指向它们。因此,若是公用集合里面的某些数据不使用而不去remove掉它们,那么将会形成这个公用集合不断增大,使得系统有内存泄露的隐患。
(33)把一个基本数据类型转为字符串,基本数据类型.toString() 是最快的方式、String.valueOf(数据) 次之、数据+"" 最慢。
由于 String.valueOf() 方法底层调用了 Integer.toString() 方法,可是会在调用前作空判断;Integer.toString() 是直接调用;i + "" 底层使用了 StringBuilder 实现,先用 append 方法拼接,再用 toString() 方法获取字符串。
(34)使用最有效率的方式去遍历 Map。
遍历 Map 的方式有不少,一般场景下咱们须要的是遍历 Map 中的 Key 和 Value,那么推荐使用的、效率最高的方式是 entrySet(),若是只是想遍历一下这个 Map 的 key 值则 keySet() 会比较合适一些。
(35)对资源的 close() 建议分开操做。
虽然有些麻烦,却能避免资源泄露,这其实和 try-catch 机制相关,各自分开 close 各自的 try-catch 就会互不影响,防止写在一个 try-catch 中由于一个异常了后面的释放不了。
(36)对于 ThreadLocal 在线程池场景使用前或者使用后必定要先 remove。
由于线程池技术作的是一个线程重用,这意味着代码运行过程当中一条线程使用完毕并不会被销毁而是等待下一次的使用,而 Thread 类中持有 ThreadLocal.ThreadLocalMap 的引用,线程不销毁意味着上条线程 set 的 ThreadLocal.ThreadLocalMap 中的数据依然存在,那么在下一条线程重用这个 Thread 的时候极可能 get 到的是上条线程 set 的数据而不是本身想要的内容。这个问题很是隐晦,一旦出现这个缘由致使的错误,没有相关经验或者没有扎实的基础很是难发现这个问题,所以在写代码的时候就要注意这一点,这将给你后续减小不少的工做量。
(37)切记以常量定义的方式替代魔鬼数字,魔鬼数字的存在将极大地下降代码可读性,字符串常量是否使用常量定义能够视状况而定。
(38)long 或者 Long 初始赋值时使用大写的 L 而不是小写的 l,由于字母 l 极易与数字 1 混淆,这个点很是细节,值得注意。
(39)全部重写的方法必须保留 @Override 注解。
这么作能够清楚地知道这个方法由父类继承而来,同时能够保证重写成功,此外在抽象类中对方法签名进行修改,实现类会立刻报出编译错误。
(40)推荐使用 JDK7 中新引入的 Objects 工具类来进行对象的 equals 比较,直接 a.equals(b) 有空指针异常的风险。
(41)循环体内不要使用 "+" 进行字符串拼接,而直接使用 StringBuilder 不断 append。
由于每次虚拟机碰到 "+" 这个操做符对字符串进行拼接的时候会 new 出一个 StringBuilder,而后调用 append 方法,最后调用 toString() 方法转换字符串赋值给对象,因此循环多少次,就会 new 出多少个 StringBuilder() 来,这对于内存是一种浪费。
(42)不捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类。
异常处理效率低,RuntimeException 的运行时异常中绝大多数彻底能够由程序员来规避,好比 ArithmeticException 能够经过判断除数是否为空来规避,NullPointerException 能够经过判断对象是否为空来规避,IndexOutOfBoundsException 能够经过判断数组/字符串长度来规避,ClassCastException 能够经过 instanceof 关键字来规避,ConcurrentModificationException 可使用迭代器来规避。
(43)静态类、单例类、工厂类将它们的构造函数置为 private。
这是由于静态类、单例类、工厂类这种类原本咱们就不须要外部将它们 new 出来,将构造函数置为 private 以后,保证了这些类不会产生实例对象。
欢迎工做一到五年的Java工程师朋友们加入Java架构开发:744677563
群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用本身每一分每一秒的时间来学习提高本身,不要再用"没有时间“来掩饰本身思想上的懒惰!趁年轻,使劲拼,给将来的本身一个交代!