java代码优化

来源于:http://thecodesample.com/

本文给出几个提升Java性能的基本方法:

不要在循环条件中计算

如果在循环 (如for循环、while循环)条件中计算,则循环一遍就要计算一次,这会降低系统的效率,如:

//每次循环都要计算 count *2
while(i < count *2) {
    //Do something ...
}
应该替换为:
//只计算一遍
int total = count *2
while(i <total) {
    //Do something ...
}
再如:
for (int i=0; i<size()*2; i++) {
    //Do something
}
应该改成:
for (int i=0, stop=size()*2; i<stop; i++) {
    //Do something
}

 不要将相同的子表达式计算多次

if (birds.elementAt(i).isGrower()) ...
if (birds.elementAt(i).isPullet()) ...
将相同的子表达式提取为一个变量,如:
Bird bird = birds.elementAt(i);
if (bird.isGrower()) ...
if (bird.isPullet()) ...

 

减少数组访问次数,每次数组访问都需要检查一下索引,所以减少数组访问的次数是值得的。
double[] rowsum = new double[n];
for (int i = 0; i < n; i++)
    for (int j = 0; j < m; j++)
        rowsum[i] += arr[i][j];
改成如下形式:
double[] rowsum = new double[n];
for (int i = 0; i < n; i++) {
    double[] arri = arr[i];
    double sum = 0.0;
    for (int j = 0; j < m; j++)
        sum += arri[j];
    rowsum[i] = sum;
}

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

如下例子用于返回给定年份给定月份所具有的天数。

public static int monthdays(int y, int m) {
    int[] monthlengths = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
    
    return m == 2 && leapyear(y) ? 29 : monthlengths[m-1];
}

 上述方法每次调用都会重新生成一个monthlengths 数组,但必要要主要的是:数组不会改变,属于不变数组。在这种情况下,

把它声明为 final static 更加合适,在类加载后就生成该数组,每次方法调用则不再重新生成数组对象了,这有助于提高系统性能。

应该改成:
private final static int[] monthlengths = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
public static int monthdays(int y, int m) {
    return m == 2 && leapyear(y) ? 29 : monthlengths[m-1];
}
稍微复杂一些的初始化可以使用静态初始化块static {… }来做,如:
private final static double[] logFac = new double[100];
static {
    double logRes = 0.0;
    for (int i=1, stop=logFac.length; i<stop; i++)
        logFac[i] = logRes += Math.log(i);
}
 
public static double logBinom(int n, int k) {
    return logFac[n] - logFac[n-k] - logFac[k];
}

 缩小变量的作用范围

关于变量,能定义在方法内的就定义在方法内,能定义在一个循环体内的就定义在循环体内,能放置在一个try…catch块内的就放置在该块内,

其目的是加快GC的回收。

 

字符串频繁”+”操作使用StringBuilder或者StringBuffer

 

虽然String的联接操作(“+”)已经做了很多优化,但大量的追加操作上StringBuilder或者StringBuffer还是比”+”的性能好很多。

String s = "";
for (int i=0; i<n; i++) {
    s += "#" + i;
}
应该改成:
StringBuilder sbuf = new StringBuilder();
for (int i=0; i<n; i++) {
    sbuf.append("#").append(i);
}
String s = sbuf.toString();
此外,
String s = "(" + x + ", " + y + ")";
它将自动会编译为StringBuilder.append(…)。

 覆写Exception的fillInStackTrace方法

fillInStackTrace方法是用来记录异常时的栈信息的,这是非常耗时的动作,如果在开发时不需要关注栈信息,则可以覆盖之,

如下覆盖fillInStackTrace的自定义异常会使性能提升10倍以上:

class MyException extends Exception {
   public Throwable fillInStackTrace() {
      return this;
   }
}

 延迟初始化

当你不知道一个对象是否需要的时候,可以选择延迟初始化:直到需要的时候才初始化,但只分配一次。

public class Car {
    private Button button = new JButton();
    public Car() {
        ... initialize button ...
    }
    public final JButton getButton() {
        return button;
    }
}
可以改为:
public class Car {
    private Button button = null;
 
    public Car() { ... }
    public final JButton getButton() {
        if (button == null) { // button not yet created, so create it
            button = new JButton();
            ... initialize button ...
        }
        return button;
    }
}

 不要重复创建对象

比如,一个商品相关信息的更新,为了搜索,需要为其创建索引。

针对单个商品建立的索引如下:

public void index(Product product) {
    if (product != null) {
        FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
        fullTextEntityManager.index(product);
    }
}
针对多个商品建立索引,可能会写出如下的代码,写一个for循环,直接调用单个商品索引建立的方法。
public void indexAll(List<Product> products) {
    if (products != null) {
        for (Product product : products) {
            index(product);
        }
    }
}
这个方法比较直观,但是为每一个Product创建索引的时候,都会新创建一个FullTextEntityManager对象。其实我们只需要创建一次,代码如下:
public void indexAll(List<Product> products) {
    if (products != null) {
        FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
        for (Product product : products) {
            fullTextEntityManager.index(product);
        }
    }
}
使用Collections.emptyXXX方法代替创建空的ArrayList,HashSet等

