zoukankan      html  css  js  c++  java
  • Java编程常见缺陷汇总(一)

     [案例1】

    1 public boolean equalNode(JudgeNode a, JudgeNode b) {
    2     return a.getId() == b.getId();
    3 }

    【点评】

        应在JudgeNode类里定义equals()方法(估计刚从面向过程语言"转行"过来...)。

    【案例2】

    1 public String[] getMsg() {
    2     List<String> msgList = new ArrayList<String>(2);
    3     msgList.add(linkStatus.get(AC)); msgList.add(getLinkMsg()); 
    4     String[] result = new String[msgList.size()];
    5     msgList.toArray(result);
    6     return result;
    7 }

    【点评】

        可简化为return new String[]{linkStatus.get(AC), getLinkMsg()};

    【案例3】

     1 private boolean bInteger(Object[][] values, int i) { //开发环境真实代码
     2     boolean b = false;
     3     try {
     4         b = values[0][i] instanceof Integer;
     5     } catch(Exception e) {
     6         logger.error("values[0][i] instanceof Integer error:"+e);
     7         b = false;
     8     }
     9     return b;
    10 }

    【点评】

    1. instanceof为运算符,不会抛出空指针等异常;从上下文得知,values[0][i]也不会出现越界等异常。

        因此,通过instanceof判断类型时无须捕获异常。进一步,bInteger()方法毫无意义,完全可以在调用处直接用instanceof判断。

    2. 使用slf4j日志组件时,logger.error(与log.warn)接受Throwable参数,以打印异常名和详细的堆栈信息(可能内部调用e.printStackTrace())。

        但书写打印语句时,需要注意格式。例如:

    1 logger.error("Best print: ", e);
    2 logger.error("Good print: {}", e); //a.
    3 logger.error("Bad print: " + e); //b. 或 + e.toString()
    4 logger.error("Bad print: " + e.getMessage()); //c. 或: {}", e.getMessage())

        a句仍可打印异常名和堆栈信息,但多输出一对花括号"{}";b句仅打印异常名;c句打印异常消息字符串。以空指针异常(Runtime异常)为例,b句打印"java.lang.NullPointerException",c句打印"null"(过于简陋)。

    【案例4】

    1 public DBBatch(int count) throws SQLException {
    2     con = ConnnectionManager.getConnection(DBConsts.DB_SOURCE_NAME);
    3     preparedStatement = con.prepareStatement(DBConsts.SQL);
    4     result = preparedStatement.executeQuery();
    5     this.count = count;
    6 }
    7 public void close() { //close resultstatementconnection
    8 }

    【点评】

        应避免在构造函数里申请资源。若构造函数DBBatch(int count)里preparedStatement.executeQuery()发生异常,上面的preparedStatement、con将无法关闭。此时,外部获取不到DBBatch对象引用,也就无法调用DBBatch.close()方法,导致资源泄露。

    【案例5】

    1 private static String listToString(List<String> columnList) {
    2     StringBuilder colName = new StringBuilder(1000); //1
    3     for(String s : columnList) {
    4     colName.append(s + ",");
    5     }
    6     colName.deleteCharAt(colName.length() - 1); //2
    7     return colName.toString();
    8 }

    【点评】

    1. StringBuffer的初始容量为16个字节。若待处理的数据远大于16个字节,最好在创建StringBuffer时给出适当的初始容量,以减少不必要的扩容操作(容量不足时会通过Arrays.copyOf创建2倍容量的新数组来装载老数据)。但此处new StringBuilder(1000)初始化指定的长度显然过大,会造成空间浪费。

        StringBuilder不同创建方式的初始容量如下: 

    1) StringBuilder()                  // 默认分配16个字节的空间
    2) StringBuilder(int size)             // 默认分配size个字节的空间
    3) StringBuilder(String str)           // 默认分配16个字节 + str.length()个字节空间
    4) StringBuilder(String str)           // 默认分配16个字节 + str.length()个字节空间
    5) StringBuilder(CharSequence charSeq)  // 默认分配16个字节 + charSeq.length()个字节空间

        优选方式3,不建议方式4、5(进行字符串拼接时会面临再次扩容的问题)。

        相关资料还可参考《浅谈JAVA中HashMap、ArrayList、StringBuilder等的扩容机制》。

     2. columnList为空集合时,例如List<String> columnList = Collections.emptyList(),会抛出下标越界异常(StringIndexOutOfBoundsException)。

     3. 可用String.join(",", columnList)代替。若期望"[a, b, c, d]"的输出格式,直接用columnList.toString()即可。

    【案例6】

     1 private static boolean checkName1(String name) { //循环10000000次,耗时350ms
     2     List<String> nameList = new ArrayList<String>();
     3     nameList.add("LiLei"); nameList.add("HaMeimei"); nameList.add("HeDan");
     4     nameList.add("Jame"); nameList.add("Lucy"); nameList.add("Beth");
     5     if(nameList.contains(name)) {
     6         return true;
     7     }
     8     return false;
     9 }
    10 
    11 private static boolean checkName2(String name) { //循环10000000次,耗时1729ms
    12     Set<String> nameSet = new HashSet<String>() {
    13         { add("LiLei"); add("HaMeimei"); add("HeDan");
    14           add("Jame"); add("Lucy"); add("Beth");
    15         }};
    16     return nameSet.contains(name);
    17 }
    18 
    19 private static final List<String> NAME_LIST = new ArrayList<String>() {
    20     { add("LiLei"); add("HaMeimei"); add("HeDan");
    21       add("Jame"); add("Lucy"); add("Beth");
    22     }};
    23 private static boolean checkName3(String name) { //循环10000000次,耗时110ms
    24     return NAME_LIST.contains(name);
    25 }
    26 
    27 private static final Set<String> NAME_SET = new HashSet<String>() {
    28     { add("LiLei"); add("HaMeimei"); add("HeDan");
    29       add("Jame"); add("Lucy"); add("Beth");
    30     }}; 
    31 private static boolean checkName4(String name) { //循环10000000次,耗时52ms
    32     return NAME_SET.contains(name);
    33 }
    34 
    35 private static boolean checkName5(String name) { //循环10000000次,耗时46ms
    36     return "LiLei".equals(name) || "HaMeimei".equals(name) || "HeDan".equals(name) || 
    37            "Jame".equals(name) || "Lucy".equals(name) || "Beth".equals(name);
    38 }

    【点评】

    1. 不要使用"if(expression) return true; else return false;"的写法,应改为"return expression;"。

    2. List的contains()方法内部调用indexOf()遍历查找整个数组,效率较低。若涉及大规模数据操作,应选用Map等容器。

    3. 本案例采用《Java字符串连接的多种实现方法及效率对比》中的计时方法,统计checkName1~5()的执行效率。结果一目了然~

     【案例7】

    1 Map<String, String> apMap = constructApMap();
    2     for(String apId: apMap.keySet()) {
    3     String apType = apMap.get(apId);
    4     ... ...

    【点评】

        建议使用apMap.entrySet遍历Map,而不是keySet+get(实际上遍历两次)。虽然Map get方法"很快",但也不能浪费!

        相关资料参见《keySet 与entrySet 遍历HashMap性能差别》。

        entrySet简化语法(Java 8函数式编程):

    1 Map<String, Object> map = new HashMap<>();
    2 map.forEach((key, value)-> {
    3     ... ...
    4 });

    【案例8】

    1 private double[] devanningArray(List<Double> origin) {
    2     double[] target = new double[origin.size()];
    3     for(int i = 0; i < origin.size(); ++i) {
    4         target[i] = origin.get(i);
    5         ... ...

    【点评】

        ArrayList随机访问,时间复杂度O(1);LinkedList随机访问,时间复杂度O(n)。

        建议:1) 优先for-each,即for(e:list){};2) 若必须随机访问,建议从接口契约上约束,如fun(ArrayList array){}。

        附Java容器类的特征概述:

    ArrayList封装数组,适用于“读多写少”的场景;
    LinkedList双向链表,适用于“写多”的场景,随机访问效率低;
    Vector、Stack鸡肋
    HashMap、HashTable关联容器,HashTable同步
    TreeMap红黑树实现,有序:按逻辑大小排序
    LinkedHashMap有序:按插入顺序排序
    HashSet、TreeSet、LinkedHashSet封装对应的Map
    ConcurrentHashMap
    ConcurrentLinkedQueue、ConcurrentLinkedDueue无锁队列,慎用
    ArrayBlockingQueue、LinkedBlockingQueue 阻塞队列
    CopyOnWriteArrayList、CopyOnWriteArraySet 慎用
    工具、封装类:
    Arrays.asList、Collections.unmodifiableListSetMapCollection
    Collections.synchronizedListSetMapCollection
  • 相关阅读:
    [转]SDRAM/DDR/DDR2/DDR3/DDR4
    Altera cyclone系列altlvds调试
    [转]关于Altera LVDS 经验分享
    [转]FPGA的GTP高速串行接口数据收发
    [转]ISE、vivado、QuartusII调用notepad++、UE汇总(整理)
    [转]vivado管脚分配:PACKAGE_PIN or LOC
    [转]如何使用WinDriver为PCIe采集卡装驱动
    【Docker系列教程之六】Docker的前世今生
    【Docker系列教程之五】如何构建Dockerfile
    【Docker系列教程之四】Dockerfile入门
  • 原文地址:https://www.cnblogs.com/clover-toeic/p/7896023.html
Copyright © 2011-2022 走看看