分类 编码技巧 下的文章

lombok是一个Java库,官方网址是 https://projectlombok.org。它通过对Java字节码进行修改,简化Java开发。这边介绍可以看作是对官方文档的简单介绍,不少例子都是官方文档上,另外官方文档上还有使用lombok和不使用lombok代码的对比,可以加深理解。如果需要更加详细的介绍或者配置,建议大家去官方网站看下。

val

可以使用val作为本地变量声明的类型,替代变量的实际类型。当你使用val的时候,变量的类型会通过初始化表达式推断出。本地变量也可以是final的。

限制条件:

  1. val只作用于本地变量和foreach循环,不能作用于类字段
  2. 必须有初始化表达式,以便于lombok进行推断

举例:

import lombok.val;

public void example{
    // 作用于本地变量
    val a = "123";
    val b = 123;
    System.out.println(a + b);
    
    // 作用于foreach循环
    val list = new int[]{1, 2, 3, 4, 5, 6};
    for (val i : list){
        System.out.println(i);
    }
}

- 阅读剩余部分 -

关于native2ascii的某某

在此处输入标题今天深圳一扫周末的阴冷,阳光明媚。但是一上班,就有一个乱码的问题,上周一个同事修改了代码,使用新的流水线部署后,发出的邮件标题是乱码!而这个同事并没有更改邮件的相关代码。邮件功能以前也是正常的,并没有出现过乱码的现象。

这个发送邮件的过程如下:

  1. Java代码从mail.properties文件读取标题和正文,标题和正文使用类似{0}的占位符进行内容的动态输出;
  2. 然后Java代码从数据库获取收件人,抄送人列表,构建邮件,最后发送。

这次奇怪的是,邮件正文是正常的,标题是乱码的。我看了正文用的是HTML格式,指定了编码charset='gb2312'。

- 阅读剩余部分 -

本周测试同事,在测试我做的一个需求的时候,发现了这么一个bug。
CDMS下发下游系统的数据中,有几条数据有问题,因为其中两个字段是有关联的,都是从同一个字段扩展来的,明眼一看就有问题。
可是这几条数据,都是在cursor里处理的,不会出现,前一个字段扩展用的值是一个样,后一个字段扩展用的是另一个。
我就很费解,并且我在开发环境无法重现。我试了几组数据,下发都没有问题。
然后我就开始检查测试环境出错的数据,发现了猫腻。
现在我就拿Java代码模拟一下这个BUG。

下发下游的数据为Security,如下类所示,我简单的建了三个字段。

package com.chengjf.fxdemo;

public class Security {
    // 证券ID
    private String securityID;
    // 证券名称
    private String securityName;
    // 证券市场
    private String market;

    public Security(String securityID, String securityName, String market) {
        this.securityID = securityID;
        this.securityName = securityName;
        this.market = market;
    }

    public String getSecurityID() {
        return securityID;
    }

    public void setSecurityID(String securityID) {
        this.securityID = securityID;
    }

    public String getSecurityName() {
        return securityName;
    }

    public void setSecurityName(String securityName) {
        this.securityName = securityName;
    }

    public String getMarket() {
        return market;
    }

    public void setMarket(String market) {
        this.market = market;
    }

}

下发逻辑如下:

public class OracleException {

    // 模拟数据库
    private Map<String, String> db = new HashMap<String, String>(8);

    public static void main(String[] args) {

        OracleException exception = new OracleException();

        Collection<Security> collection = getSecurities();
        Iterator<Security> iterator = collection.iterator();

        // 要插入数据库的字段
        String systemID = null;
        String tradeID = null;

        while (iterator.hasNext()) {
            Security security = iterator.next();
            try {
                // 对systemID进行处理
                // !这块处理的时候,某些数据不符合条件,导致没有生成systemID
                // !而是用上一次处理生成的ID
                if ("1".equals(security.getMarket())) {
                    systemID = security.getSecurityID() + "_SYSTEM";
                }
                // 对tradeID继续处理
                tradeID = security.getSecurityID() + "_TRADE";

                // 插入数据库
                exception.insertIntoDB(systemID, tradeID);

            } catch (IllegalArgumentException e) {
                System.err.println(e);
            }
        }

        // 输出数据库结果
        System.out.println(exception.db);
        /*        
         * 000002_SYSTEM=600002_TRADE, 
         * 000001_SYSTEM=000001_TRADE, 
         * 000003_SYSTEM=000003_TRADE, 
         * 000004_SYSTEM=600004_TRADE
         * 000002竟然对应600002,00004竟然对应600004
         * 显然错误
         */
    }

