编程规约

接口和实现类的命名规则

对于Service和DAO类,基于SOA(Software-oriented Architecture)理念,暴露出来的服务一定是接口,内部的实现类用Impl后缀与接口区别。

接口: XXXService 实现类:XXXServiceImpl

例子: 接口: CacheService 接口实现类:CacheServiceImpl

各层命名规约

Service/DAO层方法命名规约如下

  • 获取单个对象的方法用get作为前缀。 eg: getObject()

  • 获取多个对象的方法用list作为前缀。 eg: listObjects()

  • 获取统计值的方法用count作为前缀。eg: countMessage()

  • 插入的方法用insert/save作为前缀。eg: InsertUserInfo()

  • 删除的方法用delete/remove作为前缀。eg:DeleteUserInfo()

  • 修改更新的方法用update作为前缀。eg: updateUserInfo()

领域模型命名规约如下

  • 数据对象: XXXDO xxx为数据表名称

  • 数据传输对象: XXXDTO xxx为业务领域相关的名称

  • 展示对象:XXVO xxx为要将展示对象传输的网页名称

POJO是DO、DTO、BO、VO的通常,禁止命名为: XXXPOJO

🌈如果变量值仅仅在一个范围内发生变化,则使用枚举类型

枚举类型是特殊的常量类,其构造方法默认被私有化

public enum DayEnum {
MONDAY(1),THUESDAY(2),WEDENSDAY(3),THURSDAY(4),FRIDAY(5),SATURDAY(6),SUNDAY(7);
private int seq;
DayEnum(int seq){
this.seq = seq;
}
public int getSeq(){
return seq;
}
}

if / for / while / switch / do 等保留字与括号之间都必须加空格。

for (int i = 0; i < 100; i++) {
}

换行原则: 单行字符数不超过120个,超出则需要换行,换行时遵循如下的原则:

1) 第二行相对于第一行缩进4个空格,从第三行开始,不在持续缩进。
2) 运算符与下文一起换行
3) 方法调用的点符号与下文一起换行
4) 方法调用中的多个参数需要换行的时候,在**逗号后**换行
5) 在括号前不要换行
StringBuilder sb = new StringBuilder();
// 超过120个字符串的情况下,换行缩进4个空格
// 点好和方法名称一起换行
sb.append("zx").append("zx").append("web")....
.append("java") ...
.append("python");

方法参数在定义和传入时,多个参数逗号后边必须加空格

private static double calculateAverageAcceleration(double initialV, double finalV, double time){
return (finalV - initialV) / time;
}

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

"tempValue".equals(object);

很大的坑🌈 对于所有整型包装类对象之间的值的比较,全部使用equals方法进行比较

Integer num01 = 129;
Integer num02 = 129;
System.out.println(num01 == num02); // false
System.out.println(num01.equals(num02)); // true
Integer num03 = 127;
Integer num04 = 127;
System.out.println(num03 == num04); // true
System.out.println(num03.equals(num04)); // true

对于Integer var = ? -128 ~ 127范围之内的赋值,Integer对象是在IntegerCache.cache中产生的,会复用已有对象。
-128~127区间内的Integer值可以直接使用 ==来判断,但是这个区间之外的数据就要牵扯到堆内存空间的开辟此时 ==是过来比较地址而不是其堆中的内容
要使用equals()方法来比较(无论如何都是比较其背后的value)

二进制无法精确表达十进制小数为了实现浮点数之间的表达可以采用差值表达法

  • 指定一个误差范围,两个浮点数的差值在此范围以内,则认为是相等的。
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 numA = new BigDecimal("1.0");
BigDecimal numB = new BigDecimal("0.9");
BigDecimal numC = new BigDecimal("0.8");
BigDecimal x = numA.subtract(numB);
BigDecimal y = numB.subtract(numC);
System.out.println("x: " + x + ", y: " + y); // x: 0.1, y: 0.1
if(x.equals(y)){
System.out.println("true");
}

🚫禁止使用构造方法BigDecimal(double)的方式把double值转化为BigDecimal对象

优先推荐入参为String的构造方法,或使用BigDecimal的valueOf方法。

BigDecimal good01 = new BigDecimal("0.111");
BigDecimal good02 = BigDecimal.valueOf(0.111);

🌈 基本数据类型和包装数据类型的使用规则

  • 所有的POJO类都必须使用包装数据类型
  • RPC方法的返回值和参数必须使用包装数据类型
  • 所有巨变变量使用基本数据类型

POJO类属性没有初始值,提醒使用者在需要使用时,必须显式地进行赋值。
为什么要在POJO类中使用包装类型。
答:数据库查询的结果有可能为null,因为自动拆箱,所以基本数据类型接收有NPE风险。

包装数据类型的null值,能够产生额外信息

🌈 在定义DO/DTO/VO等POJO类时,不要添加默认值。

🌈 在构造方法之中不要添加任何业务逻辑,如果有初始化逻辑,请放在init方法之中。

public方法是类的调用者和维护者最关心的方法
protected方法虽然只有子类关心(也有可能包含核心方法)
private方法一般不用太关心,是一个黑盒实现;

字符串的循环拼接一律采用append方法来实现

