1.不应该只是为了测试而新增setter方法,实在无法依赖注入可以在测试中使用如下工具类:
import org.springframework.test.util.ReflectionTestUtils;
ReflectionTestUtils.setField(targetObject, "beanName", mockBean);
2.Stream使用
推荐优先使用filter,map等操作替代for循环,lamba请注意换行缩进,建议流式处理时每个方法另起一行,代码避免存在IDEA提醒的可简化提示。
不使用Stream的parallel提供的并行处理能力,不可控,如果需要请自行实现多线程处理。
(paralleStream里直接去修改变量是非线程安全的,但是采用collect和reduce操作就是满足线程安全的了。)
3.Optional的使用
推荐使用Optional避免JAVA中的null,使用user.orElse(null)替代 user.isPresent() ? user.get() : null;
注意所有使用到Optional的场景,尤其是stream中使用到findFirst,FindAny等操作的时候,确保流中的元素不能为null,否则会NPE,建议在find方法前增加不为null的filter。
4.Joiner & Splitter
推荐使用这两个工具类处理字符串拼接等操作
Joiner.on("|").useForNull("").join(args);
Splitter.on(",").splitToList("1,2,3");
5.前端传递List集合数据给后台时,如果需要判断非空的话,除了判断!=null,还得要求list.size()>0以防前台传个空集合造成不必要的错误。
6.方法重载的时候如果多个方法都有可变参数最好把方法名改成不同名的。
如下面这样调用会有问题:
public void test(String a,String b,String...args)
public void test(String a,String b,String c,String...args)
7.log打日志的时候不要用+号去链接内容,可以用{ }然后参数填充。
log.warn("request, queryJobList:{}", query);
8.日期解析SimpleDateFormat
private static DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
这样时间解析出来是24小时制度
private static DateFormat dfm = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
这样时间解析出来是12小时制度
9.比较值相等最好用equal别用==
public class equalTest {
public static void main(String[] args) {
Integer a = 12;
Integer b = 12;
Integer c = 133;
Integer d = 133;
Integer e = 24;
int f = 133;
System.out.println(a == b); //true
System.out.println(c == d); //false
System.out.println(f == c); //true
System.out.println(a + b == e); //true
}
}
这里Integer会有个-128~127的缓存,所以比较1是true比较2为false,包装类型和基本类型比较会自动拆箱,所以3为true,包装类型计算也会自动拆箱,所以4为true。
10.map遍历如果用到key,value建议直接使用entry,不要用keySet再通过map获取value,因为keySet中实际上key,value都一起遍历了。
11.只要重写equals,就必须重写hashCode。
当Object类int java.lang.Object.equals()方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码object对象中的 public boolean equals(Object obj),对于任何非空引用值 x 和 y,当且仅当 x 和 y 引用同一个对象时,此方法才返回 true;
当obj1.equals(obj2)为true时,obj1.hashCode() == obj2.hashCode()必须为true
当obj1.hashCode() == obj2.hashCode()为false时,obj1.equals(obj2)必须为false
hashcode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashcode值来进行判断是否相同的。这样如果我们对一个对象重写了euqals,意思是只要对象的成员变量值都相等那么euqals就等于true,但不重写hashcode,那么我们再new一个新的对象,当原对象.equals(新对象)等于true时,两者的hashcode却是不一样的,由此将产生了理解的不一致,如在存储散列集合时(如Set类),将会存储了两个值一样的对象,导致混淆,因此,就也需要重写hashcode()。
按照名字相同去重:
package com.da.javatest.demo;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
public class HashTest {
public static void main(String[] args) {
List students1 = Arrays.asList(new Student(1, "aa", 18), new Student(2, "bb", 19),
new Student(3, "cc", 18), new Student(4, "aa", 19));
System.out.println(students1);
HashSet students2 = new HashSet<>(students1);
System.out.println(students2);
}
}
class Student {
private Integer id;
private String name;
private Integer age;
public Student(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) { return true; }
if (o == null || getClass() != o.getClass()) { return false; }
Student student = (Student)o;
return Objects.equals(name, student.name);
}
//@Override
//public int hashCode() {
// return Objects.hash(name);
//}
}
重写后的结果:
[Student{id=1, name=‘aa’, age=18}, Student{id=2, name=‘bb’, age=19}, Student{id=3, name=‘cc’, age=18}, Student{id=4, name=‘aa’, age=19}]
[Student{id=1, name=‘aa’, age=18}, Student{id=3, name=‘cc’, age=18}, Student{id=2, name=‘bb’, age=19}, Student{id=4, name=‘aa’, age=19}]
不重写的结果:
[Student{id=1, name=‘aa’, age=18}, Student{id=2, name=‘bb’, age=19}, Student{id=3, name=‘cc’, age=18}, Student{id=4, name=‘aa’, age=19}]
[Student{id=1, name=‘aa’, age=18}, Student{id=2, name=‘bb’, age=19}, Student{id=3, name=‘cc’, age=18}]
12.线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool:
允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool和ScheduledThreadPool:
允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
13.如果用try/carch/finally语句,finally需要释放锁的时候,在分布式情况下如果在try块中还没拿到锁出现异常,finally释放锁有可能把别人的锁给释放了,所以可以加个flag标记,判断是否拿到锁finally再进行释放。
14.CopyOnWriteArrayList 参考:https://www..com/dolphin0520/p/3938914.html
15.Intellij IDEA 无法找到引入的包和类:
git切换分支回滚后出现该问题,清除缓存即可
16.java.lang.IllegalStateException: Failed to load ApplicationContext 问题
找到最后面那个Caused by是不是找不到class: com.xxx.xxx.xxx,然后搜索ctrl+shift+f排查
17.用mybatis逆向工程生成文件的时候,如果用QBC查询大字段需要用selectByParamWithBLOBs方法,不然大字段返回null。
18.不要使用isXXX作为boolean值的命名,不然生成get set的时候会有意想不到的问题。
19.对象转json串如果字段为null不显示则:new Gson().toJson(resultDO);
如果要显示null的话:new GsonBuilder().serializeNulls().create().toJson(resultDO)
用fastjson的话:
null不显示则:JSON.toJSONString(resultDO);
null显示则:JSONObject.toJSONString(Object object, SerializerFeature... features)
比如:JSONObject.toJSONString(resultDO, SerializerFeature.WriteMapNullValue)
说明:
QuoteFieldNames———-输出key时是否使用双引号,默认为true
WriteMapNullValue——–是否输出值为null的字段,默认为false
WriteNullNumberAsZero—-数值字段如果为null,输出为0,而非null
WriteNullListAsEmpty—–List字段如果为null,输出为[],而非null
WriteNullStringAsEmpty—字符类型字段如果为null,输出为”“,而非null
WriteNullBooleanAsFalse–Boolean字段如果为null,输出为false,而非null
20.在高并发场景中,避免使用“等于”判断作为中断或退出的条件
说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件来代替。
例子:某营销活动发奖,判断剩余奖品数量等于0时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成了负数,活动无法终止,产生资损。
21.不要使用count(列名)来替代count(*)
说明:count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行。
22.避免用Apache Beanutils进行属性的copy。
说明:Apache BeanUtils性能较差,可以使用其他方案比如Spring BeanUtils, Cglib BeanCopier,注意均是浅拷贝。
23.如果用了构造器的@Autowired就不要再加其他的@Autowired在属性上面,不然那个属性会装配不上
这里transactionManager会装配不上,使用的时候会造成NPE
24.写测试用例的时候如果@Autowired实现类,然而实现类的构造器和上面一样则启动的时候会报错:
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'jobInstanceWriteWriteServiceImpl' is expected to be of type 'com.xxx.xxx.JobInstanceWriteWriteServiceImpl' but was actually of type 'com.sun.proxy.$Proxy172'
这个时候要测试接口的话需要直接自动装配该实现类的接口即可