开发经常使用规范技巧总结

阅读文本大概须要3分钟。java

一、类的命名使用驼峰式命名的规范。
程序员

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

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

@Data
@Builder
public class CustomBodyDTO {

    private String name;

    private String idCode;

    private String status;
}

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

例如说: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;
    }
}

三、Object 的 equals 方法容易抛空指针异常。设计模式

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

object类中的源码:微信

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

为了不这种现况出现,在比对的时候尽可能将常量或者有肯定值的对象置前。架构

例如说:

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

四、对于全部相同类型的包装类进行比较的时候,都是用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.length; 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);
    }

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

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

若是换成使用包装类Integer类型的话,就能够经过null值来进行区分了。

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

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

七、在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;
    }
}

八、final 能够声明类、成员变量、方法、以及本地变量。

下列状况使用 final 关键字:

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

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

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

  • 不容许运行过程当中从新赋值的局部变量。

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

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

举例说明:

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

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

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

remove 元素请使用 Iterator方式,若是并发操做,须要对 Iterator 对象加锁。

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

十一、使用HashMap的时候,能够指定集合的初始化大小。

例如说,HashMap里面须要存放10000个元素,可是因为没有进行初始化大小操做,因此在添加元素的时候,hashmap的内部会一直在进行扩容操做,影响性能。

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

   HashMap hashMap=new HashMap<>(13334);

十二、Map类集合中,K/V对于null类型存储的状况:

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

1三、能够利用 Set 元素惟一的特性,能够快速对一个集合进行去重操做,避免使用 List 的contains 方法进行遍历、对比、去重操做。

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

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

1四、线程池不容许使用 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。

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

1六、尽可能避免在for循环里面执行try-catch操做,能够选择将try-catch操做放在循环体外部使用。

 正确作法:



不推荐作法:


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

1七、对于大段的代码进行try-catch操做,这是一种不负责任的行为,将稳定的代码也都包围在了try-catch语句块里面没能很好的分清代码的稳定性范围。

一般咱们称在运行中不会出错的代码块为稳定性代码,可能会有异常出错的部分为非稳定性代码块,后者才是try-catch重点须要关注的对象。

1八、在jdk7以后,对于流这类须要关闭链接释放资源的对象,可使用try-with-resource处理机制来应对。

例以下方代码:

  File file = new File("*****");
        try (FileInputStream fin = new FileInputStream(file)) {
            //执行相关操做
        } catch (Exception e) {
            //异常捕获操做
        }

1九、使用ArrayList的时候,若是清楚它的指定大小的话,能够尽可能在初始化的时候进行大小指定,由于随着arraylist不断添加新的元素以后,链表的体积会不断增大扩容。

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

20、对于一些短信,邮件,电话,下单,支付等应用场景而言,开发的时候须要设置相关的防重复功能限制,防止出现某些恶意刷单,滥刷这类型状况。

2一、对于敏感词汇发表的时候,须要考虑一些文本过滤的策略。

这一块的功能能够考虑直接接入市面上已有的成熟的UGC监控服务,或者使用公司内部自研的ugc过滤工具,防止用户发表恶意评论等状况出现。

2二、在创建索引的时候,对于索引的命名须要遵循必定的规范:

索引类型 命名规则 案例
主键索引 pk_字段名,pk是指primary key pk_order_id
惟一索引 uk_字段名,uk是指 unique key uk_order_id
普通索引 idx_字段名,idx是指 index idx_order_id

2三、当咱们须要存储一段文本信息的时候,须要先考虑存储文本的长度。

若是文本的长度超过了5000,则不建议再选择使用varchar类型来进行存储,能够考虑使用text类型进行数据存储,这个时候能够考虑单独用一张表来进行存储数据,而且经过一个额外的主键id来对应,从而避免影响其余字段的查询。

2四、在进行数据库命名的时候尽可能保证数据库的名称和项目工程的名称一致。

2五、在进行表结构设计的时候,只要具备惟一性质的字段都须要创建惟一索引。

这样有助于后期进行查询的时候提升查询的效率,没有惟一索引这一层的保障,即便在业务层加入了拦截,可是依然容易形成线上脏数据的产生。

2六、在进行order by这类型sql查询的时候,须要注意查询索引的有序性。

关于索引的创建,能够去了解一下索引的星级评定,例如三星索引。可是我的认为索引没有所谓的最优性,须要结合实际的业务场景来设计。

2七、在MySQL中,使用count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

2八、在进行数据库存储引擎选择的时候,须要结合相关的应用场景来选择,若是是须要应用在select操做较多的状况下,能够选择使用MyIsAM存储引擎,若是是对于数据的insert,update,这类修改操做较多的业务场景,则优先推荐使用innodb存储引擎。目前广泛互联网公司都推荐使用innodb较多。

往期精彩


01 漫谈发版哪些事,好课程推荐

02 Linux的经常使用最危险的命令

03 精讲Spring&nbsp;Boot—入门+进阶+实例

04 优秀的Java程序员必须了解的GC哪些

05 互联网支付系统总体架构详解

关注我

天天进步一点点

很干!在看吗?☟

本文分享自微信公众号 - JAVA乐园(happyhuangjinjin88)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。