java开发手册

目录

一、编程规约

1、命名风格

1.1 领域模型命名规约

1、数据对象:xxxDO,xxx即为数据表名。
2、数据传输对象:xxxDTO,xxx为业务领域相关的名称。
3、展示对象:xxxVO,xxx一般为网页名称。
4、POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO。

1.2 中括号是数组类型的一部分,数组定义如下:String[] args; 反例:请勿使用 String args[]的方式来定义。

1.3 枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。

说明:枚举其实就是特殊的常量类,且构造方法被默认强制是私有。 正例:枚举名字:DealStatusEnum,成员名称:SUCCESS / UNKOWN_REASON。

2、常量定义

2.1 常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包 内共享常量、类内共享常量。

1、跨应用共享常量:放置在二方库中(通用模块),通常是client.jar中的constant目录下。
2、应用内共享常量(应用间通用):放置在一方库(base包)中,通常是子模块中的constant目录下。
3、子工程内部共享常量:即在当前子工程的constant目录下。
4、包内共享常量:即在当前包下单独的constant目录下。
5、类内共享常量:直接在类内部private static final定义。

2.2如果变量值仅在一个固定范围内变化用 enum 类型来定义。

春夏秋冬

public enum SeasonEnum {
   SPRING(1), 
   SUMMER(2), 
   AUTUMN(3), 
   WINTER(4);

   int seq;
   SeasonEnum(int seq){
       this.seq = seq;
   }
}

注:enum为枚举类型SeasonEnum.SPRING

2.3 long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟数字 1 混淆,造成误解。

说明:Long a = 2l; 写的是数字的 21,还是 Long 型的 2?

3、代码格式

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

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

1) 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
2) 运算符与下文一起换行。 3) 方法调用的点符号与下文一起换行。 4) 在多个参数超长,逗号后进行换行。 5) 在括号前不要换行,见反例。

  1. 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进。
  2. 运算符与下文一起换行。
  3. 方法调用的点符号与下文一起换行。
  4. 在多个参数超长,逗号后进行换行。
  5. 在括号前不要换行。

//正例: 
StringBuffer sb = new StringBuffer();  //超过 120 个字符的情况下,换行缩进 4 个空格,并且方法前的点符号一起换行  
sb.append("zi").append("xin")...    
	.append("huang")...  
	.append("huang")...  
	.append("huang");  
//反例: 
StringBuffer sb = new StringBuffer();  //超过 120 个字符的情况下,不要在括号前换行  
sb.append("zi").append("xin")...append      
	("huang");   
 
//参数很多的方法调用可能超过 120 个字符,不要在逗号前换行  
method(args1, args2, args3, ...      
, argsX);

3.3 缩进采用 4 个空格,禁止使用 tab 字符。

说明:如果使用 tab 缩进,必须设置 1 个 tab 为 4 个空格。IDEA 设置 tab 为 4 个空格时, 请勿勾选Use tab character ;而在 eclipse 中,必须勾选insert spaces for tabs

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

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

正例:下例中实参的"a",后边必须要有一个空格。
method(“a”, “b”, “c”);

3.7 IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式, 不要使用 windows 格式。

4、OOP规约

4.1 接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。

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

正例:“test”.equals(object);

反例:object.equals(“test”);

说明:推荐使用java.util.Objects#equals (JDK7 引入的工具类)

		String test1 = "test";
        String test2 = null;
        System.out.println(Objects.equals(test1,test2));

Objects.equals会先判断变量是否为空

4.3 POJO 类必须写 toString 方法。如果继承了另一个 POJO 类,注意在前面加一下 super.toString。

说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排 查问题。

4.4 循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。

说明:反编译出的字节码文件显示每次循环都会 new 出一个 StringBuilder 对象,然后进行 append 操作,最后通过 toString 方法返回 String 对象,造成内存资源浪费。

String a = “abc” + “bcd” + “efg” 会创建出5个对象

StringBuild是JDK1.5新增加的一个类,与StringBuffer具有相同的操作。

区别在于:StringBuffer是线程安全的类。StringBuild不是线程安全的类,在单线程中性能要比StringBuffrer高。

4.5 所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较

说明:对于 Integer var=?在-128 至 127 之间的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行 判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑, 推荐使用 equals 方法进行判断。

4.6 关于基本数据类型与包装数据类型的使用标准如下:

  1. 所有的 POJO 类属性必须使用包装数据类型。
  2. RPC 方法的返回值和参数必须使用包装数据类型。
  3. 所有的局部变量【推荐】使用基本数据类型。
    说明:POJO 类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE 问题,或者入库检查,都由使用者来保证。 正例:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。 反例:比如显示成交总额涨跌情况,即正负 x%,x 为基本数据类型,调用的 RPC 服务,调用 不成功时,返回的是默认值,页面显示:0%,这是不合理的,应该显示成中划线-。所以包装 数据类型的 null 值,能够表示额外的信息,如:远程调用失败,异常退出。

