Integer orderStatus1 = new Integer(1);
Integer orderStatus2 = new Integer(1);
System.out.println(orderStatus1 == orderStatus2);
此时结果是false,因为其new Integer()构造函数并未用到-128~127的缓存:
public Integer(int value) {
this.value = value;
}
只有valueOf才用到了缓存:
String status1 = new String("1");
String status2 = new String("1");
System.out.println(Integer.valueOf(status1) == Integer.valueOf(status2));
此时结果是true,因为valueOf用到了缓存,会直接将缓存中的Integer对象直接返回:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
总结:尽量不用==判断两个Integer是否相等,而是应该使用equals
Integer orderStatus1 = new Integer(1);
Integer orderStatus2 = new Integer(1);
System.out.println(orderStatus1.equals(orderStatus2));
Objects.equals
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
equals方法的判断逻辑如下:
- 该方法先判断对象a和b的引用是否相等,如果相等则直接返回true。
- 如果引用不相等,则判断a是否为空,如果a为空则返回false。
- 如果a不为空,调用对象的equals方法进一步判断值是否相等。
这就要从Integer的equals方法说起来了。
它的equals方法具体代码如下:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
先判断参数obj是否是Integer类型,如果不是,则直接返回false。如果是Integer类型,再进一步判断int值是否相等。
而上面这个例子中b是long类型,所以Integer的equals方法直接返回了false。
也就是说,如果调用了Integer的equals方法,必须要求入参也是Integer类型,否则该方法会直接返回false。
除此之外,还有Byte、Short、Double、Float、Boolean和Character也有类似的equals方法判断逻辑。
BigDecimaldouble amount1 = 0.02; double amount2 = 0.03; System.out.println(amount2 - amount1);
正常情况下预计amount2 - amount1应该等于0.01
但是执行结果,却为:
0.009999999999999998
实际结果小于预计结果。
Double类型的两个参数相减会转换成二进制,因为Double有效位数为16位这就会出现存储小数位数不够的情况,这种情况下就会出现误差。
常识告诉我们使用BigDecimal能避免丢失精度。但是BigDecimal不能避免丢失精度。
BigDecimal amount1 = new BigDecimal(0.02); BigDecimal amount2 = new BigDecimal(0.03); System.out.println(amount2.subtract(amount1));
大致的意思是此构造函数的结果可能不可预测,可能会出现创建时为0.1,但实际是0.1000000000000000055511151231257827021181583404541015625的情况。
我们可以使用Double.toString方法,对double类型的小数进行转换,这样能保证精度不丢失。
BigDecimal bd1 = new BigDecimal(Double.toString(0.02)); BigDecimal bd2 = new BigDecimal(Double.toString(0.03)); System.out.println(bd2.subtract(bd1));
BigDecimal bd1 = BigDecimal.valueOf(0.02); BigDecimal bd2 = BigDecimal.valueOf(0.03); System.out.println(bd2.subtract(bd1));
使用BigDecimal.valueOf方法初始化BigDecimal类型参数,也能保证精度不丢失。在新版的阿里巴巴开发手册中,也推荐使用这种方式创建BigDecimal参数。
filter使用Java8的filter功能,代码会变得简洁很多,例如:
public ListfilterUser(List userList) { if(CollectionUtils.isEmpty(userList)) { return Collections.emptyList(); } return userList.stream() .filter(user -> user.getId() > 1000 && user.getAge() > 18) .collect(Collectors.toList()); }
但如果你对过滤后的数据,做修改了:
ListuserList = queryUser(); List filterList = filterUser(userList); for(User user: filterList) { user.setName(user.getName() + "测试"); } for(User user: userList) { System.out.println(user.getName()); }
你当时可能只是想修改过滤后的数据,但实际上,你会把元素数据一同修改了。
其根本原因是:过滤后的集合中,保存的是对象的引用,该引用只有一份数据。
也就是说,只要有一个地方,把该引用对象的成员变量的值,做修改了,其他地方也会同步修改。
自动装箱拆箱自动装箱是指:JDK会把基本类型,自动变成包装类型。
比如:
Integer integer = 1;
等价于:
Integer integer = new Integer(1);
而自动拆箱是指:JDK会把包装类型,自动转换成基本类型。
例如:
Integer integer = new Integer(2); int sum = integer + 5;
等价于:
Integer integer = new Integer(2); int sum = integer.intValue() + 5;
但实际工作中,我们在使用自动拆箱时,往往忘记了判空,导致出现NullPointerException异常。
例子1很多时候,我们需要对传入的数据进行计算,例如:
public class Test2 {
public static void main(String[] args) {
System.out.println(add(new Integer(1), new Integer(2)));
}
private static Integer add(Integer a, Integer b) {
return a + b;
}
}
如果传入了null值:
System.out.println(add(null, new Integer(2)));
则会直接报错。
例子2有时候,我们定义的某个方法是基本类型,但实际上传入了包装类,比如:
public static void main(String[] args) {
Integer a = new Integer(1);
Integer b = null;
System.out.println(add(a, b));
}
private static Integer add(int a, int b) {
return a + b;
}
如果出现add方法报NullPointerException异常,你可能会懵逼,int类型怎么会出现空指针异常呢?
其实,这个问题出在:Integer类型的参数,其实际传入值为null,JDK字段拆箱,调用了它的intValue方法导致的问题。
replace既然replace和replaceAll都能替换所有匹配字符,那么他们有啥区别呢?
replace有两个重载的方法。
- 其中一个方法的参数:char oldChar 和 char newChar,支持字符的替换。
source.replace('A', 'B')
- 另一个方法的参数是:CharSequence target 和 CharSequence replacement,支持字符串的替换。
source.replace("A", "B")
而replaceAll方法的参数是:String regex 和 String replacement,即基于正则表达式的替换。
例如对普通字符串进行替换:
source.replaceAll("A", "B")
使用正则表达替换(将*替换成C):
source.replaceAll("\*", "C")
顺便说一下,将*替换成C使用replace方法也可以实现:
source.replace("*", "C")
区别:使用replace方法无需对特殊字符进行转义
不过,千万注意,切勿使用如下写法:
source.replace("\*", "C")
这种写法会导致字符串无法替换。
还有个小问题,如果我只想替换第一个匹配的字符串该怎么办?
这时可以使用replaceFirst方法:
source.replaceFirst("A", "B")
参考:https://mp.weixin.qq.com/s/a3cFjIvkAo14DHcTB6IkYg



