Java相关
1.POJO是DO/DTO/BO/VO的统称,禁止命名为xxxPOJO
2.获取多个对象的方法中list作为前缀(不用清一色的get)
3.获取统计值的方法用count作为前缀(比get…Count这种命名简略)
4.POJO类中的布尔类型(Boolean)的变量都不要加is前缀,否则部分框架解析会引起序列化错误
如果你的变量名带is的话,比如isActive,框架解析的时候可能就当成active了。
5.如果是形容能力的接口名称,取对应的形容词为接口名(通常是-able的形式)
6.不允许任何魔法值(未经预先定义的常量)直接出现在代码中(这个要尽量避免,因为后人来阅读的你代码,根本不知道魔法值代表什么)
7.所有POJO类的属性全部使用包装数据类型,RPC的返回值和参数必须使用包装数据类型,所有的局部变量都使用基本数据类型。定义VO/DTO/DO等POJO类时,不要设定任何属性的默认值
简单的说就是0是有意义的,而很多时候我们只想表达null,那基本类型就无能为力了(局部变量有基本类型是因为我清楚意义,效率稍微高那么一点点)
8.构造方法禁止加入任何的业务逻辑,如果初始化逻辑可以放在init方法中。set/get方法也不要增加业务逻辑。此处指的是业务逻辑,比如一些基本的数据赋值,这种还是允许的
如果set/get方法放入业务逻辑,有时候排查问题就变得很麻烦了
9.工具类Arrays.asList()把数组转成List时,不能使用其修改集合的相关方法。比如说add、clear、remove (因为他产生的是一个不可变的List)
10.使用entrySet遍历Map类集合K/V,而不是用keySet方式遍历
keySet遍历了两次,一次是转成Iterator对象,一次是从hashMap中取出key所对应的value,如果JDK8可以使用Map.foreach方法
其实在源码里边我们可以发现,forEach实际上就是封装了entrySet,提供forEach给我们可以更加方便地遍历Map集合
// forEach源码 default void forEach(BiConsumer<? super K, ? super V> action) { Objects.requireNonNull(action); for (Map.Entry<K, V> entry : entrySet()) { K k; V v; try { k = entry.getKey(); v = entry.getValue(); } catch(IllegalStateException ise) { // this usually means the entry is no longer in the map. throw new ConcurrentModificationException(ise); } action.accept(k, v); } }
11.线程资源必须由线程池提供,不允许在应用中自行显示创建线程。线程池不允许用Executors创建【小家java】Java中的线程池,你真的用对了吗?(教你用正确的姿势使用线程池,Executors使用中的坑),通过ThreadPoolExecutor的方式创建,这样的处理方式能够让编写代码的工程师更加明确线程池的运行规则,规避资源耗尽的风险。
12.SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类
如果是JDK8应用,可以使用Instant(针对时间统计等场景)代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat
下面这么使用SimpleDateFormat ,就不会有线程安全问题了
// 1. 在方法内部使用,没有线程安全问题 private static final String FORMAT = "yyyy-MM-dd HH:mm:ss"; public String getFormat(Date date){ SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT); return dateFormat.format(date); } // 2. 每次使用的时候加锁 private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public void getFormat(){ synchronized (SIMPLE_DATE_FORMAT){ SIMPLE_DATE_FORMAT.format(new Date()); ….; } // 3. 使用ThreadLocal,每个线程都有自己的SimpleDateFormat对象,互不干扰 private static final ThreadLocal<DateFormat> DATE_FORMATTER = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } }; // 4. 使用DateTimeFormatter(This class is immutable and thread-safe.) DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); System.out.println(timeFormatter.format(LocalDateTime.now()));
13.避免Random实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一seed导致性能下降
在JDK7之后,可以直接使用API ThreadLocalRandom,而在JDK7 之前,需要编码保证每个线程持有一个实例。
14.类、类属性、类方法的注释必须使用 Javadoc 规范,*使用 /内容/ 格式,不得使用 //xxx 方式
15.对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码的上方,使用三个斜杠///来****说明注释代码的理由
16.使用CountDownLatch进行异步转同步操作,每个线程退出前必须调用 countDown方法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到,避免主线程无法执行至 await 方法,直到超时才返回结果。说明: 注意,子线程抛出异常堆栈,不能在主线程 try-catch 到。
17.对于一写多读,是可以解决变量同步问题, 但是如果多写,同样无法解决线程安全问题。如果是 count++操作,使用如下类实现: AtomicInteger count = new AtomicInteger(); count.addAndGet(1);如果是 JDK8,推荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。
18.使用JDK8的Optional类来防止NPE问题。