关于规范化:一位攻城狮的自我修养在于良好的编程规范

11次阅读

共计 15256 个字符,预计需要花费 39 分钟才能阅读完成。

命名格调

  • 类名 应用 UpperCamelCase 格调, 但下列情景除外:

    • DO: Data Object. 与数据库表构造一一对应, 通过 DAO 层向上传输数据源对象
    • BO: Business Object, 业务对象. 由 Service 层输入的封装业务逻辑的对象
    • DTO: Data Transfer Object, 数据传输对象. Service 或 Manager向外传输的对象
    • VO: View Object, 显示对象. 通常是 Web 向模板渲染引擎层传输的对象
    • AO: Application Object, 利用对象. 在 Web 层与 Service 层之间形象复用的对象模型
    • PO: POJO 缩写,Plain Ordinary Java Object. 专指只有 setter/getter/toString 的简略类, 包含 DO,DTO,BO,VO 等, 禁止应用 xxxPOJO 来命名
    • UID
  • 办法名, 参数名, 成员变量, 局部变量 都对立应用 lowerCamelcase 格调
  • 常量命名全副大写, 单词间用下划线隔开, 力求语义表白残缺分明, 不要嫌名字长
  • 抽象类命名应用 Abstract 或者 Base 结尾
  • 异样类命名应用 Exception 结尾
  • 测试类命名要以要测试的类的名称命名, 以 Test 结尾
  • 类型与中括号紧挨来示意数组
  • POJO类中布尔类型的变量都不要加 is 前缀, 在局部框架中会引起序列化谬误
  • 包名对立应用小写, 点分隔符之间有且仅有一个天然语义的英语单词. 包名对立应用复数模式. 然而类名如果有复数含意, 能够应用复数模式
  • 杜绝不标准的缩写, 防止望文不知义
  • 为了达到代码自解释的指标, 任何自定义的编程元素在命名时, 应用尽量残缺的单词组合来表白含意
  • 在常量与变量命名的同时, 示意类型的名词放在词尾, 以晋升辨识度
  • 如果模块, 接口, 类, 办法应用了设计模式, 在命名时须要体现出设计模式
  • 接口类中的办法和属性不要加任何润饰符号(不要加public), 放弃代码的简洁
  • 尽量不要在接口中定义变量, 如果肯定要定义变量, 肯定是与接口办法无关的, 并且是整个利用的根底变量

    • 接口办法签名: void commit()
    • 接口根底常量: String COMPANY=”Oxford”
  • 接口和实现类:

    • 对于 ServiceDAO类, 基于 SOA 的理念, 裸露进去的服务肯定是接口, 外部的实现类用 Impl 的后缀与接口的区别
    • 如果是形容能力的接口名称, 去对应的形容词为接口 (-able 的模式)
  • 枚举类带上 Enum 后缀, 枚举成员名称需全副大写

    • 枚举类是非凡的类, 域成员均为常量, 且构造方法被默认强制是公有的
  • 各层命名标准:

    • Service 或者 DAO 层办法命名标准:

      • 获取单个对象的办法用 get 做前缀
      • 获取多个对象的办法用 list 做前缀 , 复数模式结尾
      • 获取统计值的办法用 count 做前缀
      • 插入方法应用 save 或者 insert 做前缀
      • 删除的办法应用 remove 或者 delete 做前缀
      • 批改的办法应用 update 做前缀
    • 畛域模型命名标准:

      • 数据对象: XxxDO,Xxx 为数据表名
      • 数据传输对象: XxxDTO,Xxx 为业务畛域相干的名称
      • 展现对象: XxxVO,xxx 个别为网页名称
      • POJO为 DO,DTO,BO,VO 的统称, 禁止命名成 XxxPOJO

        常量定义

  • 不容许任何未经事后定义的常量呈现在代码中
  • 在 long 或者 Long 赋值时,数值后应用大写的 L, 不能是小写的l. 因为小写容易和数字1 混同, 造成误会
  • 不要应用一个常量类保护所有常量, 要按常量的性能进行归类, 离开保护

    • 大而全的常量类横七竖八, 应用查找性能能力定位到批改的常量, 不利于了解和保护
  • 常量的复用档次有五层:

    • 跨利用共享常量: 搁置在二方库中, 通常是 client.jar 中的 constant 目录下
    • 利用类共享常量 搁置在一方库中, 通常是子模块中的constant 目录下
    • 子工程内共享常量 在以后子工程的constant 目录下
    • 包内共享常量 在以后包的constant 目录下
    • 类内共享常量 间接在类外部private static final 定义
  • 如果变量值仅在一个固定范畴内变动, 应用 enum 类型定义

    • 如果存在名称之外的延长属性应应用 enum 类型, 比方节令, 示意一年中第几个节令:

      public enum SeasonEnum {SPRING(1),SUMMER(2),AUTUMN(3),WINTER(4);
      private int seq;
      SeasonEnum(int seq) {this.seq=seq;}
      } 

      代码格局

  • 大括号的应用约定:

    • 如果大括号内为空, 则简洁地写成 {} 即可, 不须要换行
    • 如果是非空代码块:

      • 左大括号前不换行
      • 左大括号后换行
      • 右大括号前换行
      • 右大括号后如果还有 else 则不换行
      • 示意终止的右大括号后必须换行
  • 小括号的应用约定:

    • 左小括号和字符之间不要呈现空格
    • 右小括号和字符之间也不要呈现空格
    • 左大括号之前须要空格
  • if,for,while,switch,do 等保留字与括号之间都必须加空格
  • 任何二目, 三目运算符左右两边都须要加一个空格

    • 运算符包含:

      • 赋值运算符 :=
      • 逻辑运算符 :&&
      • 加减乘除符号
  • 采纳 4 个空格进行缩进
  • 正文的双斜线与正文内容之间有且仅有一个空格
  • 办法参数在定义和传入时, 多个参数逗号前面必须加空格
  • 单个办法的总行数不要超过 80 行:

    • 除正文之外的办法签名, 左右大括号, 办法内代码, 空行, 回车及任何不可见字符的总行数不超过 80 行
    • 代码逻辑分清红花和绿叶, 共性和共性:

      • 绿叶逻辑独自进去成为额定的办法, 使骨干代码更加清晰
      • 共性逻辑抽取成共性办法, 便于复用和保护
  • 不须要减少若干空格来使某一行的字符与上一行对应地位的字符对齐
  • 不同逻辑, 不同语义, 不同业务代码之间只须要插入一个空行宰割来晋升可读性即可

    OPP 规约

  • 防止通过一个类的对象援用拜访类的动态变量和静态方法, 这会减少编译器的解析老本, 间接应用类名拜访即可
  • 所有的覆写办法, 必须加 @Override
  • 雷同参数类型, 雷同业务含意, 才能够应用 Java 的可变参数, 防止可变参数应用 Object 类型

    • 可变参数必须搁置在参数列表的最初, 倡议尽量不要用可变参数编程
  • 内部正在调用的或者二方库依赖的接口, 不容许批改办法签名 (办法名和参数列表), 防止对接口的调用方产生影响 . 接口过期 必须加上 ==@Deprecated== 注解, 并清晰地阐明采纳的新接口和新服务是什么
  • 不能应用过期的类或办法:

    • 接口的提供方既然明确是过期接口, 那么有任务提供新接口
    • 作为调用方, 有任务考据过期办法的新实现是什么
  • Objectequals 办法容易抛出空指针异样, 应应用常量或者确定有值的对象来调用equals

    • “test”.equals(Object)
    • 举荐应用java.util.objects
  • 所有雷同类型的包装类对象之间的值的比拟, 全副应用 equals 办法比拟

    • 对于 Integer var = ? 在 -128 至 127 范畴内赋值时 ,Integer对象是在 IntegerCache.cache 中产生, 会复用已有对象, 这个区间内的 Integer 值能够间接应用 == 进行判断
    • 然而这个区间之外的所有数据, 都会在堆上产生, 并不会复用已有对象, 所以举荐应用 equals 办法进行比拟
  • 任何货币金额, 均以最小货币单位且整型类型来进行存储
  • 浮点数之间的等值判断:

    • 根本类型不能用 == 来比拟
    • 包装类型不能应用 equals 来判断

      • 浮点数采纳 尾数 + 阶码 的编码方式, 相似与迷信计数法有效数字 + 指数的示意形式. 二进制无奈准确示意大部分十进制小数
    • 为了避免出现问题, 所有的浮点数都应用 BigDecimal 定义

      /*
       * float 类型的浮点数:
       *         指定一个误差范畴,
       *         两个浮点数的差值在此范畴之内,
       *         则认为是相等的.
       */
       float a = 1.0f - 0.9f;
       float b = 0.9f - 0.8f;
       float diff = 1e - 6f;
       if (Math.abs(a-b) < diff) {System.out.println("true");
       }
      
      /* 
       *  应用 BigDecimal 来定义值, 再进行浮点数的运算操作
       */
       BigDecimal a = new BigDecimal("1.0");
       BigDecimal b = new BigDecimal("0.9");
       BigDecimal c = new BigDecimal("0.8");
       BigDecimal x = a.substract(b); 
       BigDecimal y = b.substract(c);
       if (x.equals(y)) {System.out.println("true");
       } 
  • 定义数据对象 DO 类时, 属性类型要与数据库字段类型相匹配

    • 数据库字段的 bigint 必须与类属性 Long 类型绝对应
  • 禁止应用构造方法 BigDecimal(double) 的形式将double 值转化为 BigDecimal 对象:

    • BigDecimal(double)存在精度损失危险, 在准确计算或值比拟的场景中会导致业务逻辑异样

      • 举荐应用入参为 String 的构造方法
      • 或者应用 BigDecimalvalueOf办法: 此办法外部执行了 Double 的 toString, 理论能表白的精度对尾数进行了截断

        BigDecimal a = new BigDecimal("0.1");
        BigDecimal b = BigDecimal.valueOf(0.1);
  • 根本类型和包装类型的应用规范:

    • 所有的 POJO 类属性必须应用包装类数据类型
    • RPC 办法的返回值和参数必须应用包装数据类型
    • 所有的局部变量应用根本数据类型
  • 定义 DO,DTO,VO 等 POJO 类时, 不要设定任何属性默认值
  • 序列化类新增属性时, 不能批改 serialVersionUID 字段, 这样会导致反序列化失败; 如果齐全不兼容降级, 防止反序列化凌乱, 能够批改 serialVersionUID 值. 在 serialVersionUID 不统一时会抛出序列化运行时异样
  • 构造方法中禁止退出任何业务逻辑, 如果有初始化逻辑, 要放在 init
  • POJO类必须写 toString 办法. 如果继承了一个 POJO 类, 须要在后面增加super.toString

    • 这样在办法执行抛出异样时, 能够间接调用 POJO 的 toString()办法打印属性值, 便于排查问题
  • 禁止在 POJO 类中, 同时存在对应属性 Xxx 的isXxx()getXxx() 办法

    • 框架在调用属性 Xxx 的获取办法时, 不能确定哪个办法肯定是被优先调用到的
  • 应用索引拜访用 String 的 split 办法失去的数组时, 须要做最初一个分隔符后有无内容的查看, 否则会有 IndexOutofBoundsException 异样
  • 当一个类有多个构造方法, 或者多个同名办法, 这些办法应该按程序搁置在一起, 便于浏览
  • 类内办法定义的程序顺次为:

    • 私有办法或者爱护办法

      • 私有办法是类调用者或者保护最频繁应用的办法, 最好首先展现
      • 爱护办法只管是子类须要的办法, 但也可能是模板设计模式中的外围办法
    • 公有办法

      • 公有办法内部个别不须要关怀, 是一个黑盒实现
    • getter 或者 setter 办法

      • 所有 Service 和 DAO 的 getter 或者 setter 办法都放在类的最初
  • setter 办法中, 参数名称要和类成员变量名称统一 ,this. 成员名 = 参数名.
  • 在 getter 或者 setter 办法中, 不要减少业务逻辑
  • 循环体内, 字符串的类连贯形式, 应用 StringBuilderappend办法进行扩大

    • 否则会导致每次循环都会 new 一个新的 StringBuilder 对象
    • 而后再进行 append 操作
    • 最初通过 toString 办法返回 String 对象, 造成资源节约
  • final 能够申明 类, 成员变量, 办法, 以及本地变量. 应用 final 的状况:

    • 不容许被继承的类

      • String
    • 不容许批改的援用的域对象
    • 不容许被重写的办法

      • POJO 中的 setter 办法
    • 不容许运行过程中从新赋值的局部变量
    • 防止上下文重复使用一个变量, 应用 final 形容能够强制从新定义, 不便更好地进行重构
  • 不要应用 Objectclone办法拷贝对象:

    • 对象的 clone 办法默认是浅拷贝
    • 若想实现深度拷贝须要重写 clone 办法实现域对象的深度遍历拷贝须要重写 clone 办法实现域对象的深度遍历拷贝
  • 类成员与办法访问控制规约:

    • 如果不容许内部间接通过 new 来创建对象, 那么构造方法必须是 private
    • 工具类不容许有 public 或者 default 构造方法
    • 类非 static 成员变量并且与子成员共享, 必须是protected
    • 类非 static 成员变量并且仅在本类中应用, 必须是private
    • 类 static 成员变量如果仅在本类中应用, 必须是private
    • 若是 static 成员变量, 思考是否为final
    • 类成员办法只供类外部调用时, 必须是private
    • 类成员办法只对继承类公开时, 限度应用protected

      日期工夫

  • 日期格式化时, 传入 pattern 中示意年份对立应用小写的yyyy

    • 日期格式化时:

      • yyyy示意当天所在的年
      • YYYY示意当天所在的周属于的年份, 一周从周日开始, 至周六完结. 如果本周跨年, 返回的 YYYY 就是下一年
  • 在日期格局中分分明大写的 M 和小写的 m, 大写的 H 和小写的 h 的含意:

    • 示意月份的是大写的M
    • 示意分钟的是小写的m
    • 24 小时的是大写的H
    • 12 小时的是小写的h
  • 获取以后的毫秒数 :System.currentTimeMillis()

    • 如果想要获取更加准确的纳秒级的工夫值, 应用System.nanoTime()
    • 针对统计工夫的场景, 举荐应用Instant
  • 不要应用 java.sql 中的相干工夫办法
  • 不要在程序中写死一年的为 365, 防止在公历平年时呈现日期转换谬误或程序逻辑谬误

    • 应用 LocalDate 办法

      // 获取往年的天数
      int daysOfThisYear = LocalDate.now().lengthOfYear();
      
      // 获取指定某年的天数
      LocalDate.of(2011, 1, 1).lengthOfYear();
  • 应用 Calendar 中的枚举值来指代月份

    • 如果应用数字, 要留神 Date,Calendar 等日期相干类的月份 month 的值在 0 – 11 之间

      汇合解决

  • hashCode 和 equals 的解决:

    • 只有重写equals, 就必须重写hashCode
    • Set中存储的是不反复的对象, 根据 hashCodeequals进行判断, 所以 Set 存储的对象必须重写这两个办法
    • 如果自定义对象作为 Map 的键, 必须重写 hashCodeequals

      • String重写了 hashCodeequals办法所以能够应用 String 对象作为 key 来应用
  • ArrayList 的 subList 后果不能够强转成 ArrayList, 否则会抛出 ClassCastException 异样:

    • subList返回的是 ArrayList 的外部类 SubList, 并不是ArrayList, 而是ArrayList 的一个视图. 对于 SubList 子列表的所有操作最终会反映到原列表上
  • 在 subList 场景中, 要留神对原汇合元素的减少或者删除, 都会导致子列表的遍历, 减少和删除产生 ConcurrentModificationException 异样
  • 应用 Map 的办法:

    • keySet()
    • values()
    • entrySet()
    • 返回汇合对象时, 不能够进行增加元素的操作, 否则会抛出 UnsupportedOperationException 异样
  • Collections 类返回的对象不能够进行增加或者删除操作:

    • 如果查问无后果, 则返回 Collection.emptyList()空集合对象. 调用方一旦进行了增加元素操作, 就会触发 UnsupportedOperationException 异样
  • 应用汇合转数组的办法, 必须应用汇合的 toArrary(T[] array), 传入的是类型齐全一样的数组, 数组的大小就是list.size()

    • 应用 toArray 带参办法, 入参调配的数组空间不够大时,toArray 办法外部将重新分配内存空间, 并返回新数组的地址;
    • 如果数组元素个数大于理论所需, 下标为[list.size()] 的元素的数组元素将被置为 null, 其余数组元素放弃原值
    • 因而最好将办法入参数组大小定义为与汇合元素个数统一

      List<String> list = new ArrayList<>();
      list.add("guan");
      list.add("bao");
      String[] array = new String[list.size];
      array = list.toArray(array);
  • 在应用 Collection 接口任何实现类的 addAll() 办法时, 都要对输出汇合参数进行NPE 判断
  • 应用工具类 Arrays.asList()将数组转换成汇合时, 不能应用这个相干的批改汇合的办法, 这个汇合的 add, remove, clear 办法会抛出 UnsupportedOperationException 异样

    • asList 的返回对象是一个 Arrays 外部类, 并没有实现汇合的批改办法
    • Arrays.asList 体现的是适配器模式, 只是转换接口, 后盾数据仍旧是数组
  • 泛型通配符 <? extends T> 来接管返回的数据, 这种写法的泛型汇合不能应用 add 办法 ;<? super T> 不能应用 get 办法, 作为接口调用赋值时会出错

    • PECS(Producer Extends Consumer Super)准则:

      • 频繁往外读取内容, 适宜应用 <? extends T>
      • 常常往里插入的, 适宜应用 <? super T>
  • 不要在 foreach 循环里进行元素的 remove 或者 add 操作

    • remove元素要应用 Iterator 形式, 如果是并发操作, 要对 Iterator 对象加锁

      List<String> list = new ArrayList<>();
      list.add("1");
      list.add("2");
      Iterator<String> iterator = list.iterator();
      while (iterator.hasNext()) {String item = iterator.next();
      if (condition) {iterator.remove();
      }
      }
  • JDK 7 当前的版本中 ,Comparator实现要满足三个条件, 否则 Arrays.sort, Collections.sort 会呈现 IllegalArgumentException 异样:

    • x, y 的比拟后果和 y, x 的比拟后果相同
    • x > y, y > z, 则 x > z
    • x = y, 则 x, z 比拟后果和 y, z 比拟后果雷同
  • JDK 7 当前的版本中, 给汇合的泛型定义时, 应用全省略, 即间接应用 <> 来指定前边曾经指定的类型
  • 汇合初始化时, 指定汇合初始值大小

    • HashMap应用HashMap(int initialCapacity) 初始化
    • initalCapacity = (须要存储的元素个数 / 负载因子) + 1. 留神负载因子 (即 loader factor) 默认为 0.75, 如果临时无奈确定初始值的大小, 设为为默认值 16
  • 应用 entrySet 遍历 Map 类汇合 kv, 而不是应用keySet 形式进行遍历

    • 如果应用 keySet 形式遍历, 其实是遍历了两次:

      • 一次转换为 Iterator 对象
      • 一次从 hashMap 中取出 key 所对应的value
    • entrySet只是遍历一次就把 keyvalue都放到了 entry 中, 效率更高
    • 如果是 JDK 8 当前的版本, 应用 Map.foreach 办法
    • 示例:

      • values()返回的是 V 值汇合, 是一个 list 汇合对象
      • keySet()返回的是 K 值汇合, 是一个 Set 汇合对象
      • entrySet()返回的是 K - V 值组合汇合
  • 要留神 Map 类汇合中的 K - V 能不能存储 null 值的状况:
汇合类 Key Value Super 阐明
Hashtable 不容许为 null 不容许为 null Dictionary 线程平安
ConcurrentHashMap 不容许为 null 不容许为 null AbstractMap 锁分段技术
TreeMap 不容许为 null 容许为 null AbstractMap 线程不平安
HashMap 容许为 null 容许为 null AbstractMap 线程不平安

因为 HashMap 的烦扰, 误以为 ConcurrentHashMap 能够置入 null 值, 其实这样会抛出 NPE 异样

  • 正当利用汇合的有序型 – sort和汇合的稳定性 – order, 防止汇合的无序性 – unsort 和不稳定性 – unorder 带来的负面影响

    • 有序性是指遍历的后果依照某种比拟规定顺次排列的
    • 稳定性是指汇合每次遍历的元素秩序是肯定的
    • ArrayList, HashMap, TreeSet
  • 利用 Set 元素惟一的个性, 能够疾速对一个汇合进行去重操作

    • 防止应用 List 的 contains 办法进行遍历, 比照, 去重操作

      并发解决

  • 获取单例对象须要保障线程平安, 其中的办法也要保障线程平安

    • 资源驱动类, 工具类, 单例工厂类都须要留神
  • 创立线程或者线程池时要指定有意义的线程名称, 不便出错时回溯
  • 线程资源必须通过线程池提供, 不容许在利用中自行显式创立线程

    • 应用线程池的益处是缩小在创立和销毁线程上所耗费的工夫以及系统资源的开销, 解决资源有余的问题
    • 如果不应用线程池, 有可能造成零碎创立大量同类线程而导致耗费完内存或者适度切换的问题
  • 线程池不容许应用 Executors 创立, 要通过 ThreadPoolExecutors 创立, 这样能够让人更加明确线程池的运行规定, 躲避资源耗费的危险

    • Executors 返回线程池对象存在以下问题:

      • FixedThreadPool 和 SingleThreadPool:

        • 容许申请队列长度为 Integer.MAX_VALUE, 可能会沉积大量申请, 导致 OOM
      • CachedThreadPool 和 ScheduledThreadPool:

        • 容许创立的线程数量为 Integer.MAX_VALUE, 可能会创立大量线程, 导致 OOM
  • SimpleDateFormat是线程不安全类, 不要定义为 static 变量. 如果定义为 static, 必须加锁, 或者应用 DateUtils 工具类

    • 留神线程平安, 应用 DateUtils, 能够进行如下解决:

      private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
        @Override
        protected DateFormat initialValue() {return new SimpleDateFormat("yyyy-MM-dd");
        }
      }
    • 在 JDK 8 中, 能够应用:

      • Instant 代替 Date
      • LocalDateTime 代替 Calendar
      • DateTimeFormatter 代替 SimpleDateFormat
  • 必须回收自定义的 ThreadLocal 变量:

    • 尤其在线程池场景下, 线程常常会被复用, 如果不清理自定义的 ThreadLocal 变量, 会影响后续业务逻辑和造成内存透露的问题
    • 尽量在代理中应用 try – finally 块进行回收

      ObjectThreadLocal.set(userInfo);
      try {...} finally {ObjectThreadLocal.remove();
      } 
  • 高并发时, 同步调用应该考量锁的性能损耗.

    • 能用无锁数据结构, 就不要用锁
    • 能用锁区块, 就不要锁整个办法体
    • 能用对象锁, 就不要用类锁

      • 尽可能使加锁的代码块工作量尽可能的小, 防止在锁代码块中调用 RPC 办法
  • 对多个资源, 数据库表, 对象同时加锁时, 须要保持一致的加锁程序, 否则可能会造成死锁

    • 如果线程一须要对 A, B, C 顺次全副加锁后才能够进行更新操作
    • 那么线程二的加锁程序也必须是 A, B, C. 否则可能会呈现死锁
  • 在应用阻塞期待获取锁的形式中:

    • 必须在 try 代码块之外

      • 如果 lock 办法在 try 代码块之内, 可能因为其它办法抛出异样 , 导致在 finally 代码块中 ,unlock对未加锁的对象解锁, 会调用 AQStryRelease办法, 抛出 IlleagalMonitorStateException 异样
    • 必须在加锁办法与 try 代码块之间没有任何可能抛出异样的办法调用, 防止加锁胜利后, 在 finally 中无奈解锁

      • 如果在 lock 办法与 try 代码块之间的办法调用抛出异样, 那么无奈解锁, 造成其它线程无奈获取锁
    • Lock 对象的 l ock办法实现中可能抛出 unchecked 异样, 导致 unlock 对未加锁的对象解锁, 会调用 AQStryRelease办法, 抛出 IlleagalMonitorStateException 异样

      Lock lock = new XxxLock();
      lock.lock();
      try {...} finally {lock.unlock();
      }
  • 在应用尝试机制来获取锁的形式中:

    • 进入业务代码之前, 必须 先判断以后线程是否持有锁
    • 锁的开释规定与锁阻塞期待的形式雷同

      • Lock对象的 unlock 办法在执行时, 会调用 AQStryRelease办法, 如果 以后线程不持有锁, 则抛出 IllegalMonitorStateException 异样

        Lock lock = new XxxLock();
        boolean isLocked = lock.tryLock();
        if (isLocked) {
        try {...} finally {lock.unlock();
        }
        }
  • 并发批改同一记录时, 防止更新失落, 须要加锁:

    • 在应用层加锁
    • 在缓存加锁
    • 在数据库中加锁
    • 应用 version 作为更新根据

      • 如果每次拜访概率小于20%, 举荐应用乐观锁
      • 否则的话, 应用乐观锁
      • 乐观锁的重试次数不得小于 3
  • 多线程并行处理定时工作时:

    • Timer运行多个 TimerTask 时只有其中之一没有捕捉抛出的异样, 工作便会主动终止运行
    • 应用 ScheduleExecutorService 则没有这个问题
  • 乐观锁 遵循 一锁二判三更新四开释 的准则
  • 应用 CountDownLatch 进行异步转同步操作:

    • 每个线程退出前必须调用 countDown 办法
    • 线程执行代码留神 catch 异样, 确保 counDown 办法被执行到
    • 防止主线程无奈执行至 await 办法, 直到超时才返回后果

      • 子线程抛出的异样堆栈, 不能在主线程 try-catch 失去异样
  • 防止 Random 实例被多线程应用, 共享该实例是线程平安的, 然而会因为竞争同一个 seed 导致性能降落

    • Random 实例:

      • java.util.Random的实例
      • Math.random() 的形式
    • 在 JDK 7 后, 能够间接应用ThreadLoalRandom
  • 在并发的场景下, 通过双重查看锁 double-check locking 实现提早初始化来优化问题隐患:

    • 将指标属性申明为 volatile
  • volatile用于解决多线程内存不可见问题:

    • 对于一写多读, 能够解决变量同步问题
    • 对于多写, 无奈解决线程平安问题
    • 对于 count++ 操作, 应用如下的类实现:

      AtomicInteger count = new AtomicInteger();
      count.addAndGet(1);
    • 在 JDK 8 后, 举荐应用 LongAdder 对象, 比 AtomicLong 性能更好, 因为能够缩小乐观锁的重试次数
  • HashMap 在容量不够进行 resize 操作时会因为高并发可能呈现死锁, 导致 CPU 减少:

    • 应用其它的数据结构
    • 加锁
  • ThreadLocal无奈解决共享对象的更新问题, 倡议要应用 static 进行润饰:

    • 这个变量是针对一个线程内所有操作共享的
    • 因而设置为动态变量, 所有的此类实例共享此动态变量
    • 即这个变量在类第一次被应用时装载, 只调配一块内存空间, 只有这个线程内定义的所有此类的对象都能够操作这个变量

      管制语句

  • 在一个 switch 块内:

    • 每个 case 要通过 break 或者 return 来终止
    • 或者正文阐明程序将继续执行到哪一个 case 为止
    • 必须蕴含一个 default 语句并且放在最初, 即便是空代码
  • 当 Switch 括号内的变量类型为 String 并且此变量为内部参数时, 必须进行 null 判断
  • if, else, for, while, do语句中必须应用大括号, 即便只有一行代码, 防止采纳单行编码模式
  • 三目运算符: condition ? 表达式 1 : 表达式 2 要留神表达式 1 和表达式 2 在类型对齐时, 可能因主动拆箱导致 NPE 异样

    • 触发类型对齐的拆箱操作:

      • 表达式 1 或者表达式 2 只有有一个原始类型
      • 表达式 1 或者表达式 2 类型不统一, 会强制拆箱升级成示意范畴更大的那个类型
  • 在高并发的场景中, 防止应用 “等于” 判断作为中断或者退出的条件

    • 因为如果并发管制没有解决好, 容易产生等值判断被 “击穿” 的状况 . 要应用大于或者小于区间判断条件来代替
    • 示例: 判断残余数量等于 0 时, 当数量等于 0 的过程中, 因为并发处理错误导致数量霎时变成了正数, 这样的话, 解决无奈终止
  • 表白异样的分支时, 不要应用 if – else 形式, 改写为

    if (condition) {
      ...
      return obj;
    }
    // 而后写 else 的业务解决逻辑

    对于超过 3 层的 if – else 的逻辑判断代码能够应用卫语句, 策略模式, 状态模式等实现

  • 除罕用的办法 :getXxx, isXxx等, 不要在条件判断中执行简单的语句, 将简单逻辑判断的后果赋值给一个有意义的布尔变量名, 以进步可读性

    • 很多 if 语句内的逻辑相当简单, 须要剖析表达式的最终后果, 能力明确什么样的条件执行什么样的语句
  • 不要在其它表达式中(尤其时条件表达式), 插入赋值语句
  • 循环体中的语句要考量性能, 以下操作尽量挪动至循环体外解决:

    • 定义对象, 变量
    • 获取数据库连贯
    • 进行不必要的 try – catch 操作(思考这个 try – catch 操作是否能够挪动至循环体外)
  • 防止应用取反逻辑运算符

    • 取反逻辑运算符不利于疾速了解
    • 取反逻辑写法必然存在对应的正向逻辑写法
  • 接口入参爱护: 这种场景常见的是用作批量操作的接口
  • 参数校验:

    • 须要 进行参数校验的情景:

      • 调用频次低的办法
      • 执行工夫开销很大的办法

        • 此情景中, 参数校验的工夫简直能够忽略不计
        • 然而如果因为参数谬误导致两头执行被退回, 或者谬误, 就得失相当
      • 须要极高稳定性和可用性的办法
      • 对外提供凋谢接口, 无论是 RPC, API, HTTP 接口
      • 敏感权限入口
    • 不须要 进行参数校验的情景:

      • 极有可能被循环调用的办法. 然而在办法阐明里必须注明内部参数的查看要求
      • 底层调用频度比拟高的办法
      • 被申明成 private 只会被本人代码所调用的办法. 如果可能确定调用办法的代码传入参数曾经做过查看或者必定不会有问题, 此时能够不校验参数

        正文规约

  • 类, 类属性, 类办法 的正文必须应用 Javadoc 标准, 应用 /* xxx / 格局, 不容许应用 // xxx 形式
  • 所有 形象办法, 包含 接口中的办法, 都必须应用 Javadoc 正文, 除了 返回值, 参数, 异样阐明 外, 还必须指出 该办法做了什么事件, 实现什么性能. 对子类的实现要求以及调用的注意事项须要一并阐明
  • 所有的类都必须增加创建者和创立日期
  • 办法外部正文:

    • 单行正文: 在被正文语句上方另起一行, 应用 // 正文
    • 多行正文: 应用 / / 正文, 留神与代码对齐
  • 所有枚举类型字段必须要有正文, 阐明每个数据项的用处
  • 当程度足够高时, 该当应用英文正文. 否则就用中文把问题说分明, 只有将 专有名词 关键字 放弃英文原文即可
  • 代码批改的同时, 正文也要进行相应的批改, 尤其是 参数, 返回值, 异样, 外围逻辑等. 要放弃代码与正文更新同步
  • 审慎正文代码:

    • 正文的代码要进行具体的阐明, 而不是简略的正文
    • 如果无用, 则应该删除
  • 正文的要求:

    • 可能精确反映设计思维和代码逻辑
    • 可能形容业务含意, 可能迅速理解到代码背地的信息
  • 好的命名, 代码构造是自解释的, 正文保障精简精确, 表白到位
  • 非凡的正文标记, 须要注明标记人与标记工夫. 留神及时处理这些标记, 通过标记扫描, 常常清理此类标记. 线上故障有时候就源于这些标记处的代码

    • 待办事宜TODO : (标记人, 标记工夫, [预处理工夫])

      • 示意要实现, 但目前尚未实现的性能. 这实际上是一个 Javadoc 的标签. 只能利用于 类, 接口, 办法
    • 谬误, 不能工作FIXME : (标记人, 标记工夫, [预处理工夫])

      • 在正文中用 FIXME 标记某段代码是谬误的, 而且不能工作, 须要及时纠正状况

        前后拆散

  • 前后端交互的 API, 须要明确 协定, 域名, 门路, 申请办法, 申请内容, 状态码, 响应体:

    • 协定: 生产环境必须应用HTTPS
    • 门路: 每一个 API 须要对应一个门路, 示意 API 具体的申请地址

      • 代表资源, 只能为名词, 举荐应用复数, 不能为动词, 因为申请办法曾经表白动作含意
      • URL门路不能应用大写, 单词如果须要宰割, 对立应用下划线
      • 门路禁止携带申请内容类型的后缀 : “.json”,”.xml”, 通过 accept 头表白即可
    • 申请办法: 对具体操作的定义

      • GET: 获取
      • POST: 新增
      • PUT: 批改
      • DELETE: 删除
    • 申请内容:

      • URL带的参数必须无敏感信息或者合乎平安要求
      • body里带参数时必须设置Content-Type
    • 响应体: 响应体 body 能够搁置多种数据类型, 由 Content-Type 头来确定
  • 前后端数据列表相干的接口返回时, 如果为空, 则返回空数组 [] 或者空集合 {}
  • 服务端产生谬误时, 返回给前端的响应信息必须蕴含 HTTP 状态码, errorCode, errorMessage, 用户提示信息 四个局部:

    • HTTP 状态码: 浏览器

      • 200 OK : 表明该申请被胜利实现, 所申请的资源发送到客户端
      • 401 Unauthorized : 申请要求身份验证, 通常是须要登录而用户未登录的状况
      • 403 Forbidden : 服务器拒绝请求, 通常是机密信息或复制其余登录用户链接拜访服务器的状况
      • 404 Not Found : 服务器无奈获得所申请的网页. 申请的资源不存在
      • 500 Internal Server Error: 服务器外部谬误
    • errorCode: 前端开发
    • errorMessage: 谬误排查人员
    • 用户提示信息: 用户. 要求简短清晰, 提醒敌对, 疏导用户进行下一步操作或者解释谬误起因, 上下文环境, 举荐操作
  • errorMessage是前后端谬误追踪机制的体现, 能够在前端输入到 type=”hidden” 的文字类控件或者用户端的日志中, 这样可能疾速地定位问题
  • 对于须要应用超大整数的场景, 服务端一律应用 String 字符串返回类型, 禁止应用 Long 类型

    • Java服务端如果间接返回 Long 整型数据给前端 ,JS会主动转换为 Number 类型:

      • Number 类型: 双精度浮点数, 示意原理和取值范畴等同于 Java 中的Double
      • Long 类型: 示意的最大值为 2^63^ -1. 超过2^53^(9007199254740992) 的数值转化为JSNumber时, 有些数值会有精度损失

        • Long 取值范畴内, 任何 2 的指数次整数都是相对不会存在精度损失的, 所以说精度损失是一个概率问题
        • 如果浮点数尾数位与指数位空间不限, 则能够准确示意任何整数. 然而双精度浮点数的尾数位只有 52
    • 示例: 通常在订单号或者交易号大于等于 16 位, 大概率会呈现前后端单据不统一的状况. 比方后端的 362909601374617692 到前端则是362909601374617660
  • HTTP申请通过 URL 传递参数时, 不能超过 2048 个字节:

    • 不同浏览器对于 URL 的最大长度限度略有不同, 并且对超出最大长度的解决逻辑也有差别. 2048 字节是取所有浏览器的最小值
  • HTTP申请通过 body 传递内容时, 必须管制长度, 超出最大长度后, 后端解析会出错:

    • Nginx默认限度是1MB
    • Tomcat默认限度是2MB
    • 当的确有业务须要传较大内容时, 能够通过调大服务器端的限度
  • 在分页场景中, 用户输出参数小于1, 则前端返回第一页参数给后端. 后端发现用户输出的参数大于总页数, 间接返回最初一页
  • 服务器外部重定向必须应用 forward. 内部重定向地址必须应用URL 对立代理模块生成, 否则会因为线上采纳 HTTPS 协定而导致浏览器提醒 “ 不平安 ”, 并且还会带来 URL 保护不统一的问题
  • 服务器返回信息必须被标记是否能够缓存, 如果缓存, 客户端可能会重用之前的申请后果

    • 缓存有利于缩小交互次数, 缩小交互的均匀提早
    • 示例: http 1.1,s-maxage告诉服务器进行缓存, 工夫单位为秒:

      • response.setHeader(“Cache-Control”, “s-maxage=” + cacheSeconds)
  • 服务端返回的数据, 应用 JSON 格局而非XML :

    • HTTP反对应用不同的输入格局, 例如 纯文本,JSON,CSV,XML,RSS以至HTML
    • 在应用面向用户的服务, 应该抉择 JSON 作为通信中应用的规范数据交换格局, 包含申请和响应

      • application/JSON是一种通用的 MIME 类型, 具备实用, 精简, 易读的特点
  • 前后端的工夫格局对立为 “yyyy-MM-dd HH:mm:ss”, 对立为GMT

    其它留神

  • 在应用正则表达式时, 利用好预编译性能, 能够无效放慢正则匹配速度
  • 不要在办法体内定义
  • 二方库中能够定义枚举类型, 参数能够应用枚举类型, 然而接口返回值不容许应用枚举类型或者蕴含枚举类型的 POJO 对象
  • velocity调用 POJO 类的属性时, 间接应用属性名取值即可, 模板引擎会主动按标准调用 POJO 的getXxx(), 如果是 boolean 根本类型变量 ,boolean 命名不要加 is 前缀, 会主动调用 isXxx 办法. 如果是 Boolean 包装类对象, 优先调用getXxx() 办法
  • 后盾输送给页面变量必须加上 $ ! {var}, 留神两头的感叹号

    • 如果 var 等于 null 或者不存在, 那么 ${var}会间接显示在桌面上
  • 留神 Math.random() 这个办法返回是double 类型, 取值范畴0 <= x <1(可能取到零值, 留神除零)

    • 如果获取整数类型的随机数, 不须要将 x 放大 10 的若干倍而后取整, 间接应用 Random 对象的 nextInt 或者 nextLong 办法
  • 获取以后秒数System.currentTimeMillis(), 不是应用 new Date().getTime()

    • 如果想获取更加准确的纳秒级工夫值, 应用System.nanoTime() 的形式
    • 在 JDK 8 当前, 针对统计工夫等常景, 须要应用Instant
  • 不要在视图模版中退出任何简单逻辑, 依据 MVC 实践, 视图的职责是展现, 不要有模型和控制器的代码逻辑
  • 任何数据结构的结构和初始化, 都应指定大小, 防止数据结构有限增长吃光内存
  • 及时清理不再应用的代码段或配置信息

    • 对于垃圾代码或过期配置, 坚定清理洁净, 防止程序适度臃肿, 代码冗余
    • 对于临时被正文掉, 后续可能复原应用的代码片段, 在正文代码的上方, 对立规定应用 三个斜杠 /// 来阐明凝视掉代码的理由
正文完
 0