阿里编码规约

1、所有的覆写方法,必须加@Override注解

解释:所有的覆写方法,必须加@Override注解。 反例:getObject()与get0bject()的问题。一个是字母的O,一个是数字的0,加@Override可以准确判断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。

加上注解能增加代码的可读性,看到标签就知道这是从父类重写的方法,在调用时也将调用重写后的方法。并且使用@Override可以准确判断是否覆盖成功。

注意:

  • 子类的访问级别必须高于父类被覆盖方法的访问级别,例如父类是public的而子类是protected的则是错误的。
  • 方法被定义为private、static、final则不能被覆盖
  • 方法调用时,会先在子类寻找覆盖的方法,再到父类寻找

2、Map/Set的key为自定义对象时,必须重写hashCode和equals

解释:HashMap中,hashCode()方法继承与Object类,hashCode码默认是内存地址。即便有相同含义的两个对象,比较也是不相等的。例如:

Student st1 = new Student("wei","man");
Student st2 = new Student("wei","man");

HashCode

HashCode是通过hash函数得来的,指的是在hash表中有对应的位置。

能够大大提高查询效率

3、Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals

错误示范:

if(appRoleMemberVo.getMemberType().equals("3")){
    //代码
}

正确示范:

if(State.THREE.equals(appRoleMemberVo.getMemberType())){
    //代码
}

4、浮点数之间的等值判断,基本数据类型不能用==来比较

float a = 1.0f;
float b = 1.0f;
System.out.println(a);//1.0
System.out.println(b);//1.0
System.out.println(a==b);//true

//精度丢失
float c = 1.0f-0.9f;
float d = 0.9f-0.8f;
System.out.println(c);//0.100000024
System.out.println(d);//0.099999964
System.out.println(c==d);//false

输出并不是我们想要的结果(精度丢失),解决办法是使用 BigDecimal 来定义浮点数的值,再进行浮点数的运算操作。

方法1:

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);// 0.1
BigDecimal y = b.subtract(c);// 0.1
System.out.println(x.equals(y));// true 

方法2:

a.compareTo(b) : 返回 -1 表示小于,0 表示 等于, 1表示 大于

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
System.out.println(a.compareTo(b));// 1

5、SimpleDateFormat线程不安全问题

如果我们把SimpleDateFormat定义成static成员变量,那么多个thread之间会共享这个SimpleDateFormat对象, 所以Calendar对象也会共享。

复现错误

public class SimpleDateFormatBugTest2 {
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static Date d1 = null;
    private static Date d2 = null;
    private static Date d3 = null;

    static {
        try {
            d1 = sdf.parse("2020-12-12 12:12:12");
            d2 =sdf.parse("2019-11-11 11:11:11");
            d3 =sdf.parse("2018-10-10 10:10:10");
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            String parse = sdf.format(d1);
            System.out.println("当前日期:" + parse);
        });

        Thread t2 = new Thread(() -> {
            String parse = sdf.format(d2);
            System.out.println("当前日期:" + parse);
        });


        Thread t3 = new Thread(() -> {
            String parse = sdf.format(d3);
            System.out.println("当前日期:" + parse);
        });

        t1.start();
        t2.start();
        t3.start();

        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }
}

使用三个线程分别对2020-12-12 12:12:12、2019-11-11 11:11:11、2018-10-10 10:10:10的Date格式转字符串格式的操作

第一次

当前日期:2019-11-11 11:11:11
当前日期:2019-11-11 11:11:11
当前日期:2019-11-11 11:11:11

第二次

当前日期:2018-10-10 10:10:10
当前日期:2018-10-10 10:10:10
当前日期:2018-10-10 10:10:10

第三次

当前日期:2018-10-11 11:11:11
当前日期:2020-11-11 11:11:11
当前日期:2019-11-11 11:11:11

总结:

SimpleDateFormat是线程不安全的

5.1 SimpleDateFormat线程不安全的解决方法

5.1.1 将SimpleDateFormat定义成局部变量

SimpleDateFormat sdf = new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
String str1 = "01-Jan-2010";
String str2 = sdf.format(sdf.parse(str1));

缺点:每调用一次方法就会创建一个SimpleDateFormat对象,方法结束又要作为垃圾回收。

5.1.2 使用ThreadLocal

// 可以直接设置初始值
private static ThreadLocal<SimpleDateFormat> simpleDateFormat = ThreadLocal.withInitial(() ->
    new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
);
// 调用get()方法取得值
System.out.println(simpleDateFormat.get().parse(date));
// 移除
simpleDateFormat.remove();

为什么用ThreadLocal能解决问题

ThreadLocal即线程本地变量。它用来为每个线程维护一个专属的变量副本,线程对自己的变量副本进行操作时,对其他线程的变量副本没有任何影响。它特别适合解决并发情况下变量共享造成的线程安全性问题。

ThreadLocal的Lambda构造方式:withInitial方法

用于创建一个线程局部变量,变量的初始化值通过调用Supplier的get方法来确定。ThreadLocal会为每个线程提供一份变量,各个线程互不影响

public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
    return new SuppliedThreadLocal<>(supplier);
}

DEMO(银行存款案例):

https://blog.csdn.net/zebe1989/article/details/82692551


版权声明:本文为qq_37436998原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。