String userInfo01 = "";
for(int i = 0; i < 10; i++){
userInfo01 = userInfo01 + i;
}
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 10; i++){
sb.append(i);
}
System.out.println("userinfo: " + userInfo01); // userinfo: 0123456789
System.out.println("info: " + sb); // info: 0123456789

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

下列情况不准许使用final关键字:

  1. 不允许被继承的类 eg:String
  2. 不允许被重写的方法
  3. 不允许在运行中修改局部变量
  4. 不允许修改引用的域对象
  5. 不允许代码上下文重复使用同一个变量名称

类成员和方法访问控制从严

修饰符 本类 子类 其他
public ✔️ ✔️ ✔️ ✔️
protected ✔️ ✔️ ✔️
无(default) ✔️ ✔️
private ✔️

不允许外部直接通过new方法来创建对象 ==》 构造方法私有化(the privatization of constructor method)
工具类中不允许出现 public或default 修饰的方法同时也要保证构造方法私有化 eg. Math.random() 工具类直接通过类名调用工具方法
类中non-static成员变量与子类共享必须采用protected修饰
类中non-static成员变量只用于本类中采用private修饰
类static成员变量如果仅在本类中使用就必须限制为private修饰
若是static成员变量必须考虑使用final eg: private static final double INTEREST_RATE = 23.12;(常量的定义)
类成员方法仅供类内部调用,必须限制为private
类成员方法仅供继承类调用,必须限制为protected

过宽的访问范围不利于解耦。将变量比作小孩,要保证变量的活动范围在自己的视线之内。

集合处理

关于hashCode和equals的处理,遵循以下原则:

  1. 重写equals方法就必须同时重写hashcode方法
  2. Set用于存储非重复对象,依据hashcode和equals方法进行判断,所以set存储必须重写这两个方法
  3. 自定义对象作为Map的键,就必须重写这两个方法。

hashcode主要是set集合使用,是用于判断对象是否”可能“相等的快捷办法,以解决大集合的问题。举例来说,如果一个一万个元素的集合加入一个元素,如果是一个新元素,那么必须要equal一万次才能加入。所以采用hashcode,hashcode的思路是如果equal,则hashcode一定要相等,反过来则不一定;所以如果hashcode不相等,那么不一定不equal,这跟md5的hash来判别密码是一个道理。hashcode用64位整数,这样可以建立一个索引,新加入元素,先判断这个新元素的hashcode是否存在,如果不存在,肯定不相等,加入set中;如果存在,则与已有的hashcode的若干个元素比较,这样大大简化了set的equal操作。

ArrayList的subList结果不是ArrayList,而是ArrayList的一个视图,对于subList子列表的所有操作最终会反映到原有列表上。

在subList场景中,【⚠️Warning】高度注意对原集合元素的增加或删除,均会导致子列表的遍历、增加、删除都会产生ConcurrentModificationException

List<String> usernameList = new ArrayList<String>();
usernameList.add("wangzz");
usernameList.add("wangbo");
usernameList.add("wanghu");
usernameList.add("Felix");
usernameList.add("LittleRed");
// use usernameList.toArray() with arg
String[] usernameArray = new String[usernameList.size()];
usernameList.toArray(usernameArray);
System.out.println("usernameArray: " + Arrays.toString(usernameArray)); // usernameArray: [wangzz, wangbo, wanghu, Felix, LittleRed]
// use usernameList.toArray() with no-arg
Object[] objects = usernameList.toArray(); // return an array with object type

Array.asList(T a[]) 返回的是ArrayList中的内部类而且不仅添加任何数据操作否则会抛出UnsupportedOperationException

不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。

List<String> usernameList = new ArrayList<String>();
usernameList.add("wangzz");
usernameList.add("wanghu");
usernameList.add("wangbo");
Iterator<String> usernameIterator = usernameList.iterator();
while(usernameIterator.hasNext()){
System.out.println("username: " + usernameIterator.next());
usernameIterator.remove();
}

使用entrySet遍历Map类集合Key : Value, 而不是keySet方式遍历

keySet的使用会遍历两次,一次是转为Iterator对象,另一次是从HashMap中取出key所对应的value。

  • values()返回的是V值集合,是一个list集合对象;
  • keySet()返回的是K值集合,是一个Set集合对象;
  • entrySet()返回的是K/V值组合集合。

高度注意Map类集合K/V能不能存储null值的情况。

| 集合类 | Key | Value | Super | 说明 |
| ---- | ---- | ---- | ---- | ---- | ---- |
| Hashtable | 不允许为null | 不允许为null | Dictionary | 线程安全 |
| ConcurrentHashMap | 不允许为null | 不允许为null | AbstractMap | 锁分段技术(JDK8:CAS) |
| TreeMap | 不允许为null | 允许为null | AbstractMap | 线程不安全 |
| HashMap | 允许为null | 允许为null | AbstractMap | 线程不安全 |

只有HashMap和TreeMap中有为null的情况出现其他都不允许为null

posted @   Felix_Openmind  阅读(92)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
*{cursor: url(https://files-cdn.cnblogs.com/files/morango/fish-cursor.ico),auto;}
点击右上角即可分享
微信分享提示