详解Java代码常见优化方案

首先,良好的编码规范非常重要。在 java 程序中,访问速度、资源紧张等问题的大部分原因,都是代码不规范造成的。

单例的使用场景

单例模式对于减少资源占用、提高访问速度等方面有很多好处,但并不是所有场景都适用于单例。

简单来说,单例主要适用于以下三个方面:

  1. 多线程场景,通过线程同步来控制资源的并发访问。
  2. 多线程场景,控制数据共享,让多个不相关的进程或线程之间实现通信(通过访问同一资源来控制)。
  3. 控制实例的产生,单例只实例化一次,以达到节约资源的目的;

不可随意使用静态变量

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

示例如下:

public class A {
  private static B b = new B();
}

此时静态变量 b 的生命周期与 A 类同步,即如果 A 类不卸载,b 对象会常驻内存,直到程序终止。

创建 Java 对象使用注意事项

根据业务使用场景,尽量避免在循环中 new 对象。

这是因为系统要花费时间来创建对象,而且还要花时间对这些对象进行管理和垃圾回收。所以在可以控制的范围内,尽量保证最大限度地重用对象,最好能用基本的数据类型或数组来替代对象。

final 修饰符使用注意事项

final 修饰符的类是不可派生的,即不可被继承。在 java 核心代码中,有很多 被 final 修饰的类,如 java.lang.String 类。

如果一个类是 final 的,则该类所有方法都是 final 的。java 编译器会寻找机会内联(inline)所有的 final 方法,这与具体的编译器实现有关。这样做能够使性能平均提高 50% 。

示例如下:

class A {
  public void setSize (int size) {
    this.size = size;
  }
  private int size;
}
// setSize 方法变为 final ,性能更好
class A {
  final public void setSize (int size) {
    this.size = size;
  }
  private int size;
}

让访问实例变量的 getter/setter 方法变成 final:简单的 getter/setter 方法应该被置成 final ,这会告诉编译器此方法不会被重载,可以变成 ”inlined” 。

局部变量使用规范

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

处理好包装类型和基本类型的使用场所

基本类型:byte,short,int,long,float,double,char,boolean
对应包装类型:Byte,Short,Int,Long,Float,Double,Character,Boolean

基本类型和包装类型在使用过程中可以相互转换,但它们所产生的内存区域是完全不同的。基本类型的产生和处理都在栈中处理,包装类型是引用类型,其对象是在堆中产生实例。

在集合类对象,有对象方面需要的处理使用包装类型合适,其他情况的处理提倡使用基本类型。

使用基本数据类型代替对象

示例如下:

String s1 = "hello";

这种方式会创建一个 “hello” 字符串,而且 JVM 的字符缓存池会缓存这个字符串。

String s2 = new String("hello");

这种方式除了创建字符串外,s2 所引用的 String 对象底层包含一个 char[] 数组,其中依次存放了 h,e,l,l,o 。

synchronized 使用规范

实现同步需要很大的系统开销作为代价的,甚至可能造成死锁。所以尽量避免无谓的同步控制。

synchronize 方法被调用时会直接把当前对象锁住,在该方法执行完之前其他线程无法调用当前对象的其它方法。比较灵活的用法是使用代码块同步代替在方法中同步。

finalize使用规范

不要将资源清理放在 finalize 方法中完成,这种方法也很少使用。

由于 GC 的工作量很大,尤其是回收 Young 代内存时,大都会引起应用程序暂停。如果选择使用 finalize 方法进行资源清理,会导致 GC 负担加大,程序运行效率变差。

不需要线程同步,应尽量使用 HashMap 、ArrayList

HashTable 、Vector 等使用了同步机制,导致降低。

HashMap 使用规范

创建一个比较大的 hashMap 时,应该使用带有参数的构造函数创建对象。

示例如下:

public HashMap(int initialCapacity, float loadFactor);

hash 扩容是一件很耗费性能的事,默认构造函数创建的对象的 initialCapacity 只有 16,loadFactor 是 0.75 ,最好准确的估计所需要的最佳大小。同样对于 Hashtable ,Vectors 也是如此。

