本文的主要类容来自 微信公众号 : java后端技术, (之后了解到的其他细节也会在此文中进行补充)
一、 代码优化的目的:
1,减小代码体积 : 还有助于增加代码的可阅读性(这个很重要)
2,提高运算效率
二、 代码优化细节
1、尽量指定类、 变量、 方法的 final 修饰符
带有修饰符的类是不可派生的, 在java的api中 像String之类的就是属于这类。
被final修饰的类不能被继承, 被final修饰的方法不能被重写。 如果指定一个类是final的,那么这个类中所有的方法都是final的。
文中的作者指出: java的编译器会内联所有的final方法, 这对java运行效率提升很大, (可以达到 50% 左右)。
2、尽量重用对象, 特别是String对象的使用, 出现字符串连接的时候, 应该使用StringBuilder 或者 StringBuffer代替, 因为java
虚拟机 不仅要创建String对象(String对象为final对象,不能被改变), 这样可以减少内存中的对象信息,。
3,、如果可以的话, 尽量使用局部变量, 局部变量 存在与 stack中, 随着方法运行的结束 而消失。 而其他的变量 则会存在与heap中, 这在
方法运行结束后 还需要额外的垃圾回收。
4、及时的关闭流, 在进行数据库连接或者 io编程的时候, 在使用完毕应当即使的关闭 用来释放资源。 因为流的操作会造成极大的性能开销。
5、尽可能的减少重复的计算 , 在方法的调用的时候, 即便只有一句话,也是系统的性能开销。 在进行 集合 或者数组的遍历的时候, 其长度
可以先计算出来。尤其是在 length恒大的时候, 对性能的提高就越发的明显
6、尽量使用 懒加载策略, 在已经马上需要该类的时候再去创造该类。
7、谨慎使用 异常, 在使用异常的时候 java虚拟机 就一定回去调用堆栈信息, 消耗很多资源。 所以不要为了达到某个普通的逻辑功能来使用异常。
8、try catch 语句 最好放在最外面, 不要被选择 ,循环等判断。
9、如果能估计到 最后添加的类容的长度, 可以为 底层使用数组方法、实现的集合指定长度 : ArryList 、 LinkeList 、 StringBuffer 、 StringBuidler
HashMap 、 HashSet 等。 因为 当集合数据满时, 会自动在底层创建一个 两倍当前长度的 集合,并将数据拷贝到新集合中。 而集合的初始
默认长度较小, 所以数据量较大的时候, 会创建很多 类, 消耗大量的资源。
10、当复制大量数据的时候, 使用 System.arrycopy()
11、 乘法 和 除法使用移位操作, 这回加快计算机的执行速度
12、循环内不要创建新的对象引用, 因为 如果循环量较大的话, 会在 stack中创建大量的临时引用。
13、如无必要, 应当尽量使用 array , 而不是使用 arrayList(当无法确定数组大小的时候再使用 list)
14、如果没有同步要求, 尽量使用 HashMap, ArrayList, StringBuilder。 不要使用 Hashtable, Vechtor,StringBuffer
15、不要将一个数组 声明成一个 public static final 数组声明为 final 也还是能被改变, 而一个public 的数组明显并不安全。
16、 在合适的场合使用单例模式 : 单例模式 可以减轻加载的负担, 减少加载的时间, 提高加载的效率,
使用场合 : 1)控制资源使用, 通过线程同步来控制资源的并发访问
2)控制数据的共享, 以达到节约资源的目的
3)控制数据共享, 再不建立直接关联的情况下, 让多个不相干的进程和线程中实现共享。
17、尽量避免随意使用静态变量, 一个静态的变量 一般不会被 gc 回收,。
18、及时清除不需要的会话 session 等 。
19、 实现 RandomAccess 接口的集合 (ArryList 等) , 应当使用普通的 for 循环 而不是 forEach循环。
因为foreach 循环底层使用的是 iterator , 其是顺序访问的, 而RandomAccess接口表明支持随记访问。
而随记访问使用 for 循环更加的高效 , 反之亦然。
20、使用同步代码块 来代替 同步方法,
21、将常量声明为 static final ,并大写命名 : 虚拟机会将该常量放入常量池。
22、不要创建一些不使用的 对象, 不要引入一些不使用的类。
23、 少使用 反射, 虽然反射机制非常强大, 但是效率不太高。 建议: 在项目启动的时候 通过反射机制将要反射的地方创建出反射的对象。
24、使用数据库连接池 和 线程池 (不用解释)
25、使用带缓冲的输入输出流
26、顺序插入和随机访问较多的地方 使用 ArrayList , 删除和中间插入比较多的使用 LinkedList (底层数据结构)。
27、public 方法中不要有太多形参
28、字符串变量 和 字符串常量进行比较的时候将 常量放在前面。
29、在java中 i==1 和 1==i是没有什么区别的, 但是在习惯上使用 i == 1.
30、不建议对 数组使用 toString 方法, 因为 会打印成一个 哈希地址, 但是可以对 集合使用toString方法, 因为其父类中重写了 toSting方法。
31、不要对超出范围的基本数据类型使用强制向下转型。如: 将一个 long 类型的强制转换成一个 int类型的, 这是因为 : 在java中向下转型是
切断了long 的高 32 为二进制码, 所以并不能直接得到 是、十进制上的后面位数。
32、在公共的集合中, 不使用的类要即使清理掉, 集合没有在方法中的话, 只要对象一直存在集合就会一直存在, 随着使用的增加, 集合中的数据也会增加
当增加到一定程度时 , 有可能造成内存泄露隐患。
33、如果将一个 基本数据类型转换成一个字符串, 那么 运行速度最快的是 toString()方法, 之后是 String.valueOf(), 在之后是 “” + “” 最慢
因为String.valueOf()底层使用的是toString方法, 而 “”+底层使用的是 Stringuilder实现的。
34、使用最有效率的方法来遍历 Map
public static void main(String [] args){ HashMap<String, String> hm = new HashMap<String, String>; hm.put("hehe","java"); Set<Map.entry<String,String>> entrySet = hm.entrySet(); Iterator it = entrySet.iterator(); while(it.hasNext()){ ...... } }
如果只想遍历 key的话, 使用 keySet就好了
35、对资源的 close 应当分开进行: 因为一个 close 操作的时要 使用try catch 语句进行异常处理, 如果有多个close, 当一个出现了异常
那么他后面的 close就能操作了。
36、在使用 ThreadLocal 的时候, 在使用前 和使用后都要进行 remove 操作。
因为在使用 线程池的时候, 当一个线程使用结束之后, 会将其放入线程池中,而不会将其 销毁, 我们拿到一个线程之后, 很可能
拿到其上绑定的其它数据, 由此造成的异常非常难以查明。
37、应当使用 常量的定义来代替魔鬼数字(没有具体含义的数字 和 字符串), 会极大地提高可阅读性。
38、在long 或者 Long 类型赋值的时候 应当使用 L 而不是 l, 因为 l 易于 1 混淆。
39、所有重写的方法都应当 保留 @Override 注解
一是这样可以 知道这个放法是个重写方法,
二是能够判断你的重写是否有语法错误
三是在抽像类 或者 接口中进行 签名修改的时候, 继承类中会出现异常, 容易定义到具体的类。
40、在循环 中 不要使用 + 进行字符串的拼接, 直接使用 StringBuilder 的 apperd方法进行拼接。
前面提到过 + 操作底层使用的是 SringBuilder 进行的, 这样就会生成 循环次数 个 StringBuilder
41、推荐使用 jdk7 中提供的 行的 Objects 类进行equals比较, 直接使用 equals 有空指针危险。
42、不要使用多线程对 一个 Random对象进行操作, 虽然没有线程安全问题, 但是会降低性能,推荐使用 ThreadLocalRandom
43、 静态类 、单例类 、 工厂类 的构造器使用 private