Java 代码优化

本贴最后更新于 1688 天前,其中的信息可能已经斗转星移
  1. 尽量重用对象

比如:String 对象的使用中,出现字符串连接情况时应用 StringBuffer 代替。由于系统不仅要花时间生成对象,以后可能还需花时间对这些对象进行垃圾回收和处理。因此,生成过多的对象将会给程序的性能带来很大的影响。

  1. 尽量使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其他变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。

  1. 及时关闭以释放资源

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

  1. 对象使用完毕,应手动置成 null

JVM 回收垃圾的条件是:对象不在被引用;然而,JVM 的 GC 并非十分的机智,即使对象满足了垃圾回收的条件也不一定会被立即回收。所以,建议我们在对象使用完毕,应手动置成 null。

  1. 尽量减少对变量的重复计算

比如:

for(int i = 0; i < list.size; i ++) {

}

应替换为:

for(int i = 0,len = list.size(); i < len; i ++){

}

  1. 尽量采用 lazy loading 策略,即在需要的时候才开始创建

比如:

String str = “abc”;

if(i == 1) {

list.add(str);

}

应替换为:

if(i == 1) {

String str = “abc”;

list.add(str);

}

  1. 不要在循环中使用 try-catch,应该把其放在最外层。

  2. array(数组) 和 ArryList 的使用

array([]):最高效;但是其容量固定且无法动态改变;

ArrayList:容量可动态增长;但牺牲效率;

基于效率和类型检验,应尽可能使用 array,无法确定数组大小时才使用 ArrayList!

  1. 尽量使用 HashMap 和 ArrayList ,除非必要,否则不推荐使用 HashTable 和 Vector,后者由于使用同步机制,而导致了性能的开销

  2. 用 switch-case 替代冗长的 if-else-if

  3. 避免频繁地通过“+”运算符进行字符串拼接

原因:因为它会不断地生成新字符串对象,而生成字符串对象不仅耗时而且耗内存。应使用 StringBuilder 的 append 方法。

  1. 使用缓冲流 BufferedReader、BufferedWriter、BufferedInputStream 和 BufferedOutputStream 可以提升 IO 速度 20 倍。

  2. 尽量指定类、方法的 final 修饰符。

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

  1. 慎用异常。

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

  1. 如果能估计到待添加的内容长度,为底层以数组方式实现的集合、工具类指定初始长度。

比如 ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet 等,以 StringBuilder 为例,StringBuilder() 构造方法默认分配 16 个字符的空间,StringBuilder(int size) 构造方法默认分配 size 个字符的空间,StringBuilder(String str) 构造方法默认分配 16 个字符加 str.length() 个字符空间,所以可以通过类的构造方法来设定它的初始化容量,这样可以明显地提升性能。

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

  2. 乘法和除法使用移位操作

用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的,但是移位操作虽然快,可能会使代码不太好理解,因此最好加上相应的注释。

  1. 循环内不要不断创建对象引用。

  2. 不要将数组声明为 public static final

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

  1. 尽量在合适的场合使用单例

使用单例可以减轻加载的负担、缩短加载的时间、提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:

控制资源的使用,通过线程同步来控制资源的并发访问;

控制实例的产生,以达到节约资源的目的;

控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信;

  1. 尽量避免随意使用静态变量

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

  1. 及时清除不再需要的会话

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

  1. 实现 RandomAccess 接口的集合(比如 ArrayList)应当使用最普通的 for 循环而不是 foreach 循环来遍历。

这是 JDK 推荐给用户的,JDK API 对于 RandomAccess 接口的解释是实现 RandomAccess 接口用来表明其支持快速随机访问,此接口的主要目的是允许一般的算法更改其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。实际经验表明,实现 RandomAccess 接口的类实例,假如是随机访问的,使用普通 for 循环效率将高于使用 foreach 循环,反过来,如果是顺序访问的,则使用 Iterator 会效率更高。

(20)使用同步代码块替代同步方法。

尽量使用同步代码块,避免对那些不需要进行同步的代码也进行了同步,影响了代码执行效率。

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

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

(22)不要创建一些不使用的对象,不要导入一些不使用的类。

这毫无意义,如果代码中出现 "The value of the local variable i is not used"、"The import java.util is never used",那么请删除这些无用的内容,虽说没啥影响,但是有些时候编译期会报错,譬如没 import 用到的类被删掉了。

