位于java.util包下,最常用的两个场景就是相等判断和非空判断;
包含以下方法:
与 Object 类的区别:
Object 是 Java 中所有类的基类,位于java.lang包;
Objects 是 Object 的工具类,位于java.util包,由一些静态的实用方法组成,这些方法是null-save(空指针安全的)
或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。
1 相等判断
equals主要用来判断基本类型和自定义类型,源代码如下:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
deepEquals主要是用来对数组进行判断,源代码如下:
public static boolean deepEquals(Object a, Object b) {
if (a == b)
return true;
else if (a == null || b == null)
return false;
else
return Arrays.deepEquals0(a, b);
}
这是Arrays.deepEquals0方法的源代码:
static boolean deepEquals0(Object e1, Object e2) {
assert e1 != null;
boolean eq;
if (e1 instanceof Object[] && e2 instanceof Object[])
eq = deepEquals ((Object[]) e1, (Object[]) e2);
else if (e1 instanceof byte[] && e2 instanceof byte[])
eq = equals((byte[]) e1, (byte[]) e2);
else if (e1 instanceof short[] && e2 instanceof short[])
eq = equals((short[]) e1, (short[]) e2);
else if (e1 instanceof int[] && e2 instanceof int[])
eq = equals((int[]) e1, (int[]) e2);
else if (e1 instanceof long[] && e2 instanceof long[])
eq = equals((long[]) e1, (long[]) e2);
else if (e1 instanceof char[] && e2 instanceof char[])
eq = equals((char[]) e1, (char[]) e2);
else if (e1 instanceof float[] && e2 instanceof float[])
eq = equals((float[]) e1, (float[]) e2);
else if (e1 instanceof double[] && e2 instanceof double[])
eq = equals((double[]) e1, (double[]) e2);
else if (e1 instanceof boolean[] && e2 instanceof boolean[])
eq = equals((boolean[]) e1, (boolean[]) e2);
else
eq = e1.equals(e2);
return eq;
}
2 空值判断
具体源代码如下:
其中isNull和nonNull返回值为Boolean
public static boolean isNull(Object obj) {
return obj == null;
}
public static boolean nonNull(Object obj) {
return obj != null;
}
requireNonNull方法更加严格,一旦为null,为抛出对应的空指针异常;
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
public static <T> T requireNonNull(T obj, String message) {
if (obj == null)
throw new NullPointerException(message);
return obj;
}
3 Java中==,equals和Objects.equals(x,y)的使用
首先我们要有==操作符的相关概念:
1.对于对象引用类型比较的是对象的存储地址
2.对于基本数据类型,比较的是它们的值
所有的对象都有一个终极父类--Object类,Object类定义了适合用于任何Java对象的方法,其中就有equals方法,
这个方法的实现很简单:return (this == obj);如果两个对象的引用相同,它们毫无疑问指向同一个对象。
Object类的equals方法源代码如下:
public boolean equals(Object obj) {
return (this == obj);
}
注意:
在比较两个对象的时候,Object.equals方法容易抛出空指针异常,所以字符串常量与变量对象比较的时候,常量要写在equals外边,变量放在equals()括号里边;
对很多类来说,默认的equals方法已经够用,但有时候有必要覆盖equals方法,在这种情况下,当两个对象的内容相等时,认为它们是相等的,String类就覆盖了equals方法,String类的equals方法检查两个字符串是否由相同的字符组成:
public static void main(String[] args) {
String s1=new String("abc");
String s2=new String("abc");
System.out.println(s1==s2); //false
System.out.println(s1.equals(s2)); //true
}
如果我们需要自己定义一个对象相等的比较规则,则需要覆盖equals方法,注意:无论何时覆盖equals方法,均必须同时提供一个兼容的hashCode方法
4 如何重写equals方法
重写equals方法的要求:
1、自反性:对于任何非空引用x,x.equals(x)应该返回true。
2、对称性:对于任何引用x和y,如果x.equals(y)返回true,那么y.equals(x)也应该返回true。
3、传递性:对于任何引用x、y和z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
4、一致性:如果x和y引用的对象没有发生变化,那么反复调用x.equals(y)应该返回同样的结果。
5、非空性:对于任意非空引用x,x.equals(null)应该返回false。
在实际应用中经常会比较两个对象是否相等,比如下面的Address类,它有两个属性:String province 和 String city:
public class Address {
private String province;
private String city;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Address() {
}
public Address(String province, String city) {
this.province = province;
this.city = city;
}
}
在现实生活中我们认为若两个 Address 的 province 和 city 属性相同,则它们应该是同一个地址:但下面的代码却表明:address1 和 address2 是两个“不同的地址”:
public static void main(String[] args) {
Address address1 = new Address("广东", "广州");
Address address2 = new Address("广东", "广州");
System.out.println(address1 == address2);//false
System.out.println(address1.equals(address2));//false
System.out.println(address1.hashCode() == address2.hashCode());//false
}
其原因是在Java(编程语言)中比较对象 和 现实生活中 比较两个对象是两个不同的概念,前者暂且称为“物理相等”,而后者是“逻辑相等”:adress1==adress2 是根据两个对象的内存地址是否相同进行比较的,第4、5行分别 new 了两个对象,这两个对象存储在内存中不同的地址,当然不会相等了;
由于Address类并没有重写equals方法,那么address1.equals(address2) 执行的就是Object类的equals方法,而java.lang.Object类的equlas方法是这样实现的;
public boolean equals(Object obj) {
return (this == obj);
}
即Object类的equals方法 是通过 == 来比较两个对象是否相等的,也即根据 对象x引用 和 对象y 的引用是否指向内存中的同一个地址 来判断 对象x 和 对象y 是否相等;
于是就需要重写 Object 类中的 equals 方法 和 hashCode方法了;那具体如何正确地覆盖equals()呢?《Effective JAVA》里面给出了方法,套路是一样的,其目标是保证:自反性、对称性、一致性。总之,对于上面的Address类而言,可以这样:
public class Address {
private String province;
private String city;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public Address() {}
public Address(String province, String city) {this.province = province; this.city = city;}
@Override
public boolean equals(Object obj) {
if(obj == this)
return true;
if(!(obj instanceof Address))
return false;
Address address = (Address)obj;
return address.getProvince().equals(province) && address.getCity().equals(city);
}
@Override
public int hashCode() {
int result = 17;
result += 31 * province.hashCode();
result += 31 * city.hashCode();
return result;
}
}
测试代码如下:
public class TestAddress {
public static void main(String[] args) {
Address address1 = new Address("广东","广州");
Address address2 = new Address("广东", "广州");
System.out.println(address1 == address2);//false
System.out.println(address1.equals(address2));//true
System.out.println(address1.hashCode() == address2.hashCode());//true
Address diff1 = new Address("四川","成都");
Address diff2 = new Address("四川","绵阳");
System.out.println(diff1 == diff2);//false
System.out.println(diff1.equals(diff2));//false
System.out.println(diff1.hashCode() == diff2.hashCode());//false
Map<Address, Integer> hashMap = new HashMap<Address, Integer>();
hashMap.put(address1, 1);
hashMap.put(address2, 2);//address2的hashCode 和 address1 相同,因此 put 方法会覆盖 address1 对应的 Value值1
System.out.println(hashMap.get(address1));//2
System.out.println(hashMap.get(address2));//2
hashMap.put(diff1, 1);
hashMap.put(diff2, 2);
System.out.println(hashMap.get(diff1));//1
System.out.println(hashMap.get(diff2));//2
}
}
最后,附上开发工具提供的重写equals 方法 和 hashCode方法的代码:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address = (Address) o;
return Objects.equals(province, address.province) &&
Objects.equals(city, address.city);
}
@Override
public int hashCode() {
return Objects.hash(province, city);
}