程序代码优化要点:

  • 字符串优化:分析String源码,了解String常用方法,使用StringBuffer、StringBuilder。
  • List、Map、Set优化:分析常用ArrayList、LinkedList、HashMap、TreeMap、LinkedHashMap、Set接口、集合常用方法优化。
  • 使用NIO:Buffered、Channel操作和原理,使用零拷贝。
  • 引用优化:强引用、弱引用、软引用、虚引用、WeekHashMap。
  • 优化技巧:常用代码优化技巧。这里不一一罗列,请参考下面的详解。
  • 字符串优化:

    • String对象特点:

      • 终态:String类被声明为final,不可被继承重写,保护了String类和对象的安全。在jdk1.5之前final声明会被inline编译,性能大幅度提高,jdk1.5之后性能提升不大。
      • 常量池:String在编译期间会直接分配在方法区的常量池中,当我们写了多个相同值的String对象时,它们实际是指向了同一空间的不同引用罢了。这样对于String这样经常使用的对象访问代价和创建代价是十分低的。需要注意的是当使用String a="123";String b=new String("123");的时候,编译器虽然会创建一个新的String实例,但是实际值依然是指向常量池中的已有的123。我们可以使用a.intern(),String的intern方法返回常量池中的引用,intern是一个native本地方法。
      • 不变性:String对象生成后内存空间永久不会变化,好处是在多线程的情况下不用加锁同步操作。需要注意如下代码:String a="123";a="456";只是改变了对象的引用所指向的位置,实际的”123”是不变的。
    • 关于内存泄漏:

      • 存在内存泄漏的方法: 
        String:

        • substring(int,int):

关于字符串分割和查找:

  • String的split: 
    split实现中使用了正则表达式,在大量字符串分割时正则表达式会贪婪匹配,效率会降低,不推荐使用。

    • StringTokenizer的使用: 
      StringTokenizer是jdk自带的字符串分割工具,由于没有使用正则匹配,所以速度更快,

      • StringBuffer和StringBuilder:

        • 区别:StringBuffer是线程安全的,所有操作字符串的方法都做了synchronized操作,而StringBuilder没有,是线程不安全的,所以StringBuffer性能低于StringBuilder。
        • 注意事项:StringBuffer和StringBuilder都提供了带有capacity参数的构造函数,主要作用是指定初始化容量(保存字符串缓冲区)的大小,当容量超过capacity时,会进行扩容,扩容为原来大小的2倍,创建新内存空间,同时把原来空间的内存拷贝到新内存空间,然后释放原内存空间。由于内存拷贝很耗时,所以最好指定适当的capacity。
        • 与String的+号对比:当使用+号拼接字符串时,编译器会把+号替换成new StringBuilder().append(),提高拼接效率,但是在大量循环拼接时,编译器不够智能,每次都生成新的StringBuilder,产生大量gc,所以性能不高,最好在循环中使用conact或自己构建StringBuffer或StringBuilder。List接口: 由于篇幅过长,故拆分,请参考《Java性能优化笔记-List接口分析》//TODO
          Map接口: 由于篇幅过长,故拆分,请参考《java性能优化笔记-Mapt接口分析》//TODO
          Set接口: 由于篇幅过长,故拆分,请参考《java性能优化笔记-Set接口分析》//TODO
          RadnomAccess接口: 由于篇幅过长,故拆分,请参考《java性能优化笔记-RadnomAccess接口分析》//TODO
            • 使用缓冲区:BufferedInput和BufferedOutput在上面的文章中已经介绍过了,同样BufferedWrtier和BufferedReader效率也非常高。优先使用缓冲区。
            • 使用静态方法:静态方法不需要构建实例就可以直接使用,并且由于方法区gc很少回收,且jvm会缓存常用的类,所以一些常用工具类封装成static的性能会更高。而且要比函数重载更具有表达意义。
            • 使用设计模式:在对象比较大时可以使用原型模式替换new操作,尤其对象构造函数比较耗时时,可以直接使用原型模式clone对象,也可以使用apache的commons下的BeanUtil中的clone方法。同样在一些业务下,可以使用单例模式、享元模式、代理模式、工厂模式等常用的设计模式优化对象生成过程,提升性能。