(23)程序运行过程中避免使用反射。

不建议在程序运行过程中使用,除非万不得已,尤其是频繁使用反射机制,特别是 Method 的 invoke 方法,如果确实有必要,一种建议性的做法是将那些需要通过反射加载的类在项目启动的时候通过反射实例化出一个对象并放入内存,用户只关心和对端交互的时候获取最快的响应速度,并不关心对端的项目启动花多久时间。

(24)使用数据库连接池和线程池。

这两个池都是用于重用对象的,前者可以避免频繁地打开和关闭连接,后者可以避免频繁地创建和销毁线程。

(25)使用带缓冲的输入输出流进行 IO 操作。

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

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

(27)不要让 public 方法中有太多的形参。

public 方法即对外提供的方法,如果给这些方法太多形参的话主要坏处是违反了面向对象的编程思想,Java 讲求一切都是对象,太多的形参和面向对象的编程思想并不契合,参数太多势必导致方法调用的出错概率增加。

(28)字符串变量和字符串常量 equals 的时候将字符串常量写在前面,这样可以避免空指针。

(29)建议使用 if (i == 1) 而不是 if (1 == i) 的方式。

因为有可能 == 会误写成 =,而在 C/C++ 中 if (i = 1) 是会出问题的,而 Java 会在编译时报错 "Type mismatch: cannot convert from int to boolean",但是,尽管 Java 的 if (i == 1) 和 if (1 == i) 在语义上没有任何区别,从阅读习惯上讲,建议使用前者会更好些。

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

本意是想打印出数组内容,却打出来的是对象信息,甚至有可能因为数组引用为空而导致空指针异常。对于集合 toString() 是可以打印出集合里面的内容的,因为集合的父类 AbstractCollections 重写了 Object 的 toString() 方法。

(31)不要对超出范围的基本数据类型做向下强制转型。

这很明确,譬如 long 转 int 是会存在潜在风险的。

(32)公用的集合类中不使用的数据一定要及时 remove 掉。

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

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

因为 String.valueOf() 方法底层调用了 Integer.toString() 方法,但是会在调用前做空判断;Integer.toString() 是直接调用;i + "" 底层使用了 StringBuilder 实现,先用 append 方法拼接,再用 toString() 方法获取字符串。

(34)使用最有效率的方式去遍历 Map。

遍历 Map 的方式有很多,通常场景下我们需要的是遍历 Map 中的 Key 和 Value,那么推荐使用的、效率最高的方式是 entrySet(),如果只是想遍历一下这个 Map 的 key 值则 keySet() 会比较合适一些。

(35)对资源的 close() 建议分开操作。

虽然有些麻烦,却能避免资源泄露,这其实和 try-catch 机制相关,各自分开 close 各自的 try-catch 就会互不影响,防止写在一个 try-catch 中因为一个异常了后面的释放不了。

(36)对于 ThreadLocal 在线程池场景使用前或者使用后一定要先 remove。

因为线程池技术做的是一个线程重用,这意味着代码运行过程中一条线程使用完毕并不会被销毁而是等待下一次的使用,而 Thread 类中持有 ThreadLocal.ThreadLocalMap 的引用,线程不销毁意味着上条线程 set 的 ThreadLocal.ThreadLocalMap 中的数据依然存在,那么在下一条线程重用这个 Thread 的时候很可能 get 到的是上条线程 set 的数据而不是自己想要的内容。这个问题非常隐晦,一旦出现这个原因导致的错误,没有相关经验或者没有扎实的基础非常难发现这个问题,因此在写代码的时候就要注意这一点,这将给你后续减少很多的工作量。

(37)切记以常量定义的方式替代魔鬼数字,魔鬼数字的存在将极大地降低代码可读性,字符串常量是否使用常量定义可以视情况而定。

(38)long 或者 Long 初始赋值时使用大写的 L 而不是小写的 l,因为字母 l 极易与数字 1 混淆,这个点非常细节,值得注意。

(39)所有重写的方法必须保留 @Override 注解。

这么做可以清楚地知道这个方法由父类继承而来,同时可以保证重写成功,此外在抽象类中对方法签名进行修改,实现类会马上报出编译错误。

(40)推荐使用 JDK7 中新引入的 Objects 工具类来进行对象的 equals 比较,直接 a.equals(b) 有空指针异常的风险。