4.7 定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值。

反例:POJO 类的 gmtCreate 默认值为 new Date();但是这个属性在数据提取时并没有置入具 体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。

4.8 使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无 内容的检查,否则会有抛 IndexOutOfBoundsException 的风险。

说明:
String str = “a,b,c,”;
String[] ary = str.split(","); //预期大于 3,结果是 3 System.out.println(ary.length);

4.9 final 可提高程序响应效率,声明成 final 的情况:

  1. 不需要重新赋值的变量,包括类属性、局部变量。
  2. 对象参数前加 final,表示不允许修改引用的指向。
  3. 类方法确定不允许被重写。

4.10 类成员与方法访问控制从严

  1. 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。
  2. 工具类不允许有 public 或 default 构造方法。
  3. 类非 static 成员变量并且与子类共享,必须是 protected。
  4. 类非 static 成员变量并且仅在本类使用,必须是 private。
  5. 类 static 成员变量如果仅在本类使用,必须是 private。
  6. 若是 static 成员变量,必须考虑是否为 final。
  7. 类成员方法只供类内部调用,必须是 private。
  8. 类成员方法只对继承类公开,那么限制为 protected。

说明:任何类、方法、参数、变量,严控访问范围。过宽泛的访问范围,不利于模块解耦。思 考:如果是一个 private 的方法,想删除就删除,可是一个 public 的 Service 方法,或者一 个 public 的成员变量,删除一下,不得手心冒点汗吗?变量像自己的小孩,尽量在自己的视 线内,变量作用域太大,如果无限制的到处跑,那么你会担心的。

5、集合处理

5.1 关于 hashCode 和 equals 的处理,遵循如下规则:

  1. 只要重写 equals,就必须重写 hashCode。
  2. 因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的 对象必须重写这两个方法。
  3. 如果自定义对象做为 Map 的键,那么必须重写 hashCode 和 equals。
    正例:String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象 作为 key 来使用。

在Java API文档中关于hashCode方法有以下几点规定

  1. 在java应用程序执行期间,如果在equals方法比较中所用的信息没有被修改,那么在同一个对象上多次调用hashCode方法时必须一致地返回相同的整数。如果多次执行同一个应用时,不要求该整数必须相同。
  2. 如果两个对象通过调用equals方法是相等的,那么这两个对象调用hashCode方法必须返回相同的整数。
  3. 如果两个对象通过调用equals方法是不相等的,不要求这两个对象调用hashCode方法必须返回不同的整数。但是程序员应该意识到对不同的对象产生不同的hash值可以提供哈希表的性能。

通过前面的分析,我们知道在Object类中,hashCode方法是通过Object对象的地址计算出来的,因为Object对象只与自身相等,所以同一个对象的地址总是相等的,计算取得的哈希码也必然相等,对于不同的对象,由于地址不同,所获取的哈希码自然也不会相等。因此到这里我们就明白了,如果一个类重写了equals方法,但没有重写hashCode方法,将会直接违法了第2条规定,这样的话,如果我们通过映射表(Map接口)操作相关对象时,就无法达到我们预期想要的效果。

***5.2 ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException 异常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ;

说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。

***5.3 在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增 加、删除均产生 ConcurrentModificationException 异常。

5.4 使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全 一样的数组,大小就是 list.size()。

反例:
直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它 类型数组将出现 ClassCastException 错误。
正例:
List list = new ArrayList(2);
list.add(“guan”);
list.add(“bao”);
String[] array = new String[list.size()];
array = list.toArray(array);
说明:使用 toArray 带参方法,入参分配的数组空间不够大时,toArray 方法内部将重新分配 内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[ list.size() ]的数组 元素将被置为 null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素 个数一致。

5.5 使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方 法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。

说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[] { “a”, “b” };
List list = Arrays.asList(str);
第一种情况:list.add(“c”); 运行时异常。
第二种情况:str[0]= “gujin”; 那么 list.get(0)也会随之修改。

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

//反例: 
List<String> a = new ArrayList<String>();      
a.add("1");      
a.add("2");      
for (String temp : a) {          
	if("1".equals(temp)){              
	a.remove(temp);          
	}      
} 
//说明:以上代码的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”,会是同样的 结果吗?
//正例: 
Iterator<String> it = a.iterator();  
while(it.hasNext()){              
	String temp =  it.next();                       
	if(删除元素的条件){                              
	it.remove();                 
	}      
}  

5.7 使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。

