35个Java 代码性能优化总结

前言
代码优化,一个很重要的课题。可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用,但是,吃的小虾米一多之后,鲸鱼就被喂饱了。代码优化也是一样,如果项目着眼于尽快无BUG上线,那么此时可以抓大放小,代码的细节可以不精打细磨;但是如果有足够的时间开发、维护代码,这时候就必须考虑每个可以优化的细节了,一个一个细小的优化点累积起来,对于代码的运行效率绝对是有提升的。

代码优化的目标是:

1、减小代码的体积
2、提高代码运行的效率

代码优化细节

1、尽量指定类、方法的final修饰符
带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String,整个类都是final的。为类指定final修饰符可以让类不可以被继承,为方法指定final修饰符可以让方法不可以被重写。如果指定了一个类为final,则该类所有的方法都是final的。Java编译器会寻找机会内联所有的final方法,内联对于提升Java运行效率作用重大,具体参见Java运行期优化。此举能够使性能平均提高50%。

2、尽量重用对象
特别是String对象的使用,出现字符串连接时应该使用StringBuilder/StringBuffer代替。由于Java虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理,因此,生成过多的对象将会给程序的性能带来很大的影响。
3、尽可能使用局部变量
调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快,其他变量,如静态变量、实例变量等,都在堆中创建,速度较慢。另外,栈中创建的变量,随着方法的运行结束,这些内容就没了,不需要额外的垃圾回收。

4、及时关闭流
Java编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,及时关闭以释放资源。因为对这些大对象的操作会造成系统大的开销,稍有不慎,将会导致严重的后果。

5、尽量减少对变量的重复计算
明确一个概念,对方法的调用,即使方法中只有一句语句,也是有消耗的,包括创建栈帧、调用方法时保护现场、调用方法完毕时恢复现场等。所以例如下面的操作:
for (int i = 0; i < list.size(); i++){...}
建议替换为:
for (int i = 0, int length = list.size(); i < length; i++){...}
这样,在list.size()很大的时候,就减少了很多的消耗

6、尽量采用懒加载的策略,即在需要的时候才创建
例如:
String str = "aaa";if (i == 1){list.add(str);}
建议替换为:
if (i == 1){String str = "aaa";list.add(str);}

7、慎用异常
异常对性能不利。抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为fillInStackTrace()的本地同步方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。

8、不要在循环中使用try…catch…,应该把其放在最外层
除非不得已。如果毫无理由地这么写了,只要你的领导资深一点、有强迫症一点,八成就要骂你为什么写出这种垃圾代码来了

9、如果能估计到待添加的内容长度,为底层以数组方式实现的集合、工具类指定初始长度
比如ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet等等,以StringBuilder为例:
(1)StringBuilder()      // 默认分配16个字符的空间
(2)StringBuilder(int size)  // 默认分配size个字符的空间
(3)StringBuilder(String str) // 默认分配16个字符+str.length()个字符空间
可以通过类(这里指的不仅仅是上面的StringBuilder)的来设定它的初始化容量,这样可以明显地提升性能。比如StringBuilder吧,length表示当前的StringBuilder能保持的字符数量。因为当StringBuilder达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,无论何时只要StringBuilder达到它的最大容量,它就不得不创建一个新的字符数组然后将旧的字符数组内容拷贝到新字符数组中—-这是十分耗费性能的一个操作。试想,如果能预估到字符数组中大概要存放5000个字符而不指定长度,最接近5000的2次幂是4096,每次扩容加的2不管,那么:
(1)在4096 的基础上,再申请8194个大小的字符数组,加起来相当于一次申请了12290个大小的字符数组,如果一开始能指定5000个大小的字符数组,就节省了一倍以上的空间
(2)把原来的4096个字符拷贝到新的的字符数组中去
这样,既浪费内存空间又降低代码运行效率。所以,给底层以数组实现的集合、工具类设置一个合理的初始化容量是错不了的,这会带来立竿见影的效果。但是,注意,像HashMap这种是以数组+链表实现的集合,别把初始大小和你估计的大小设置得一样,因为一个table上只连接一个对象的可能性几乎为0。初始大小建议设置为2的N次幂,如果能估计到有2000个元素,设置成new HashMap(128)、new HashMap(256)都可以。

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

