专栏名称: 朱小厮的博客
著有畅销书:《深入理解Kafka》和《RabbitMQ实战指南》。公众号主要用来分享Java技术栈、Golang技术栈、消息中间件(如Kafka、RabbitMQ)、存储、大数据以及通用型技术架构等相关的技术。
目录
相关文章推荐
哲学王  ·  钱穆:读这五类书,做高境界的人 ·  昨天  
哲学王  ·  荒诞的时代没有干净的人 ·  2 天前  
哲学园  ·  乌克兰:自由的代价 ·  2 天前  
哲学王  ·  三联的猛料,刺痛了多少中国人! ·  4 天前  
51好读  ›  专栏  ›  朱小厮的博客

N个Java开发常用规范技巧总结

朱小厮的博客  · 公众号  ·  · 2019-07-15 08:41

正文

点击上方“ 朱小厮的博客 ”,选择“ 设为星标

做积极的人,而不是积极废人



1、类的命名使用驼峰式命名的规范。

例如: UserService,但是以下情景例外: DO / BO / PO / DTO / VO。

例如说: UserPO,StudentPO(PO,VO,DTO,等这类名词需要全大写)

@Data
@Builder
public class CustomBodyDTO {

    private String name;

    private String idCode;

    private String status;
}

2、如果在模块或者接口,类,方法中使用了设计模式,那么请在命名的时候体现出来。

例如说: TokenFactory,LoginProxy等。

public class TokenFactory {


    public TokenDTO buildToken(LoginInfo loginInfo) {
        String token = UUID.randomUUID().toString();
        TokenDTO tokenDTO = TokenDTO.builder()
                .token(token)
                .createTime(LocalDateTime.now())
                .build();
        String redisKey = RedisKeyBuilder.buildTokenKey(token);
        redisService.setObject(redisKey, loginInfo, Timeout.ONE_DAY * 30 * 2);
        log.info("创建token成功|loginInfo={}", loginInfo.toString());
        return tokenDTO;
    }
}

3、Object 的 equals 方法容易抛空指针异常。

从源码来进行分析equals方法是属于Object类的,如果调用方为null,那么自然在运行的时候会抛出空指针异常的情况。

object类中的源码:

    public




    
 boolean equals(Object obj) {
        return (this == obj);
    }

为了避免这种现况出现,在比对的时候尽量将常量或者有确定值的对象置前。

例如说:

正确:“test”.equals(object);
错误:object.equals(“test”);

4、对于所有相同类型的包装类进行比较的时候,都是用equal来进行操作。

对于Integer类来说,当相应的变量数值范围在-128到127之间的时候,该对象会被存储在IntegerCache.cache里面,因此会有对象复用的情况发生。

所以对于包装类进行比较的时候,最好统一使用equal方法。

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1 ];
            int j = low;
            for(int k = 0; k 
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }

  public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

5、所有的pojo类中的属性最好统一使用包装类属性类型数据。 RPC方法的返回值和参数都统一使用包装类数据。 局部变量中使用基本的数据类型。

对于实际的应用场景来说,例如说一个学生类,当我们设置里面的成绩字段为int类型的时候,如果学生没有考试,那么这个成绩字段应该为空,但是int默认会赋值为0,那么这个时候使用基本数据类型就容易产生误区,到底是考了0分,还是说没有参加考试。

如果换成使用包装类Integer类型的话,就可以通过null值来进行区分了。

6、当pojo类在进行编写的时候要重写相应的toString方法,如果该pojo中继承了另外的一个pojo类,那么请在相应的tostring函数中加入super.toString()方法。

通过重写toString方法有利于在日志输出的时候查看相应对象的属性内容进行逐一分析,对于一些有继承关系的对象而言,加入了super.toString方法更加有助于对该对象的理解和分析。

7、在pojo的getter和setter方法里面,不要增加业务逻辑的代码编写,这样会增加问题排查的难度。

正确做法:


public class User {
    private Integer id;

    private String username;

    public Integer getId() {
        return id;
    }

    public User setId(Integer id) {
        this.id = id;
        return this;
    }