说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效 率更高。如果是 JDK8,使用 Map.foreach 方法。
正例:values()返回的是 V 值集合,是一个 list 集合对象;keySet()返回的是 K 值集合,是 一个 Set 集合对象;entrySet()返回的是 K-V 值组合集合。

5.8 高度注意 Map 类集合 K/V 能不能存储 null 值的情况,如下表格:

集合类KeyValueSuper说明
Hashtable不允许为 null不允许为 nullDictionary线程安全
ConcurrentHashMap不允许为 null不允许为 nullAbstractMap分段锁技术
TreeMap不允许为 null允许为 nullAbstractMap线程不安全
HashMap允许为 null允许为 nullAbstractMap线程不安全

反例: 由于 HashMap 的干扰,很多人认为 ConcurrentHashMap 是可以置入 null 值,注意存储 null 值时会抛出 NPE 异常。

6、并发处理

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

说明:Executors 返回的线程池对象的弊端如下:

  1. FixedThreadPoolSingleThreadPool:
    允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
  2. CachedThreadPoolScheduledThreadPool:
    允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

6.2 SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为 static,必须加锁,或者使用 DateUtils 工具类。

//正例:注意线程安全,使用 DateUtils。亦推荐如下处理: 
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {        
	@Override        
	protected DateFormat initialValue() {            
		return new SimpleDateFormat("yyyy-MM-dd");        
	}    
};  

说明:如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter代替Simpledateformatter,官方给出的解释:simple beautiful strong immutable thread-safe。

6.3 并发修改同一记录时,避免更新丢失,要么在应用层加锁,要么在缓存加锁,要么在 数据库层使用乐观锁,使用 version 作为更新依据。

说明:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次 数不得小于 3 次。

6.4 多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获 抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。

6.5 使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 catch 异常,确保 countDown 方法可以执行,避免主线程无法执行 至 countDown 方法,直到超时才返回结果。

6.6 避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed 导致的性能下降。

说明:Random 实例包括 java.util.Random 的实例或者 Math.random()实例。
正例:在 JDK7 之后,可以直接使用 API ThreadLocalRandom,在 JDK7 之前,可以做到每个 线程一个实例。

6.7 通过双重检查锁(double-checked locking)(在并发场景)实现延迟初始化的优化问题隐患(可参考 The “Double-Checked Locking is Broken” Declaration),推荐问题 解决方案中较为简单一种(适用于 JDK5 及以上版本),将目标属性声明为 volatile 型。

详解:Java中的双重检查锁(double checked locking)

//反例: 
class Foo {   
	private Helper helper = null;  
	public Helper getHelper() {  
		if (helper == null)   
		synchronized(this) {      
			if (helper == null)         
			helper = new Helper();    
		}      
		return helper;  
	}  
	// other functions and members...  
}

6.8 volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题, 但是如果多写,同样无法解决线程安全问题。如果是 count++操作,使用如下类实现: AtomicInteger count = new AtomicInteger(); count.addAndGet(1); 如果是 JDK8,推 荐使用 LongAdder 对象,比 AtomicLong 性能更好(减少乐观锁的重试次数)。

HashMap中的并发resize出现死链

6.9 ThreadLocal 无法解决共享对象的更新问题,ThreadLocal 对象建议使用 static 修饰。这个变量是针对一个线程内所有操作共有的,所以设置为静态变量,所有此类实例共享 此静态变量 ,也就是说在类第一次被使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量。

7、控制语句

7.1 在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程 序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且 放在最后,即使它什么代码也没有。

7.2 在 if/else/for/while/do 语句中必须使用大括号,即使只有一行代码,避免使用 下面的形式:if (condition) statements;

7.3 在 if/else/for/while/do 语句中必须使用大括号,即使只有一行代码,避免使用 下面的形式:if (condition) statements;

7.4 推荐尽量少用 else, if-else 的方式可以改写成:

if(condition){              
	...             
	return obj;    
}   
// 接着写 else 的业务逻辑代码; 

说明:如果非得使用 if()…else if()…else…方式表达逻辑,【强制】请勿超过 3 层, 超过请使用状态设计模式。 正例:逻辑上超过 3 层的 if-else 代码可以使用卫语句,或者状态模式来实现。
卫语句

7.5 方法中需要进行参数校验的场景:

  1. 调用频次低的方法。
  2. 执行时间开销很大的方法,参数校验时间几乎可以忽略不计,但如果因为参数错误导致 中间执行回退,或者错误,那得不偿失。
  3. 需要极高稳定性和可用性的方法。
  4. 对外提供的开放接口,不管是 RPC/API/HTTP 接口。
  5. 敏感权限入口。