    /**
     * 插入数据库,如果systemID没有则插入,有则更新 借助Map结构的特性
     * 
     * @param systemID
     * @param tradeID
     */
    private void insertIntoDB(String systemID, String tradeID) {
        if (systemID != null &amp;&amp; tradeID != null) {
            this.db.put(systemID, tradeID);
        } else {
            throw new IllegalArgumentException();
        }
    }

    /**
     * 数据源Cursor
     * 
     * @return
     */
    private static Collection<Security> getSecurities() {
        Security security1 = new Security("000001", "平安银行", "1");
        Security security2 = new Security("000002", "万科A", "1");
        Security security3 = new Security("600001", "邯郸钢铁", "2");
        Security security4 = new Security("600002", "齐鲁石化", "2");
        Security security5 = new Security("000003", "PT金田A", "1");
        Security security6 = new Security("000004", "国农科技", "1");
        Security security7 = new Security("600003", "ST东北高", "2");
        Security security8 = new Security("600004", "白云机场", "2");

        return Arrays.asList(new Security[] { security1, security2, security3,
                security4, security5, security6, security7, security8 });
    }
}

出现BUG原因的,就是在对数据进行处理的时候,没有对每条数据的context进行初始化。下一条数据因为一些逻辑,某些值竟然和上一条数据的某些值相同或者有关系。最终造成了数据不一致的结果。

然而,我发现这种代码在系统中很多,特别是在PL/SQL中。一般变量只在begin开头初始化,每次cursor的loop里都没有重新初始化,都有这种BUG的隐患。

同时,这种BUG应该是最简单的一个BUG,但是可能也是最容易犯的。这种BUG发生的时候跟处理的数据有关系,一般数据正常的时候不会出现这种BUG,但是数据如果有问题,就不灵了。

这种BUG不容易发现,但是应该谨记:

数据初始化非常重要。保证数据处理时的context一切正常。

Java中Map这种数据结构非常常用,也非常实用。最常用的方法就是下面几个:

V get(Object key);
V put(K key, V value);
V remove(Object key);
boolean containsKey(Object key);

不过,对于Map的遍历操作还是很常用的。下面就介绍几种Map的遍历操作方法。

通过key的遍历

首先拿到Map的key的集合Set,然后使用Set的迭代器Iterator,遍历每个key,通过Map的get()方法获取其值。

Set<Object> set = map.keySet();
Iterator<Object> iterator = set.iterator();
while (iterator.hasNext()) {
    Object key = iterator.next();
    System.out.println(key + " " + map.get(key));
}

上述代码的前两行可以合成一行:

Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()) {
    Object key = iterator.next();
    System.out.println(key + " " + map.get(key));
}

上面这个版本就是我原来最常用的遍历方式。这个版本的进化版本就是使用foreach:

for (Object key : map.keySet()) {
     System.err.println(key + " " + map.get(key));
}

这类方法的主要思想就是拿到key,然后使用get()方法拿到值。

通过Entry

Entry是Map的内部接口:

interface Entry<K,V>{
    K getKey();
    V getValue();
    V setValue(V value);
    boolean equals(Object o);
    int hashCode();
}

可用通过遍历Map的Entry,调用Entry的getKey()方法和getValue()方法拿到键和值。

Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
    Map.Entry entry = (Map.Entry) iterator.next();
    System.err.println(entry.getKey() + " " + entry.getValue());
}

更加简单的foreach版本如下:

for (Map.Entry<Object, Object> entry : map.entrySet()) {
    System.err.println(entry.getKey() + " " + entry.getValue());
}

这类方法的主要思想就是拿到Map的Entry,Entry就是键值对的组合。从效率上来说,通过Entry效率要稍高。http://stackoverflow.com/questions/3870064/performance-considerations-for-keyset-and-entryset-of-map