vieyahn2017 / iBlog

44 stars 0 forks source link

10.22 避免 Java ”坏味道“ 的一些技术细节 #324

Closed vieyahn2017 closed 4 years ago

vieyahn2017 commented 4 years ago

避免 Java ”坏味道“ 的一些技术细节

源自 https://github.com/apache/servicecomb-java-chassis/pull/1353

vieyahn2017 commented 4 years ago

用 Map 的主键取值的时候,遍历 entrySet 比 keySet 效率更高

原代码(需要计算 n 次 key 的 hash)

for (String key : this.configurations.keySet()) {
   ConfigurationItem item = this.configurations.get(key);
   LOG.info("config item: key=" + key + ";pattern=" + item.stringPattern + ";service=" + item.microserviceName + ";versionRule=" + item.versionRule);
}

优化之后 (不需要计算 hash,只需要每次从 entry 取值就行)

configurations.entrySet().forEach(stringConfigurationItemEntry -> {
   ConfigurationItem item = stringConfigurationItemEntry.getValue();
   LOG.info("config item: key=" + stringConfigurationItemEntry.getKey() + ";pattern=" + item.stringPattern + ";service=" + item.microserviceName + ";versionRule=" + item.versionRule);
});

源代码:

public V get(Object key) {
   Node<K,V> e;
   return (e = getNode(hash(key), key)) == null ? null : e.value;
}
vieyahn2017 commented 4 years ago

判断集合是否为空的时候,使用 Collections.isEmpty 比 collections.size()==0 可读性更强,在某些情况下效率也更高

原代码

public ServiceCombServer choose(List<ServiceCombServer> servers, Invocation invocation) {
   if (servers.size() == 0) {
       return null;
  }

优化之后

public ServiceCombServer choose(List<ServiceCombServer> servers, Invocation invocation) {
   if (servers.isEmpty()) {
       return null;
  }

源码解析(ArrayList)

public int size() {
   return size;
}
public boolean isEmpty() {
   return size == 0;
}

源码解析(BitSet)

public int size() {
   int size = 0;
   if (bits.length > 0) {
       for (int n = 0; n < bits.length; n++) {
           final long mask = bits[n];
           if (mask != 0) {
               size += Long.bitCount(mask);
          }
      }
  }
   // index.length is zero or all index[n] values are zero
   return size;
}
​
public boolean isEmpty() {
   if (bits.length > 0) {
       for (int n = 0; n < bits.length; n++) {
           final long mask = bits[n];
           if (mask != 0) {
               return false;
          }
      }
  }
   // index.length is zero or all index[n] values are zero
   return true;
}
vieyahn2017 commented 4 years ago

工具类应该设计成 final 类,构造函数应该被私有化,工具类的所有方法都声明为 static

先看一个完全遵循的例子:java.util.Math

public final class Math {
​
   /**
    * Don't let anyone instantiate this class.
    */
   private Math() {}

}

再看不遵循 final 的例子:java.util.Arrays

public class Arrays {
​
   /**
    * The minimum array length below which a parallel sorting
    * algorithm will not further partition the sorting task. Using
    * smaller sizes typically results in memory contention across
    * tasks that makes parallel speedups unlikely.
    */
   private static final int MIN_ARRAY_SORT_GRAN = 1 << 13;
​
   // Suppresses default constructor, ensuring non-instantiability.
   private Arrays() {}

}

再看一个不遵循 final 和 私有化构造方法的例子:

public class StringUtils {
   /**
    *
{@code StringUtils} instances should NOT be constructed in
    * standard programming. Instead, the class should be used as
    * {@code StringUtils.trim(" foo ");}.

    *
    *
This constructor is public to permit tools that require a JavaBean
    * instance to operate.

    */
   public StringUtils() {
       super();
  }

}
vieyahn2017 commented 4 years ago

BigDecimal 在使用的时候要用 BigDecimal.valueOf(double) 而不是 BigDecimal(double)

System.out.println(new BigDecimal(0.1)); //0.1000000000000000055511151231257827021181583404541015625
System.out.println(BigDecimal.valueOf(0.1));
//0.1
vieyahn2017 commented 4 years ago

在不确定是否会产生 NPE 的时候,使用 Objects.equals(a, b) 而不是 a.equals(b) ,使用 String.valueOf(val) 而不是 val.toString()

下面三个用法有区别么?

a == b 与 Objects.equals(a, b)
a == null 与 Objects.isNull(a)
val.toString() 与 String.valueOf(val)

String 源码

public static String valueOf(Object obj) {    
   return (obj == null) ? "null" : obj.toString();
}

Objects 源码

public static boolean equals(Object a, Object b) {    
return (a == b) || (a != null && a.equals(b));
}
public static boolean isNull(Object obj) {
   return obj == null;
}
vieyahn2017 commented 4 years ago

返回空的集合而不是 null 避免 NPE

//Before
return null;
//After
return Collections.emptyList();
return new String[0];
vieyahn2017 commented 4 years ago

枚举的属性字段必须设置为私有不可变,且没有 Setter 方法,在构造函数中赋值

原代码

public enum SchemaFormat {
 SWAGGER(".yaml"),
 HTML(".html");
​
 private String suffix;
​
 SchemaFormat(String suffix) {
   this.suffix = suffix;
}
​
 public String getSuffix() {
   return suffix;
}
}

优化后

public enum SchemaFormat {
 SWAGGER(".yaml"),
 HTML(".html");
​
 private final String suffix;
​
 SchemaFormat(String suffix) {
   this.suffix = suffix;
}
​
 public String getSuffix() {
   return suffix;
}
}