衡量一个程序是否优质,可以从多个角度进行分析。其中,最常见的衡量标准是程序的时间复杂度、空间复杂度,以及代码的可读性、可扩展性。针对程序的时间复杂度和空间复杂度,想要优化程序代码,需要对数据结构算法有深入的理解,并且熟悉计算机系统的基本概念和原理;而针对代码的可读性和可扩展性,想要优化程序代码,需要深入理解软件架构设计,熟知并会应用合适的设计模式

首先,如今计算机系统的存储空间已经足够大了,达到了 TB 级别,因此相比于空间复杂度,时间复杂度是程序员首要考虑的因素。为了追求高性能,在某些频繁操作执行时,甚至可以考虑用空间换取时间。

 1. 针对日志记录的优化

 2. 针对数据库连接的优化

共享数据库连接。共有 5 次数据库连接操作,每次都需重新建立数据库连接,数据库插入操作完成之后又立即释放了,数据库连接没有被复用。为了做到共享数据库连接,可以通过单例模式 (Singleton Pattern)获得一个相同的数据库连接,每次数据库连接操作都共享这个数据库连接。这里没有使用数据库连接池(Database Connection Pool)是因为在程序只有少量的数据库连接操作,只有在大量并发数据库连接的时候才需要连接池。

共享数据库连接而得到的性能提升的原因是,数据库连接是一个耗时耗资源的操作,需要同远程计算机进行网络通信,建立 TCP 连接,还需要维护连接状态表,建立数据缓冲区。如果共享数据库连接,则只需要进行一次数据库连接操作,省去了多次重新建立数据库连接的时间。

3. 针对插入数据库记录的优化

4. 针对多线程的优化

使用多线程实现并发 / 并行。清空数据库表的操作,使用多线程而得到的性能提升的原因是,系统部署所在的服务器是多核多处理器的,使用多线程,给每个任务分配一个线程执行,可以充分地利用 CPU 计算资源。

6. 针对设计模式的优化,

回顾以上代码优化过程:关闭日志记录、共享数据库连接、使用预编译 SQL、使用 SQL 批处理、使用多线程实现并发 / 并行、使用 DAO 模式抽象出数据访问层,程序运行时间从最初的 100 秒左右降低到 15 秒以下,在性能上得到了很大的提升,同时也具有了更好的可读性和可扩展性。

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

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

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

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

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

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

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

要知道,当某个对象被定义为static的变量所引用,那么gc通常是不会回收这个对象所占有的堆内存的

foreach循环的底层实现原理就是迭代器Iterator,

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

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

5、使用带缓冲的输入输出流进行IO操作

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

26、顺序插入和随机访问比较多的场景使用ArrayList,元素删除和中间插入比较多的场景使用LinkedList

这个,理解ArrayList和LinkedList的原理就知道了

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

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

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

32使用最有效率的方式去遍历Map

public static void main(String[] args)  

{  

HashMap<String, String> hm = new HashMap<String, String>();  

hm.put(“”, “”);  

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() + “\t” + entry.getValue());  

}  

}  

避免instanceof非预期结果;

(instanceof用来判断一个对象是否是一个类的实例,只能用于对象的判断,不能用于基本类型的判断(编译不通过),instanceof操作符的左右操作数必须有继承或实现关系,否则编译会失败。例:null instanceof String返回值是false,instanceof特有规则,若左操作数是null,结果就直接返回false,不再运算右操作数是什么类)

建议19:断言绝对不是鸡肋;

(防御式编程中经常使用断言(Assertion)对参数和环境做出判断。断言是为调试程序服务的。两个特性:1、默认assert不启用;2、assert抛出的异常AssertionError是继承自Error的)。

建议21:用偶判断,不用奇判断;