减少对变量的重复计算

for (int i = 0; i < list.size(); i++) {...}
// 应该改为
for (int i=0, l=list.size(); i < l; i++) {...}

避免不必要的创建对象

A a = new A();
if (i == 1) {
  list.add(a);
}
// 应该改为
if (i == 1) {
  A a = new A();
  list.add(a);
}

finally 使用规范

在 try-catch 里,使用到的资源要能够被释放,以避免资源泄漏,这最好在 finally 块中去做。无论程序执行是否有异常,finally 里的代码总是会执行的,这样可以确保资源的正确关闭。

StringBuffer使用规范

StringBuffer 的无参构造函数会创建一个默认 16 的字符数组。在使用过程中,如果数组长度超出 16 ,就要重新分配内存,创建一个容量更大的数组,并将原先的数组复制过来,再丢弃旧的数组。

在多数情况下,可以在创建 StringBuffer 的时候指定大小,避免了在容量不够的时候自动增长,以提高性能。

StringBuffer sb= new StringBuffer(int capacity);

显式释放空间让 gc 回收对象

多数情况下,方法内部的局部引用变量所引用的对象会随着方法结束而变成垃圾被回收。因此在程序中无需将局部引用变量显式设为 null 。

示例如下:

void gcTest1() {
  Object obj = new Object();
  ……
  obj = null;
}

随着方法 gcTest1() 的执行完成,程序中局部引用变量 obj 的作用域就结束了,这时没有必要执行 obj = null 。

反例如下:

void gcTest2(){
  Object obj = new Object();
  ……
  obj = null;
  //耗时,耗内存操作
  ……
}

此时需要尽早释放不再使用的空间,执行 obj = null 显式释放局部引用变量 obj 。

二维数组使用规范

二维数据占用的内存空间大概是一维数组的 10 倍以上。

split 使用场景

尽量避免使用 split 。split 使用正则表达式,效率比较低,如果是频繁的调用将会耗费大量资源。

如果确实需要频繁的调用 split ,使用 apache 的 StringUtils.split(string,char) 较好 ,可以缓存结果。

ArrayList 和 LinkedList 使用规范

对于线性表及链表,随机查询的操作ArrayList 优于 LinkedList ,LinkedList 需要移动指针。增加删除的操作 LinkedList 优于 ArrayList ,ArrayList 需要移动数据。

System.arraycopy() 使用规范

尽量使用 System.arraycopy() 复制数组,它要比通过循环来复制数组快的多。

缓存对象

将经常使用的对象进行缓存时,可以使用数组或者 HashMap 等容器来缓存。这种方式需要自己管理这些容器,可能导致系统占用过多的缓存,性能下降。

也可以使用一些第三方的开源工具,如 EhCache 、Oscache 进行缓存,他们基本都实现了 FIFO/FLU 等缓存算法。

尽量避免非常大的内存分配

有的问题不是由于堆内存不够造成的,而是因为内存分配失败造成的。(gc会进行内存碎片整理)

如果分配的内存块都必须是连续的,随着堆越来越满,找到较大的连续块会越来越困难。

try/catch 使用场景

不要在循环中使用 try/catch 语句,应该把 try/catch 放在循环最外层。