11、乘法和除法使用移位操作
例如:
for (val = 0; val < 100000; val += 5){a = val * 8;b = val / 2;}
用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的,因此建议修改为:
for (val = 0; val < 100000; val += 5){a = val << 3;b = val >> 1;}
移位操作虽然快,但是可能会使代码不太好理解,因此最好加上相应的注释。

12、循环内不要不断创建对象引用
例如:
for (int i = 1; i <= count; i++){Object obj = new Object();}
这种做法会导致内存中有count份Object对象引用存在,count很大的话,就耗费内存了,建议为改为:
Object obj = null;for (int i = 0; i <= count; i++) { obj = new Object(); }
这样的话,内存中只有一份Object对象引用,每次new Object()的时候,Object对象引用指向不同的Object罢了,但是内存中只有一份,这样就大大节省了内存空间了。

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

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

15、不要将数组声明为public static final
因为这毫无意义,这样只是定义了引用为static final,数组的内容还是可以随意改变的,将数组声明为public更是一个安全漏洞,这意味着这个数组可以被外部类所改变

16、尽量在合适的场合使用单例
使用单例可以减轻加载的负担、缩短加载的时间、提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:
(1)控制资源的使用,通过线程同步来控制资源的并发访问
(2)控制实例的产生,以达到节约资源的目的
(3)控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信

17、尽量避免随意使用静态变量
要知道,当某个对象被定义为static的变量所引用,那么gc通常是不会回收这个对象所占有的堆内存的,如:
/* Java学习交流QQ群:58980××× 我们一起学Java! */public class A{ private static B b = new B();}
此时静态变量b的生命周期与A类相同,如果A类不被卸载,那么引用B指向的B对象会常驻内存,直到程序终止

18、及时清除不再需要的会话
为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多的会话时,如果内存不足,那么操作系统会把部分数据转移到磁盘,应用服务器也可能根据MRU(最近最频繁使用)算法把部分不活跃的会话转储到磁盘,甚至可能抛出内存不足的异常。如果会话要被转储到磁盘,那么必须要先被序列化,在大规模集群中,对对象进行序列化的代价是很昂贵的。因此,当会话不再需要时,应当及时调用HttpSession的invalidate()方法清除会话。

19、实现RandomAccess接口的集合比如ArrayList,应当使用最普通的for循环而不是foreach循环来遍历
这是JDK推荐给用户的。JDK API对于RandomAccess接口的解释是:实现RandomAccess接口用来表明其支持快速随机访问,此接口的主要目的是允许一般的算法更改其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。实际经验表明,实现RandomAccess接口的类实例,假如是随机访问的,使用普通for循环效率将高于使用foreach循环;反过来,如果是顺序访问的,则使用Iterator会效率更高。可以使用类似如下的代码作判断:
if (list instanceof RandomAccess){ for (int i = 0; i < list.size(); i++){}}else{Iterator<span font-size:16px;line-height:2;"="" style="font-family: "Microsoft YaHei"; font-size: 16px; line-height: 2;">1、整型默认的数据类型是int,long l = 12345678901234L,这个数字已经超出了int的范围了,所以最后有一个L,表示这是一个long型数。顺便,浮点型的默认类型是double,所以定义float的时候要写成”"float f = 3.5f”
2、接下来再写一句”int ii = l + i;”会报错,因为long + int是一个long,不能赋值给int

32、公用的集合类中不使用的数据一定要及时remove掉
如果一个集合类是公用的(也就是说不是方法里面的属性),那么这个集合里面的元素是不会自动释放的,因为始终有引用指向它们。所以,如果公用集合里面的某些数据不使用而不去remove掉它们,那么将会造成这个公用集合不断增大,使得系统有内存泄露的隐患。

33、把一个基本数据类型转为字符串,基本数据类型.toString()是最快的方式、String.valueOf(数据)次之、数据+”"最慢
把一个基本数据类型转为一般有三种方式,我有一个Integer型数据i,可以使用i.toString()、String.valueOf(i)、i+”"三种方式,三种方式的效率如何,看一个测试:
public static void main(String[] args){ int loopTime = 50000;Integer i = 0; long startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++){String str = String.valueOf(i);}System.out.println("String.valueOf():" + (System.currentTimeMillis() - startTime) + "ms");startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++){String str = i.toString();}System.out.println("Integer.toString():" + (System.currentTimeMillis() - startTime) + "ms");startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++){String str = i + "";}System.out.println("i + "":" + (System.currentTimeMillis() - startTime) + "ms");}
运行结果为:
String.valueOf():11ms Integer.toString():5ms i + "":25ms
所以以后遇到把一个基本数据类型转为String的时候,优先考虑使用toString()方法。至于为什么,很简单:
1、String.valueOf()方法底层调用了Integer.toString()方法,但是会在调用前做空判断
2、Integer.toString()方法就不说了,直接调用了
3、i + “”底层使用了StringBuilder实现,先用append方法拼接,再用toString()方法获取字符串
三者对比下来,明显是2最快、1次之、3最慢