(不要使用奇判断(i%2 == 1 ? "奇数" : "偶数"),使用偶判断(i%2 == 0 ? "偶数" : "奇数")。原因Java中的取余(%标识符)算法测试数据输入1 2 0 -1 -2,奇判断的时候,当输入-1时,也会返回偶数。

建议22:用整数类型处理货币;

(不要使用float或者double计算货币,因为在计算机中浮点数“有可能”是不准确的,它只能无限接近准确值,而不能完全精确。不能使用计算机中的二进制位来表示如0.4等的浮点数。解决方案:1、使用BigDecimal(优先使用);2、使用整型)。

建议44:推荐使用序列化实现对象的拷贝;

(通过序列化方式来处理,在内存中通过字节流的拷贝来实现深拷贝。使用此方法进行对象拷贝时需注意两点:1、对象的内部属性都是可序列化的;2、注意方法和属性的特殊修饰符,比如final、static、transient变量的序列化问题都会影响拷贝效果。一个简单办法,使用Apache下的commons工具包中的SerializationUtils类,直接使用更加简洁方便)。

建议56:自由选择字符串拼接方式;

(字符串拼接有三种方法:加号、concat方法及StringBuilder(或StringBuffer)的append方法。字符串拼接性能中,StringBuilder的append方法最快,concat方法次之,加号最慢。

第五章  数组和集合

建议60:性能考虑,数组是首选;

(性能要求较高的场景中使用数组替代集合)(基本类型在栈内存中操作,对象在堆内存中操作。数组中使用基本类型是效率最高的,使用集合类会伴随着自动装箱与自动拆箱动作,所以性能相对差一些)

建议64:多种最值算法,适时选择;

(最值计算时使用集合最简单,使用数组性能最优,利用Set集合去重,使用TreeSet集合自动排序)。

建议79:集合中的哈希码不要重复;

(列表查找不管是遍历查找、链表查找或者是二分查找都不够快。最快的是Hash开头的集合(如HashMap、HashSet等类)查找,原理:根据hashCode定位元素在数组中的位置。HashMap的table数组存储元素特点:1、table数组的长度永远是2的N次幂;2、table数组中的元素是Entry类型;3、table数组中的元素位置是不连续的;每个Entry都有一个next变量,它会指向下一个键值对,用来链表的方式来处理Hash冲突的问题。如果Hash码相同,则添加的元素都使用链表处理,在查找的时候这部分的性能与ArrayList性能差不多)。

第六章  枚举和注解

建议83:推荐使用枚举定义常量;

(在项目开发中,推荐使用枚举常量替代接口常量和类常量)(常量分为:类常量、接口常量、枚举常量;枚举常量优点:1、枚举常量更简单;2、枚举常量属于稳态性(不允许发生越界);3、枚举具有内置方法,values方法可以获取到所有枚举值;4、枚举可以自定义方法)。

建议85:小心switch带来的空值异常;

(使用枚举值作为switch(枚举类);语句的条件值时,需要对枚举类进行判断是否为null值。因为Java中的switch语句只能判断byte、short、char、int类型,JDK7可以判断String类型,使用switch语句判断枚举类型时,会根据枚举的排序值匹配。如果传入的只是null的话,获取排序值需要调用如season.ordinal()方法时会抛出NullPointerException异常)。

建议98:建议采用的顺序是List<T>,List<?>,List<Object>;

(1、List<T>是确定的某一个类型,编码者知道它是一个类型,只是在运行期才确定而已;2、List<T>可以进行读写操作,List<?>是只读类型,因为编译器不知道List中容纳的是什么类型的元素,无法增加、修改,但是能删除,List<Object>也可以读写操作,只是此时已经失去了泛型存在的意义了)。

适时选择不同的线程池来实现;

(Java的线程池实现从根本上来说只有两个:ThreadPoolExecutor类和ScheduledThreadPoolExecutor类,还是父子关系。为了简化并行计算,Java还提供了一个Executors的静态类,它可以直接生成多种不同的线程池执行器,比如单线程执行器、带缓冲功能的执行器等,归根结底还是以上两个类的封装类)。

建议128:预防线程死锁;

线程死锁(DeadLock)是多线程编码中最头疼问题,也是最难重现的问题,因为Java是单进程多线程语言。要达到线程死锁需要四个条件:1、互斥条件;2、资源独占条件;3、不剥夺条件;4、循环等待条件;按照以下两种方式来解决:1、避免或减少资源贡献;2、使用自旋锁,如果在获取自旋锁时锁已经有保持者,那么获取锁操作将“自旋”在那里,直到该自旋锁的保持者释放了锁为止)。

1、不要在循环条件中计算

2、尽可能把变量、方法声明为final static类型

3、缩小变量的作用范围,目的是加快GC的回收;

4、频繁字符串操作使用StringBuilder或StringBuffer

java程序优化的更多相关文章

  1. Java 程序优化 (读书笔记)

    --From : JAVA程序性能优化 (葛一鸣,清华大学出版社,2012/10第一版) 1. java性能调优概述 1.1 性能概述 程序性能: 执行速度,内存分配,启动时间, 负载承受能力. 性能 ...

  2. 从设计模式的角度看Java程序优化

    一.前言 Java程序优化有很多种渠道,比如jvm优化.数据库优化等等,但都是亡羊补牢的措施,如果能在设计程序架构时利用设计模式就把程序的短板解决,就能使程序更加健壮切容易维护迭代 二.常用的设计模式 ...

  3. Java程序优化的一些最佳实践(转)

    衡量程序的标准 衡量一个程序是否优质,可以从多个角度进行分析.其中,最常见的衡量标准是程序的时间复杂度.空间复杂度,以及代码的可读性.可扩展性.针对程序的时间复杂度和空间复杂度,想要优化程序代码,需要 ...

  4. 超大数据量操作 java程序优化[转载]

        一个表中有1000万以上的数据,要对其进行10万次以上的增删查改的操作,请问如何优化java程序对数据库的操作? 通过使用一些辅助性工具来找到程序中的瓶颈,然后就可以对瓶颈部分的代码进行优化. ...

  5. JAVA程序优化之字符串优化处理

    字符串是软件开发中最为重要的对象之一.通常,字符串对象或其等价对象(如char数组),在内存中总是占据了最大的空间块.因此如何高效地处理字符串,必将是提高系统整体性能的关键所在. 1.String对象 ...

  6. 记一次 java程序优化

    优化原因 环境中部署两个程序: web应用 tomcat   10G(webservice服务端,前端web服务) java应用               5G(webservice客户端,sock ...

  7. 对于JAVA程序优化的一些想法,读书有感.治疗强迫症良药

    在深入了解Java虚拟机里读到:在try{}块里面执行代码,比if(x!=null)效率要高,前提是被catch的几率很低的情况下. 但是 在Effective Java里读到:因为异常机制的设计初衷 ...

  8. Java程序优化细节

    1. 尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:    1).控制资源的使用,通过线程同 ...

  9. 第三章 Java程序优化(待续)

    字符串优化处理 String对象及其特点 String对象是java语言中重要的数据类型,但它并不是Java的基本数据类型.在C语言中,对字符串的处理最通常的做法是使用char数组,但这种方式的弊端是 ...

随机推荐

  1. iOS import framework头文件时报错could not build module xxx

    导致这个错误的原因有多种: 1.库的search paths没有设置对. 2.库里面某些头文件找不到. 而导致.h头文件找不到的原因也有多种: (a)search paths没设置对,文件可能换过位置 ...

  2. Visual Studio Package扩展——vsct文件简介

    首先我们使用向导生成一个package的扩展,里面就会发现一个vsct文件.vsct文件的全称是Visual Studio Command Table,它其实就是一个xml文件,通过一定的规则来描述v ...

  3. md5是哈希算法的改进加强,因为不同原始值可能hash结果一样,但md5则改善了用于验证消息完整性,不同md5值原始值也必将不一样

    md5是哈希算法的改进加强,因为不同原始值可能hash结果一样,但md5则改善了用于验证消息完整性,不同md5值原始值也必将不一样

  4. java的几个特性

    前言 本文主要介绍java语言的三个特性:类型协变和逆变,动态代理和静态代理,注解. 协变和逆变 借用Treant的博文,逆变与协变用来描述类型转换(type transformation)后的继承关 ...

  5. eclipse中jar包打断点

    eclipse中jar包打断点 1. 下载工具 链接:http://pan.baidu.com/s/1dEF5tqL 密码:md4m 2. 增加jadeclipse功能 把 net.sf.jadcli ...

  6. 【IntellJ IDEA】idea上 实现了Serializable接口,要自动生成serialVersionUID的方法

    需要点进setting ->搜索Inspections-->右侧选择java 下拉 进入Serialization issue--->勾选Serializable class wit ...

  7. ubuntu常用 命令

    卸载软件 ,比如chrome sudo apt-get remove google-chrome-stable

  8. ElasticSearch的Rest的访问方式查询总量

    由于安装ElasticSearch插件会影响ES的性能,所以会尽量减少ES的插件安装 可以通过ElasticSearch-Sql插件 然后将生成的执行参数拷贝 { "query": ...

  9. How to dynamically load directive into page

    https://stackoverflow.com/questions/23556398/how-to-dynamically-load-directive-into-page I have an h ...

  10. 【共享单车】—— React后台管理系统开发手记:员工管理之增删改查

    前言:以下内容基于React全家桶+AntD实战课程的学习实践过程记录.最终成果github地址:https://github.com/66Web/react-antd-manager,欢迎star. ...