【程序健壮性】听说,你也一直钟爱着equals。。。
脑补一下final
final 用于声明变量/参数/属性、方法和类。
- 修饰变量:如果变量是基本类型,其值不变;如果是对象,则引用不可再变(内容可变)。
- 修饰方法:方法不可重写(是否可继承取决于方法的访问修饰符)
- 修饰类:类不可被继承。
==与equals的区别
我想,大家常规的解释是:==是操作符,比较基本类型,则为其值;比较引用类型,则用以比较两个对象在内存中的哈希地址。 equals是方法,比较的是变量的内容。
equals是超类Object的方法。参数是一个object对象。本文我将重点借助JAVADOC和源码来阐释。
java.lang.Object里对equals方法的说明:
/**
Indicates whether some other object is "equal to" this one.
The equals method implements an equivalence relation on non-null object references:
- It is reflexive: for any non-null reference value x, x.equals(x) should return true.
- It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
- It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
- It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
- For any non-null reference value x, x.equals(null) should return false.
The equals method for class Object implements the most discriminating possible equivalence relation on objects; that is, for any non-null reference values x and y, this method returns true if and only if x and y refer to the same object (x == y has the value true).
Note that it is generally necessary to override the hashCode method whenever this method is overridden, so as to maintain the general contract for the hashCode method, which states that equal objects must have equal hash codes.
Parameters:
obj - the reference object with which to compare.
Returns:
true if this object is the same as the obj argument; false otherwise.
See Also:
hashCode(), java.util.HashMap
*/
public boolean equals(Object obj) {
return (this == obj);
}
可见equals默认是比较对象的哈希值。
String、Integer这些包装类重写了equals方法。重写的逻辑都是先判断类型是否匹配,然后String是看字符串里每个char字符是否都相同,Integer是两个Integer实例的intValue。
//java.lang.String
/**
* Compares this string to the specified object. The result is {@code
* true} if and only if the argument is not {@code null} and is a {@code
* String} object that represents the same sequence of characters as this
* object.
*/
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
//java.lang.Integer
/**
* Compares this object to the specified object. The result is
* {@code true} if and only if the argument is not
* {@code null} and is an {@code Integer} object that
* contains the same {@code int} value as this object.
*/
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
BTW,心细的同学可以注意一下java.lang.String中另一个方法equalsIgnoreCase(String anotherString),参数类型不是Object而是String。这一点足见java之美。
对于String来说,String s1="abc";String s2="abc"; System.out.println(s1==s2); 输出的结果是true。我们都知道String类是不可变的字符序列,存储在常量池中,所以当你声明了一个s1=“abc"时,就会在常量池中开辟一个内存空间来存放"abc”,下次再声明时,就会在常量池中去找,有,就直接把当前地址赋给变量,没有,就再创建。而当new String("abc")时,会开辟新的内存空间,用==比较自然会返回false。
在做诸如String、Integer包装类型比较时,有的人一律用equals,这样会规避用==带来的比较错误的问题。这没错!不过使用equals也会带来一些隐性的bug,罪魁祸首就是它的参数类型是Object,当equals左右两个对象的类型不匹配时,编译器是识别不出来的。尤其当我们重构代码时,举个栗子,看下面的语句,要求在bean.platformCode是String时逻辑才合理。如果我们在重构或升级时将bean.platformCode属性的类型由String改为int,从String.equals方法来看,因为类型不匹配,会始终返回false,那就出bug了。所以,在做比较时,用equals取代==,会规避掉某些坑,同时呢,也会带来隐性的雷,大家在开发过程中需权衡利弊。
if("6".equals(bean.getPlatformCode()) { ... }
再举个栗子,比较两个BigDecimal,如果使用BigDecimal#equals,你可要慎重了,因为 new BigDecimal("0.60").equals(new BigDecimal("0.6")) 的结果是false。BigDecimal#equals是只有当两个BigDecimal的值和精度都相同时,才返回true。比较两个BigDecimal,应该使用BigDecimal#compareTo方法。
因此,个人认为。。。(此处见解见文末)
比较枚举类型能用==吗?
今天在codereview时,有同学发现如下用==来比较枚举,马上反问这么写没有bug么?是否测试过了?
if (ThirdPayPlatformEnums.TFB == _requestDTO.getThird_pay_platform()) {
_requestDTO.setOrder_no(_requestDTO.getOrder_no().replaceAll("[a-zA-Z]", ""));
}
他建议改用equals。
于是,大家都开始思考枚举是不是基本类型,并了解枚举是否可以用==来比较。
经查证,答案是肯定的。
java.lang.Enum类对equals的定义是这样的:
public final boolean equals(Object other) { return this==other; }
单看这个方法实现,可能认为比较的是哈希地址。
让我们来看看这个equals方法的注解,关注一些关键词:
/** * Returns true if the specified object is equal to this enum constant. * * @param other the object to be compared for equality with this object. * @return true if the specified object is equal to this enum constant. */
注意到了吧,它将枚举项称为枚举常量。
进一步了解到:在官方文档中也有明确的说明
JLS 8.9 Enums 一个枚举类型除了定义的那些枚举常量外没有其他实例了。 试图明确地说明一种枚举类型是会导致编译期异常。在枚举中final clone方法确保枚举常量从不会被克隆,而且序列化机制会确保从不会因为反序列化而创造复制的实例。枚举类型的反射实例化也是被禁止的。总之,以上内容确保了除了定义的枚举常量之外,没有枚举类型实例。
比较枚举类型能用==吗?
因为每个枚举常量只有一个实例,所以如果在比较两个参考值,至少有一个涉及到枚举常量时,允许使用“==”代替equals()。
顺便贴出来java.lang.Enum里的clone方法,它保证了枚举的“单例”状态:
/** * Throws CloneNotSupportedException. This guarantees that enums are never cloned, which is necessary to preserve their "singleton" status. * * @return (never returns) */ protected final Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); }
csdn《 比较java枚举成员使用equal还是==》文中分析了分别用==和equals来比较枚举的场景。最终建议是最好用==,也是考虑到了上文中提到的equals的弊端,有效保证了程序的健壮性。 同样,如果是数值比较,也最好用基本类型,因为用==来比较基本类型的数字不存在疑义。
String#contentEquals,了解一下
与equals()方法类似,contentEquals()方法也用于比较String的内容。但是,equals ()方法只比较String实例。与equals()方法不同,contentEquals()将CharSequence接口的任何实现作为参数,这意味着可以比较String、StringBuffer、StringBuilder、CharBuffer或Segment。
这个方法的签名是:
public boolean contentEquals(StringBuffer sb){ return contentEquals((CharSequence)sb); } public boolean contentEquals(CharSequence cs){ 如果参数是String对象,则调用equals()方法进行比较。 如果提供了通用字符序列,则该方法比较相似位置的各个字符。如果给定参数中的字符序列与原始String匹配,则该方法返回true。 }
因此,contentEquals()方法只关心字符串的内容。与equals()方法不同,如果将null参数传递给contentEquals()方法,它会抛出NullPointerException。
总而言之,当我们只关心对象的内容时,我们应该使用contentEquals(),这样,当上面重构参数值的数据类型时,IDE就会亮出红牌,contentEquals的这个优势可以让我们完美避开equals的坑。另一方面,有时检查对象的类型可能很重要。在这种情况下,我们应该使用equals()方法,它为我们提供了更严格的检查条件。
▄︻┻┳═一Agenda:
▄︻┻┳═一告别equals,这些姿势助你比较两个对象
▄︻┻┳═一听说,你也一直钟爱着equals。。。
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/8361329.html