下面给出一个获取一个指定分类以及其所有父类的规格的方法
public Set<Specification> getAllSpecifications(ProductCategory productCategory) {
    Set<Specification> specifications = new HashSet<Specification>();
    if (null != productCategory) {
        specifications.addAll(productCategory.getSpecifications());
        specifications.addAll(productCategory.getParentSpecifications());
    }
    return specifications;
}
该方法中,不管productCategory是否为空,都会新建一个空的Set集合,而每次创建的空Set也会是不同的对象。我们可以先判断商品分类是否为空
,如果是空,则调用Collections的emptySet()方法,该方法将返回一个常量的空集,并不需要重新创建新的空集对象。

 

代码如下:
public Set<Specification> getAllSpecifications(ProductCategory productCategory) {
    if(productCategory == null){
        return Collections.<Specification>emptySet();
    }
    Set<Specification> specifications = new HashSet<Specification>();
    specifications.addAll(productCategory.getSpecifications());
    specifications.addAll(productCategory.getParentSpecifications());
    return specifications;
}
去掉重复代码

下面的例子是根据一个指定分类,获取其子分类的方法, 分类结果以Map的形式返回,key为分类id,value为分类名称,这个主要用
于前台Ajax去构建分类的树形结构。如果指定的分类为null,则获取根分类。
public Map<Long, String> getSubCategoryTree(Long parentId) {
    Map<Long, String> categoryOptions = new HashMap<Long, String>();
    ProductCategory productCategory = productCategoryService.find(parentId);
    if (null == productCategory) {
        for (ProductCategory pc : productCategoryService.findRoots()) {
            categoryOptions.put(pc.getId(), pc.getName());
        }
    } else {
        for (ProductCategory pc : productCategory.getChildren()) {
            categoryOptions.put(pc.getId(), pc.getName());
        }
    }
    return categoryOptions;
}
在这个方法中,if-else语句中除了获取的集合 productCategoryService.findRoots()和 productCategory.getChildren()不同外,其余都是一样的。
我们完全可以将这个集合提取出来,代码块将不再包含if-else块,同时for循环也只需要一个。

帮助
public Map<Long, String> getSubCategoryTree(Long parentId) {
    Map<Long, String> categoryOptions = new HashMap<Long, String>();
    ProductCategory productCategory = productCategoryService.find(parentId);
    Collection<ProductCategory> productCategories = (productCategory == null) ? productCategoryService
            .findRoots() : productCategory.getChildren();
    for (ProductCategory pc : productCategories) {
        categoryOptions.put(pc.getId(), pc.getName());
    }
    return categoryOptions;
}

 

去掉不必要的else块
private int getAttributeValueId(String value) {
    if (StringUtils.isEmpty(value)) {
        return -1;
    } else {
        String[] values = value.split(ATTRIBUTE_ID_SEPARATOR);
        String id = values[values.length - 1];
        return Integer.parseInt(id);
    }
}
可以改成
private int getAttributeValueId(String value) {
    if (StringUtils.isEmpty(value)) {
        return -1;
    }
    String[] values = value.split(ATTRIBUTE_ID_SEPARATOR);
    String id = values[values.length - 1];
    return Integer.parseInt(id);
 
}

 在不需要再用时关闭连接资源

下面是一个简单的查询数据库的代码,执行完查询操作之后,判断连接资源是否为null,如不为null,则调用相应的close()方法关闭资源。

Statement stmt = null;
ResultSet rs = null;
Connection conn = getConnection();
try{
    stmt = conn.createStatement();
    rs = stmt.executeQuery(sqlQuery);
    progressResult(rs);
}catch(SQLException e){
    //forward to handler
}finally {
    if(rs !=null){
        rs.close();
    }
    if(stmt != null){
        stmt.close();
    }
    if(conn !=null){
        conn.close();
    }
}
在这个关闭的过程中,存在着风险,比如rs在调用close()方法时发生异常,则stmt和conn将不会关闭。

 为了能够使资源都得到释放,应改为:

Statement stmt = null;
ResultSet rs = null;
Connection conn = getConnection();
try{
    stmt = conn.createStatement();
    rs = stmt.executeQuery(sqlQuery);
    progressResult(rs);
}catch(SQLException e){
    //forward to handler
}finally {
     
    try{
        if(rs !=null){
            rs.close();
        }
    }catch(SQLException e){
        //forward to handler
    }finally{
        try{
            if(stmt !=null){
                stmt.close();
            }
        }catch(SQLException e){
            //forward to handler
        }finally{
            try{
                if(conn !=null){
                    conn.close();
                }
            }catch(SQLException e){
                //forward to handler
            }
        }
    }
 
}
如果使用的是JDK1.7后的版本, 也可以使用Java SE 7中支持的使用try-with-resource的方式。
try(   
	Connection conn = getConnection(); 
	Statement stmt = conn.createStatement();
	ResultSet rs = stmt.executeQuery(sqlQuery)){
    progressResult(rs);
}catch(SQLException e){
    //forward to handler
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值