34、使用最有效率的方式去遍历Map
遍历Map的方式有很多,通常场景下我们需要的是遍历Map中的Key和Value,那么推荐使用的、效率最高的方式是:
public static void main(String[] args){HashMap();hm.put("111", "222");Set<map.entry> iter = entrySet.iterator(); while (iter.hasNext()){Map.Entry
<span font-size:16px;line-height:2;"="" data-filtered="filtered" style="font-family: 微软雅黑; font-size: 12px; white-space: normal; background-color: rgb(255, 255, 255);"></map.entry
<map.entry</map.entry
欢迎Java工程师朋友们加入Java爬坑之路:860113481

群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

转载于:https://blog.51cto.com/13732225/2126780

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/277816.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL讲义

1 MySQL基础知识 瑞典MySQL AB公司开发&#xff0c;由SUN收购&#xff0c;而后SUN被甲骨文并购&#xff0c;目前属于Oracle公司。 MySQL是一种关联数据库管理系统 由于其体积小、速度快、总体拥有成本低、MySQL软件采用了双授权政策&#xff0c;分为社区版和企业版。 …

Teams Bot App Manifest 文件解析

这篇文章我们继续以 Hello World Bot 这个 sample 来讲一下 manifest template。 实际上在 Teams app 开发的时候&#xff0c;有 manifest 的概念&#xff0c;manifest 是用来说明这个 teams app 的一些基本信息和配置信息&#xff0c;比如 app 的名字&#xff0c;app有哪些能…

[Dart] Flutter开发中的几个常用函数

几个Flutter开发中的常用函数 /** 返回当前时间戳 */static int currentTimeMillis() {return new DateTime.now().millisecondsSinceEpoch;}/** 复制到剪粘板 */static copyToClipboard(final String text) {if (text null) return;Clipboard.setData(new ClipboardData(text…

Cordova入门系列(三)Cordova插件调用 转发 https://www.cnblogs.com/lishuxue/p/6018416.html...

Cordova入门系列&#xff08;三&#xff09;Cordova插件调用 版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请注明出处 上一章我们介绍了cordova android项目是如何运行的&#xff0c;这一章我们介绍cordova的核心内容&#xff0c;插件的调用。演示一个例子&#xf…

clojure with postgres

主要关注访问pg。不关心其他db 1 clojure.java.jdbc https://github.com/clojure/java.jdbchttp://clojure-doc.org/articles/ecosystem/java_jdbc/reusing_connections.html这个最广&#xff0c;需要配合不同DB[org.clojure/java.jdbc "0.7.9"] [org.postgresql/pos…

lua入门

https://en.blog.nic.cz/2015/08/12/embedding-luajit-in-30-minutes-or-so/

shell脚本传可选参数 getopts 和 getopt的方法

写了一个shell脚本&#xff0c;需要向shell脚本中传参数供脚本使用&#xff0c;达到的效果是传的参数可以是可选参数 下面是一个常规化的shell脚本&#xff1a; echo "执行的文件名为: $0";echo "第一个参数名为: $1";echo "第二个参数名为: $2"…

Teams Tab App 代码深入浅出 - 配置页面

上一篇文章我们使用Teams Toolkit 来创建、运行 tab app。这篇文章我们深入来分析看一下tab app 的代码。 先打开代码目录&#xff0c;可以看到在 src 目录下有入口文件 index.tsx&#xff0c;然后在 components 目录下有更多的一些 tsx 文件&#xff0c;tsx 是 typescript的一…

labelme标注的json文件数据转成coco数据集格式(可处理目标框和实例分割)

这里主要是搬运一下能找到的 labelme标注的json文件数据转成coco数据集格式&#xff08;可处理目标框和实例分割&#xff09;的代码&#xff0c;以供需要时参考和提供相关帮助。 1、官方labelme实现 如下是labelme官方网址&#xff0c;提供了源代码&#xff0c;以及相关使用方…

EpSON TM-82II驱动在POS系统上面安装问题处理

按照品牌名称&#xff0c;在网上下载的安装包为apstmt82.rar 下面讲解一下&#xff0c;如何的解决爱普生打印机在POS机器上面的安装问题&#xff0c;这个算是一个比较奇特的故障问题&#xff0c;不像其它的新北冰洋&#xff08;SN3C&#xff09;的U80_U80II&#xff0c;SeNor的…

打印图片的属性和实现另存图片功能以及使用numpy

上一篇我们已经学了如何读取图片的功能了以及和opencv的环境搭建了&#xff0c;今天接着来学习&#xff0c;哈哈哈&#xff0c;今天刚好五一&#xff0c;也没闲着&#xff0c;继续学习。 1、 首先我们来实现打印出图片的一些属性功能&#xff0c; 先来看一段代码&#xff1a; 1…

Ubuntu 18.04下命令安装VMware Tools

2019独角兽企业重金招聘Python工程师标准>>> sudo apt-get upgrade sudo apt-get install open-vm-tools-desktop -y sudo reboot 转载于:https://my.oschina.net/u/574036/blog/1829455

phpstorm PHP language level无法选择

phpstorm PHP7新特性一直提示红色波浪线&#xff0c;应该是没有设置PHP 版本&#xff0c;但是打开PHPstorm---preference--lannguage&frameworks--PHP &#xff0c; 发现PHP language level 无法选择PHP7.2 &#xff0c;查看旁边的提示信息说是同步了composer 的原因&#…

Qfile

打开方式&#xff1a; 1 void AddStudents::write_to_file(QString src){2 QFile file("stu.txt");3 if (!file.open(QIODevice::Append | QIODevice::Text)){4 QMessageBox::critical(this,"打开文件错误","确认");5 r…

多层装饰器、带参数装饰器

# 带参数的装饰器 # import time # FLAGE False # 加个标志位&#xff0c;使全部的装饰器可以失效或有效 # def timmer_out(flag): # def timmer(func): # def inner(*args,**kwargs): # if flag: # start time.time() # …

IDEA svn 菜单不见了,解决方法

2019独角兽企业重金招聘Python工程师标准>>> 参考地址: http://www.cnblogs.com/signheart/p/193448a98f92bd0cc064dbd772dd9f48.html,我是第二种方法解决的! 转载于:https://my.oschina.net/liuchangng/blog/1829679

苏宁易购:Hadoop失宠前提是出现更强替代品

在笔者持续调研国内Hadoop生态系统生存现状的同时&#xff0c;KDnuggets发布的2018年数据科学和机器学习工具调查报告再次将“Hadoop失宠”言论复活。报告一出&#xff0c;“Hadoop被抛弃”几个字瞬时成为各大标题党的最爱&#xff0c;充斥在不同的新闻平台。这些报告和数据是否…

VS2017生成一个简单的DLL文件 和 LIB文件——C语言

下面我们将用两种不同的姿势来用VS2017生成dll文件&#xff08;动态库文件&#xff09;和lib文件&#xff08;静态库文件&#xff09;&#xff0c;这里以C语言为例&#xff0c;用最简单的例子&#xff0c;来让读者了解如何生成dll文件&#xff08;动态库文件&#xff09; 生成动…

Hive数据类型及文本文件数据编码

本文参考Apache官网&#xff0c;更多内容请参考&#xff1a;https://cwiki.apache.org/confluence/display/Hive/LanguageManualTypes 1. 数值型 类型支持范围TINYINT1-byte signed integer, from -128 to 127SMALLINT2-byte signed integer, from -32,768 to 32,767INT/INTEGE…

Python绘图Turtle库详解

转载&#xff1a;https://blog.csdn.net/zengxiantao1994/article/details/76588580 Turtle库是Python语言中一个很流行的绘制图像的函数库&#xff0c;想象一个小乌龟&#xff0c;在一个横轴为x、纵轴为y的坐标系原点&#xff0c;(0,0)位置开始&#xff0c;它根据一组函数指令…