7.6 方法中不需要参数校验的场景:

  1. 极有可能被循环调用的方法,不建议对参数进行校验。但在方法说明里必须注明外部参 数检查。
  2. 底层的方法调用频度都比较高,一般不校验。毕竟是像纯净水过滤的最后一道,参数错 误不太可能到底层才会暴露问题。一般 DAO 层与 Service 层都在同一个应用中,部署在同一 台服务器中,所以 DAO 的参数校验,可以省略。
  3. 被声明成 private 只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参 数已经做过检查或者肯定不会有问题,此时可以不校验参数。

8、注释规约

8.1 类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用 //xxx 方式。

说明:在 IDE 编辑窗口中,Javadoc 方式会提示相关注释,生成 Javadoc 可以正确输出相应注 释;在 IDE 中,工程调用方法时,不进入方法即可悬浮提示方法、参数、返回值的意义,提高 阅读效率

8.2 所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能。

说明:对子类的实现要求,或者调用注意事项,请一并说明。

8.3 所有的类都必须添加创建者信息。

8.4 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释 使用/* */注释,注意与代码对齐。

8.5 所有的枚举类型字段必须要有注释,说明每个数据项的用途。

8.6 注释掉的代码尽量要配合说明,而不是简单的注释掉。

说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没 有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。

8.7 特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描, 经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。

  1. 待办事宜(TODO):( 标记人,标记时间,[预计处理时间]) 表示需要实现,但目前还未实现的功能。这实际上是一个 Javadoc 的标签,目前的 Javadoc 还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 Javadoc 标签)。
  2. 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间]) 在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。

9、其他

9.1 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。

说明:不要在方法体内定义:Pattern pattern = Pattern.compile(规则);

9.2 velocity 调用 POJO 类的属性时,建议直接使用属性名取值即可,模板引擎会自动按 规范调用 POJO 的 getXxx(),如果是 boolean 基本数据类型变量(boolean 命名不需要加 is 前缀),会自动调用 isXxx()方法。

Velocity教程
说明:注意如果是 Boolean 包装类对象,优先调用 getXxx()的方法。

9.3 后台输送给页面的变量必须加$!{var}——中间的感叹号。

说明:如果 var=null 或者不存在,那么${var}会直接显示在页面上。

9.4 注意 Math.random() 这个方法返回是 double 类型,注意取值的范围 0≤x<1(能够 取到零值,注意除零异常),如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后 取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。

9.5 获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime();

说明:如果想获取更加精确的纳秒级时间值,用 System.nanoTime()。在 JDK8 中,针对统计 时间等场景,推荐使用 Instant 类。

9.6 尽量不要在 vm (velocity )中加入变量声明、逻辑运算符,更不要在 vm 模板中加入任何复杂的逻 辑。

二、异常日志

1、异常处理

1.1 有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回 滚事务。

1.2 不能在 finally 块中使用 return,finally 块中的 return 返回后方法结束执行,不 会再执行 try 块中的 return 语句。

1.3 方法的返回值可以为 null,不强制返回空集合,或者空对象等,必须添加注释充分 说明什么情况下会返回 null 值。调用方需要进行 null 判断防止 NPE 问题。

说明:本规约明确防止 NPE 是调用者的责任。即使被调用方法返回空集合或者空对象,对调用 者来说,也并非高枕无忧,必须考虑到远程调用失败,运行时异常等场景返回 null 的情况。

2、日志规约

2.1 应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
private static final Logger logger = LoggerFactory.getLogger(Abc.class);  

2.2 日志文件推荐至少保存 15 天,因为有些异常具备以“周”为频次发生的特点。

2.3 应用中的扩展日志(如打点、临时监控、访问日志等)命名方式:

appName_logType_logName.log。
logType:日志类型,推荐分类有 stats/desc/monitor/visit 等;
logName:日志描述。这种命名的好处:通过文件名就可知 道日志文件属于什么应用,什么类型,什么目的,也有利于归类查找。
正例:mppserver 应用中单独监控时区转换异常,如: mppserver_monitor_timeZoneConvert.log
说明:推荐对日志进行分类,错误日志和业务日志尽量分开存放,便于开发人员查看,也便于 通过日志对系统进行及时监控。

2.4 对 trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式。

说明:logger.debug("Processing trade with id: " + id + " symbol: " + symbol); 如果日志级别是 warn,上述日志不会打印,但是会执行字符串拼接操作,如果 symbol 是对象, 会执行 toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。
正例:(条件)
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
}
正例:(占位符)
logger.debug("Processing trade with id: {} symbol : {} ", id, symbol);

2.5 避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false。

正例:

2.6 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么往上 抛。

正例:logger.error(各类参数或者对象 toString + “_” + e.getMessage(), e);

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值