程序代码优化要点:

  • 字符串优化:分析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. 廖雪峰的git教程

    http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000

  2. httpanalyzer 抓包时会更换证书

    今天是要httpanalyzer时发现,在启用的时候,如果当前网络地址是https的话,那么当前证书会被更换掉,效果如下: 而原来的证书如下: 所以,请注意,如果你的请求有严格的证书验证,那么证书验证 ...

  3. CURL简单使用

    学习地址:https://yq.aliyun.com/articles/33262 curl的简单使用步骤 要使用cURL来发送url请求,具体步骤大体分为以下四步: 1.初始化2.设置请求选项3.执 ...

  4. POJ 3486 &amp; HDU 1913 Computers(dp)

    题目链接:PKU:HDU: PKU:http://poj.org/problem?id=3486 HDU:pid=1913" target="_blank">htt ...

  5. 【Hadoop】Hadoop DataNode节点超时时间设置

    hadoop datanode节点超时时间设置 datanode进程死亡或者网络故障造成datanode无法与namenode通信,namenode不会立即把该节点判定为死亡,要经过一段时间,这段时间 ...

  6. [转载]Lenovo E431 装Centos7无线驱动安装

    FROM:http://huangyandong.blog.51cto.com/1396940/1613096 查看无线网卡型号 lspci |grep Network    #为BCM43142网卡 ...

  7. 用word2vec对语料进行训练

    在Linux上安装好word2vec, 进入trunk文件夹,把分词后的语料文件放在trunk文件夹内,执行:./word2vec -train tt.txt -output vectors.bin ...

  8. Ubuntu使用日志2(在Eclipse中搭建C++交叉编译环境)

    Release用交叉编译:arm-none-linux-gnueabi-gcc. 搭建步骤: 1).在Project->Properties->C/C++ Build->Settin ...

  9. ElasticSearch Java Api-删除索引

    删除可以是删除整个索引库,也可以根据文档id删除索引库下的文档,还可以通过query查询条件删除所有符合条件的数据. 一.删除整个索引库 下面的例子会删除indexName索引: DeleteInde ...

  10. [Tools] Support VS Code Navigation and Autocomplete Based on Webpack Aliases with jsconfig.json

    It's common to setup Webpack aliases to make imports much more convenient, but then you lose the abi ...