Java易错的点

BigDecimal

数据库存储金额使用Decimal类型,对应的Java类型是BigDecimal,在判断BigDecimal是否为0时,应该使用compareTo,使用equals是错的:

System.out.println(BigDecimal.ZERO.equals(new BigDecimal("0.00"))); // false

使用到BigDecimal的除法时,要指定保留的小数点位,例如四舍五入保留4位小数 :

BigDecimal.ONE.divide(BigDecimal.ONE, new MathContext(4, RoundingMode.HALF_UP))

synchronized同步块

使用synchronized同步块不应该使用不可变类,所谓的不可变类是指这个类的实例一旦创建完成后,就不能改变其成员变量值。如JDK内部自带的很多不可变类:IntergerLongString

public class MyRunnable1 implements Runnable {  
    private static Integer i = 0;    // 【1】
    @Override  
    public void run() {  
        while (true) {  
            synchronized (i) {
            if (i < 100) {  
                i++;                 // 【2】
                System.out.println(Thread.currentThread().getName() + " "  
                    + " hashCode = " + i.hashCode() + "\t"  
                    + " i = " + i);  
            } else {  
                break;  
            }  
        }  
    }    
}

【1】synchronized同步块中的变量建议使用final修饰,避免同步块中更改其引用,
private static final Integer I = 0;
在同步块中试图更改其引用,编译会直接报错

【2】i++; 实际逻辑为 i = Integer.valueOf(i.intValue() + 1);,而Integer.valueOf每次是返回一个新的Integer对象,所以,synchronized实际上没有起到预想中的效果

方法重载不要有歧义

例如相同的方法名,一个参数为Object,一个参数为List<Object>,虽然Java会自动找最精确匹配的方法调用,但在实际情况中,还是发现有例外的地方,传入List<Object>却调用了参数为Object的方法。

所以,结论是:不要出现对于相同的参数,传入任何一个方法都可以的情况。

java static顺序

在同一个类中,java static块和static变量的执行顺序是按代码顺序从上到下的,而不是static变量先或static块先。

java序列化不要使用Enum

http://mysun.iteye.com/blog/1581119 会引起的问题:当Enum增加枚举时,当旧客户端收到新服务器端的新枚举值时,会反序列化失败,抛出异常。

Guava

  • Lists.transform转换后的List中的元素,无法set改变其元素值。推荐使用Java 8的stream().map方式。

  • guava的ImmutableMap用于jxls之类生成word excel工具时,会有问题。

  • Splitter.on("&").withKeyValueSeparator("=").split(urlParam);解析QueryString时,当有多个参数有相同name时,会报重复key异常。也即withKeyValueSeparator不允许出现多个相同key情况。

Junit Test

  • Test方法中启动的线程,当Thread的主线程执行完后,启动的线程会被结束掉,不会等待这些线程执行完。如果Test中new线程去跑测试,要注意,可能测试还没跑程序就已经成功结束运行了。

Spring

  1. Spring的自动扫描注解的配置中,<context:component-scan base-package="com.abc" />,其值按语义来是扫描的包的,不要写成com.abc.*的形式,这样的话,com.abc直接下的类就扫描不到。

  2. 尽量不要使用public成员变量,因为在Spring中存在大量的代理示例,它们不会处理public成员变量,外部访问时就是null值。

Spring MVC

  1. 对于302跳转,http头部并没有Referer值。

  2. 当url参数中有多个name相同的参数,后台SpringMVC注入的值是这些参数值用逗号隔开的值。例如前端?name=a&name=b,后台SpringMVC注入为String name值为a,b

  3. 要用@RequestMapping的produces注解来设置contentType(例如gbk编码),而不是用HttpServletResponse的setContentType方法来设置。

  4. 后台的cors跨域方法必须注解上@CrossOrigin,有必要时可以限定请求域。当跨域上传文件时,浏览器会发次2次请求:第一次发出OPTIONS请求,探测服务器是否支持跨域;如果服务器支持跨域,那么浏览器会发出第二次请求,真正去上传文件。

Velocity

如果使用到自定义方法#macro,最好保证整个系统内,每个#macro方法名称是不同的。按规则不同文件定义相同方法是没有问题的,用了这么多年最近发现一台线上服务器上渲染有问题,花了个把小时才发现原因竟是两个vm文件中定义的方法名相同。这台机器的相关信息记录下,不排除和jvm有关:

# java版本
openjdk version "1.8.0_161"
OpenJDK Runtime Environment (build 1.8.0_161-b14)
OpenJDK 64-Bit Server VM (build 25.161-b14, mixed mode)
# 操作系统
CentOS Linux release 7.3.1611 (Core) 
Linux iZfk405x31aljw4eonl8snZ 3.10.0-514.21.2.el7.x86_64 #1 SMP Tue Jun 20 12:24:47 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
# 其它
apache-tomcat-8.0.48
velocity-1.7.jar
velocity-tools-2.0.jar

由于velocity现在已经少用了,所以就不去深究里面的原因。

Web

  • 设置cookie时,值value(记得要判非空字符串猜这样encode,否则为空字符串)要用URLEncoder.encode(value, "UTF-8")编码一下。后台读取时,要用URLDecoder.decode(cookie.getValue(), "UTF-8");decode一下,前端javascript读取时,对应也要decodeURIComponent(url)一下。

  • 当cookie从没有指定域名到指定根域名变化时,对于已有的cookie,它会先从子域名去拿,而根域名新打进的cookie因为被子域名抢先而拿不到,导致新cookie永远拿不到。要注意这种情况,如果发生,建议换个cookie名称。

MySQL

  • 变更数据类型时,如果转换成Decimal类型,记得要设置小数点位数,否则会丢失数据。
文档更新时间: 2018-11-10 20:15   作者:nick