(41)循环体内不要使用 "+" 进行字符串拼接,而直接使用 StringBuilder 不断 append。

因为每次虚拟机碰到 "+" 这个操作符对字符串进行拼接的时候会 new 出一个 StringBuilder,然后调用 append 方法,最后调用 toString() 方法转换字符串赋值给对象,所以循环多少次,就会 new 出多少个 StringBuilder() 来,这对于内存是一种浪费。

(42)不捕获 Java 类库中定义的继承自 RuntimeException 的运行时异常类。

异常处理效率低,RuntimeException 的运行时异常中绝大多数完全可以由程序员来规避,比如 ArithmeticException 可以通过判断除数是否为空来规避,NullPointerException 可以通过判断对象是否为空来规避,IndexOutOfBoundsException 可以通过判断数组/字符串长度来规避,ClassCastException 可以通过 instanceof 关键字来规避,ConcurrentModificationException 可以使用迭代器来规避。

(43)静态类、单例类、工厂类将它们的构造函数置为 private。

这是因为静态类、单例类、工厂类这种类本来我们就不需要外部将它们 new 出来,将构造函数置为 private 之后,保证了这些类不会产生实例对象

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • WebComponents

    Web Components 是 W3C 定义的标准,它给了前端开发者扩展浏览器标签的能力,可以方便地定制可复用组件,更好的进行模块化开发,解放了前端开发者的生产力。

    1 引用
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖
  • Openfire

    Openfire 是开源的、基于可拓展通讯和表示协议 (XMPP)、采用 Java 编程语言开发的实时协作服务器。Openfire 的效率很高,单台服务器可支持上万并发用户。

    6 引用 • 7 回帖 • 94 关注
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 351 关注
  • 京东

    京东是中国最大的自营式电商企业,2015 年第一季度在中国自营式 B2C 电商市场的占有率为 56.3%。2014 年 5 月,京东在美国纳斯达克证券交易所正式挂牌上市(股票代码:JD),是中国第一个成功赴美上市的大型综合型电商平台,与腾讯、百度等中国互联网巨头共同跻身全球前十大互联网公司排行榜。

    14 引用 • 102 回帖 • 376 关注
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    83 引用 • 37 回帖 • 1 关注
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    325 引用 • 1395 回帖
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    287 引用 • 4484 回帖 • 669 关注
  • V2EX

    V2EX 是创意工作者们的社区。这里目前汇聚了超过 400,000 名主要来自互联网行业、游戏行业和媒体行业的创意工作者。V2EX 希望能够成为创意工作者们的生活和事业的一部分。

    17 引用 • 236 回帖 • 327 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 764 关注
  • 学习

    “梦想从学习开始,事业从实践起步” —— 习近平

    169 引用 • 506 回帖
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖 • 19 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 664 关注
  • 酷鸟浏览器

    安全 · 稳定 · 快速
    为跨境从业人员提供专业的跨境浏览器

    3 引用 • 59 回帖 • 26 关注
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    91 引用 • 751 回帖 • 2 关注
  • 职场

    找到自己的位置,萌新烦恼少。

    127 引用 • 1705 回帖 • 1 关注
  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    4 引用 • 16 回帖 • 5 关注
  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    25 引用 • 7 回帖 • 173 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    247 引用 • 1348 回帖
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    729 引用 • 1327 回帖
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 22 关注
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 585 关注
  • 以太坊

    以太坊(Ethereum)并不是一个机构,而是一款能够在区块链上实现智能合约、开源的底层系统。以太坊是一个平台和一种编程语言 Solidity,使开发人员能够建立和发布下一代去中心化应用。 以太坊可以用来编程、分散、担保和交易任何事物:投票、域名、金融交易所、众筹、公司管理、合同和知识产权等等。

    34 引用 • 367 回帖
  • MySQL

    MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。

    690 引用 • 535 回帖
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    524 引用 • 4601 回帖 • 700 关注
  • SendCloud

    SendCloud 由搜狐武汉研发中心孵化的项目,是致力于为开发者提供高质量的触发邮件服务的云端邮件发送平台,为开发者提供便利的 API 接口来调用服务,让邮件准确迅速到达用户收件箱并获得强大的追踪数据。

    2 引用 • 8 回帖 • 483 关注