Java避坑指南
Lombok
1、使用Lombok时Jackson无法识别单字母+大写首字母单词的字段,类似:iPhone,这种字段要改成全小写才能反序列化。具体的可以看看序列化的结果。
2、当使用继承时,equals不会将父类的字段参与比较。
- 需要加上此注解:
@EqualsAndHashCode(callSuper = true)
SimpleDateFormat
一般我们使用SimpleDateFormat的时候会把它定义为一个静态变量,避免频繁创建它的对象实例。
单线程场景使用时没有问题,多线程场景下使用时会报错:java.lang.NumberFormatException: multiple points
此外,SimpleDateFormat和它继承的DateFormat类也不是线程安全的。
- 可以解析大于/等于他定义的时间精度,但不能解析小于它定义的时间精度。
解决方案
低效方案
1、使用临时变量的形式:即每次都生成一个对象,缺点是==低效,创建大量的临时对象==。
2、使用synchronized关键字,缺点是==降低了并发性,大量并发时进程阻塞,而且使用这个的一般是工具类,这是及其不友好的。
高效方案
1、使用Threadlocal
ThreadLocal可以确保每个线程都可以得到单独的一个SimpleDateFormat的对象。
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}
public static String format(Date date) {
return threadLocal.get().format(date);
}
2、使用 DateTimeFormatter
public class SimpleDateFormatTest {
private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
public static String formatDate2(LocalDateTime date) {
return formatter.format(date);
}
public static LocalDateTime parse2(String dateNow) {
return LocalDateTime.parse(dateNow, formatter);
}
}
hashCode和equals必须同时编写
1、集合中元素的索引与equals方法相关。
- 类实现了
compareTo方法,就需要实现equals方法。 - 两者实现过程需要需要同步,否则会出现不一致的情况。
public class EqualsOrElse {
private static class User implements Comparable<User> {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if (o instanceof User) {
return this.name.equals(((User) o).getName());
}
return false;
// return this.age - ((User) o).age == 0;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public int compareTo(User o) {
return this.age - o.age;
}
}
public static void test02(){
List<User> users = new ArrayList<>();
User us1 = new User("xiaoming", 20);
User us2 = new User("xiaoming", 22);
users.add(us1);
users.add(us2);
User user = new User("xiaoming",22);
System.out.println(users.indexOf(user)); // 基于equals:返回true
System.out.println(Collections.binarySearch(users, user)); // 基于compareTo:返回0
}
public static void main(String[] args) {
test02();
}
}
2、hashCode方法一定要编写,否则集合存储时可能会出现问题
public class EqualsOrElse {
private static class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public boolean equals(Object o) {
if(o instanceof User){
return this.name.equals(((User) o).getName());
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public static void main(String[] args) {
User us1 = new User("xiaoming",22);
User us2 = new User("xiaoming",22);
Set<User> userSet = new HashSet<>();
Map<User,Integer> userMap = new HashMap<>();
userSet.add(us1);
userSet.add(us2);
System.out.println(userSet.size()); // 2
userMap.put(us1,1);
userMap.put(us2,2);
System.out.println(userMap.size()); // 2
// 分析:两个对象参数一致,即认为是相等的,但是上面两个集合军存储了两份,这是不对的
// 原因:没有编写hashCode导致的,而Set和Map在存储时都需要用到hashcode值
// 解决:编写hashCode方法,并且要求所有参数都参与运算
}
}
BigDecimal精度问题
1、设置的精度必须必原值的精度大,如果比原值小的话需要设置舍入规则。
2、除法的结果需要设置精度,不设置精度和舍入规则,遇到无法除尽的问题就会报错。
3、数值比较时需要精度匹配,否则就会出现0.0!=0的情况。
equals:结果错误【精度不相同直接返回false】compareTo:结果正确- 所以尽量不要使用使用
BigDecimal的equals去比较大小。
避免抽象类和接口的选择失误
1、共性,有状态的属性用抽象类去表达
2、特有的独立的属性用独立的接口去实现
Lambda
1、使用Lambda表达式的前提,必须有==函数式接口==,只能有一个抽象方法,但可以有多个非抽象方法的接口
- @FunctionalInterface
@FunctionalInterface
interface IFindWord{
String find(String time);
}
public static void main(String[] args) {
Map<String,String> x = new HashMap<>();
x.put("1","a");
x.put("2","b");
IFindWord iFindWord = x::get; // :: 方法引用 lambda的语法糖
System.out.println(iFindWord.find("1"));
}
2、迭代次数非常多,每一步都会产生中间结果,产生的结果会存在JVM内存中,开销非常大。
public static void main(String[] args) {
List<String> names = Arrays.asList("xiaoming","xixixix","fsdfsdfsd");
int longMaxName = names.stream()
.filter(s->{
System.out.println("filter:"+s);
return s.startsWith("x");
}).mapToInt(n->{
System.out.println("mapToInt:"+n);
return n.length();
}).max().getAsInt();
System.out.println(longMaxName);
}
/*
filter:xiaoming
mapToInt:xiaoming
filter:xixixix
mapToInt:xixixix
filter:fsdfsdfsd
8
*/
FastJSON
当对象中的字段使用同一个别名时,转换会随机将字符串中的这个值转到这两个字段中的一个上。
BeanPostProcessor
特性
- Bean后置处理器
- 两个回调方法
postProcessBeforeInitialization:每一个Bean对象==初始化==之前回调。postProcessAfterInitialization:每一个Bean对象==初始化==之后回调。
- 在Bean==实例化==之后执行