在项目中:关于代码优化的一些思考

问题1 :客户登录APP,点击客户管理,查看NB客户12S才返回数据?

原因:当时在需求立项的时候,因为NB100+客户是不变的静态数据,不会多也不会少。但是老大提出:一半调接口取数据,另一半从自己表里查。也就是说,先从DK接口取300+数据,每条数据均有十几个字段,再从我们表里查100条数据,然后循环比对,返回前端。性能问题由此发生。通过分析:调用接口达到了8S,查库比对达到了4S。

优化:1:飞哥提出,要么纯调接口我们只管展示,要么全部我们自己查,最后取后者。(根据DK实际情况作出选择)

          2:将代码中某些逻辑放入SQL中,利用存储过程解决。

优化后5S左右。

思考:定需求的时候就没有做好这方面的详细考虑,有话语权的人直接拍板决定,可能是怕出了问题担责任吧。从另一个角度考虑,优化的还是不够彻底,代码方面我觉得还可以继续优化,良好的编码习惯也是相当重要的。通过别人的优化设计,自己也学到了许多,慢慢积累。

优化细节: 

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  


猜你喜欢

转载自blog.csdn.net/zezezuiaiya/article/details/80089893