位于java.util包下,最常用的两个场景就是相等判断和非空判断

包含以下方法:

java validator 工具类 java工具类说明_java

 与 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 空值判断 

java validator 工具类 java工具类说明_java validator 工具类_02

 具体源代码如下:

其中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);
    }