    public String getUsername() {
        return username;
    }

    public User setUsername(String username) {
        this.username = username;
        return this;
    }
}


错误做法:


public class User {
    private Integer id;

    private String username;

    public Integer getId() {
        return id;
    }

    public User setId(Integer id) {
        this.id = id;
        return this;
    }

    public String getUsername() {
        return "key-prefix-"+username;
    }

    public User setUsername(String username) {
        this.username = "key-prefix-"+username;
        return this;
    }
}

8、final 可以声明类、成员变量、方法、以及本地变量。

下列情况使用 final 关键字:

  • 不允许被继承的类,如:String 类。

  • 不允许修改引用的域对象,如:POJO 类的域变量。

  • 不允许被重写的方法,如:POJO 类的 setter 方法。

  • 不允许运行过程中重新赋值的局部变量。

  • 避免上下文重复使用一个变量,使用 final 描述可以强制重新定义一个变量,方便更好地进行重构。

9、对于任何类而言,只要重写了equals就必须重写hashcode。

举例说明:

1)HashSet在存储数据的时候是存储不重复对象的,这些对象在进行判断的时候需要依赖hashcode和equals方法,因此需要重写。

2)在自定义对象作为key键时,需要重写hashcode和equals方法,例如说String类就比较适合用于做key来使用。

10、不要在 foreach 循环里进行元素的 remove/add 操作。

remove 元素请使用 Iterator方式,如果并发操作,需要对 Iterator 对象加锁。

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if  (删除元素的条件) {
iterator.remove();
}
}

11、使用HashMap的时候,可以指定集合的初始化大小。

例如说,HashMap里面需要存放10000个元素,但是由于没有进行初始化大小操作,所以在添加元素的时候,hashmap的内部会一直在进行扩容操作,影响性能。

那么为了减少扩容操作,可以在初始化的时候将hashmap的大小设置为: 已知需要存储的大小/负载因子(0.75)+1

   HashMap hashMap=new HashMap<>(13334);

12、Map类集合中,K/V对于null类型存储的情况:

集合名称 key value 说明
HashMap 允许为null 允许为null 线程不安全
TreeMap 不允许为null 允许为null 线程不安全
HashTable 不允许为null 不允许为null 线程安全
ConcurrentHashMap 不允许为null 不允许为null 线程安全

13、可以利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的contains 方法进行遍历、对比、去重操作。

通关观察可以发现,HashSet底层通过将传入的值再传入到一个HashMap里面去进行操作,进入到HashMap里面之后,会先通过调用该对象的hashcode来判断是否有重复的值,如果有再进行equals判断,如果没有相同元素则插入处理。

   public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

14、线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

错误做法:

ExecutorService executors = Executors.newSingleThreadExecutor();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

对于线程池的参数需要有深入的理解后,结合实际的机器参数来进行参数设置,从而防止在使用中出现异常。

    ExecutorService fixedExecutorService = new ThreadPoolExecutor(
                1,
                2
                60,
                TimeUnit.SECONDS,
                 linkedBlockingQueue, 
                new MyThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

ps: 使用Executors.new 方式创建线程池的缺点:

对于FixedThreadPool 和 SingleThreadPool而言

允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

对于CachedThreadPool 和 ScheduledThreadPool而言

允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

15、使用一些日期类的时候,推荐使用LocalDateTime来替代Calendar类,或者说使用Instant来替代掉Date类。

16、尽量避免在for循环里面执行try-catch操作,可以选择将try-catch操作放在循环体外部使用。

正确做法:


try {
         for (int i = 0; i 100; i++) {
             doSomeThing();
         }
       }catch (Exception e){
            e.printStackTrace();
       }







请到「今天看啥」查看全文


推荐文章
哲学王  ·  荒诞的时代没有干净的人
2 天前
哲学园  ·  乌克兰:自由的代价
2 天前
教你看穿男人的心  ·  他真爱你?可以从床上6个细节看出!
8 年前
好奇小姐的好奇心  ·  对他们来说,性是唯一的交流方式
7 年前
海蓝博士  ·  如何对待你生命中不同的人
7 年前