以上所述是小编给大家介绍的Java代码常见优化方案详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • java web在高并发和分布式下实现订单号生成唯一的解决方案

    方案一: 如果没有并发,订单号只在一个线程内产生,那么由于程序是顺序执行的,不同订单的生成时间戳正常不同,因此用时间戳+随机数(或自增数)就可以区分各个订单.如果存在并发,且订单号是由一个进程中的多个线程产生的,那么只要把线程ID添加到序列号中就可以保证订单号唯一.如果存在并发,且订单号是由同一台主机中的多个进程产生的,那么只要把进程ID添加到序列号中就可以保证订单号唯一.如果存在并发,且订单号是由不同台主机产生的,那么MAC地址.IP地址或CPU序列号等能够区分主机的号码添加到序列号中就可以保

  • Java中实现文件上传下载的三种解决方案(推荐)

    java文件上传与文件下载是程序开发中比较常见的功能,下面通过本文给大家介绍Java中实现文件上传下载的三种解决方案,具体详情如下所示: 第一点:Java代码实现文件上传 FormFile file=manform.getFile(); String newfileName = null; String newpathname=null; String fileAddre="/numUp"; try { InputStream stream = file.getInputStream(

  • java实现乘地铁方案的最优选择(票价,距离)

    初始问题描述: 已知2条地铁线路,其中A为环线,B为东西向线路,线路都是双向的.经过的站点名分别如下,两条线交叉的换乘点用T1.T2表示.编写程序,任意输入两个站点名称,输出乘坐地铁最少需要经过的车站数量(含输入的起点和终点,换乘站点只计算一次). 地铁线A(环线)经过车站:A1 A2 A3 A4 A5 A6 A7 A8 A9 T1 A10 A11 A12 A13 T2 A14 A15 A16 A17 A18 地铁线B(直线)经过车站:B1 B2 B3 B4 B5 T1 B6 B7 B8 B9 

  • Java Web开发防止多用户重复登录的完美解决方案

    目前web项目中,很多情况都是可以让同一个账户信息在不同的登录入口登录这次,这样子就不那么美好了. 推荐阅读: Java 多用户登录限制的实现方法 现在有两种解决方案: 1.将用户的登录信息用一个标志位的字段保存起来,每次登录成功就标记1,注销登录就标记为0,当标记为1的时候不允许别人登录. 2.将用户的登录信息保存在application内置作用域内, 然后利用session监听器监听每一个登录用户的登录情况. 很显然,第一种方式 每次登录 都需要操作数据库,多了一些不必要的性能开销,而且在登

  • Java找不到或无法加载主类及编码错误问题的解决方案

    先给出具体代码(当前目录为:D:\pro): package org.test; public class TestJava{ public static void main(String args[]){ System.out.println("Hello World!!!"); System.out.println("你好,Java!!"); } } 1. cmd 窗口运行时出现"找不到或无法加载主类"问题: D:\pro>javac

  • 详解Java代码常见优化方案

    首先,良好的编码规范非常重要.在 java 程序中,访问速度.资源紧张等问题的大部分原因,都是代码不规范造成的. 单例的使用场景 单例模式对于减少资源占用.提高访问速度等方面有很多好处,但并不是所有场景都适用于单例. 简单来说,单例主要适用于以下三个方面: 多线程场景,通过线程同步来控制资源的并发访问. 多线程场景,控制数据共享,让多个不相关的进程或线程之间实现通信(通过访问同一资源来控制). 控制实例的产生,单例只实例化一次,以达到节约资源的目的: 不可随意使用静态变量 当某个对象被定义为 s

  • 详解java代码中init method和destroy method的三种使用方式

    在java的实际开发过程中,我们可能常常需要使用到init method和destroy method,比如初始化一个对象(bean)后立即初始化(加载)一些数据,在销毁一个对象之前进行垃圾回收等等. 周末对这两个方法进行了一点学习和整理,倒也不是专门为了这两个方法,而是在巩固spring相关知识的时候提到了,然后感觉自己并不是很熟悉这个,便好好的了解一下. 根据特意的去了解后,发现实际上可以有三种方式来实现init method和destroy method. 要用这两个方法,自然先要知道这两

  • Java逃逸分析详解及代码示例

    概念引入 我们都知道,Java 创建的对象都是被分配到堆内存上,但是事实并不是这么绝对,通过对Java对象分配的过程分析,可以知道有两个地方会导致Java中创建出来的对象并一定分别在所认为的堆上.这两个点分别是Java中的逃逸分析和TLAB(Thread Local Allocation Buffer)线程私有的缓存区. 基本概念介绍 逃逸分析,是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法.通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的

  • Java数据结构之KMP算法详解以及代码实现

    目录 暴力匹配算法(Brute-Force,BF) 概念和原理 next数组 KMP匹配 KMP全匹配 总结 我们此前学了前缀树Trie的实现原理以及Java代码的实现.Trie树很好,但是它只能基于前缀匹配实现功能.但是如果我们的需求是:一个已知字符串中查找子串,并且子串并不一定符合前缀匹配,那么此时Trie树就无能为力了. 实际上这种字符串匹配的需求,在开发中非常常见,例如判断一个字符串是否包括某些子串,然后进行分别的处理. 暴力匹配算法(Brute-Force,BF) 这是最常见的算法字符

  • Java之dao模式详解及代码示例

    什么是dao模式? DAO(Data Access Object)顾名思义是一个为数据库或其他持久化机制提供了抽象接口的对象,在不暴露底层持久化方案实现细节的前提下提供了各种数据访问操作.在实际的开发中,应该将所有对数据源的访问操作进行抽象化后封装在一个公共API中.用程序设计语言来说,就是建立一个接口,接口中定义了此应用程序中将会用到的所有事务方法.在这个应用程序中,当需要和数据源进行交互的时候则使用这个接口,并且编写一个单独的类来实现这个接口,在逻辑上该类对应一个特定的数据存储.DAO模式实

  • Java中Volatile关键字详解及代码示例

    一.基本概念 先补充一下概念:Java内存模型中的可见性.原子性和有序性. 可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉.通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情.为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制. 可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的.也就是一个线程修改的结果.另一个线程马上就能看到.比如:用volatile修饰的变量,就会具有可见性.volatile修饰的

  • 详解Java编译优化之循环展开和粗化锁

    循环展开和粗化锁 我们先来回顾一下什么是循环展开. 循环展开就是说,像下面的循环遍历的例子: for (int i = 0; i < 1000; i++) { x += 0x51; } 因为每次循环都需要做跳转操作,所以为了提升效率,上面的代码其实可以被优化为下面的: for (int i = 0; i < 250; i++) { x += 0x144; //0x51 * 4 } 注意上面我们使用的是16进制数字,至于为什么要使用16进制呢?这是为了方便我们在后面的assembly代码中快速找

  • 两万字详解Java Sring String的常见操作以及StringBuffer StringBuilder的区别

    目录 前言 1. 定义字符串 2.字符串比较相等 equals 使用注意事项 3. 字符串常量池( 多图解析 ) 第一个列子解析: 第二个列子解析: 第三个列子解析: 第四个列子解析: 第五个列子解析: 第六个列子解析: 第七个列子解析: 第八个列子解析: 面试题:请解释String类中两种对象实例化的区别 4. 理解字符串不可变 5.字符, 字节与字符串 字节与字符串 小结 6. 字符串常见操作 6.1 字符串比较 6.2 字符串查找 6.3 字符串替换 6.4 字符串拆分 6.5 字符串截取

  • 详解Java创建线程的五种常见方式

    目录 Java中如何创建线程呢? 1.显示继承Thread,重写run来指定现成的执行代码. 2.匿名内部类继承Thread,重写run来执行线程执行的代码. 3.显示实现Runnable接口,重写run方法. 4.匿名内部类实现Runnable接口,重写run方法 5.通过lambda表达式来描述线程执行的代码 [面试题]:Thread的run和start之间的区别? Thread类的具体用法 Thread类常见的一些属性 中断一个线程 1.方法一:让线程run完 2.方法二:调用interr

  • Java中的静态内部类详解及代码示例

    1. 什么是静态内部类 在Java中有静态代码块.静态变量.静态方法,当然也有静态类,但Java中的静态类只能是Java的内部类,也称为静态嵌套类.静态内部类的定义如下: public class OuterClass { static class StaticInnerClass { ... } } 在介绍静态内部类之前,首先要弄清楚静态内部类与Java其它内部类的区别. 2. 内部类 什么是内部类?将一个类的定义放在另一个类的内部,就是内部类.Java的内部类主要分为成员内部类.局部内部类.

随机推荐