java源码阅读之java.util.Objects

之所以写这篇文章,是因为工作中接触到一个开源项目代码,而这个开源代码使用到了这个类。同时如果不是前面的包名java.util,都很容易看错成java超类java.lang.Object。

java.util.Objects是java1.7新增的一个类。下面这篇文章将基于1.7.0_80版本的类库源码展开。

一、类定义:

package java.util;

/**
 * @since 1.7
 */
public final class Objects {

我去掉了类上面的注释,只保留了一个注解标签,用来记住这个类是自1.7开始出现的。另外需要注意的是这个类用关键字final修饰了,意味着它不能被继承

二、构造函数:

    private Objects() {
        throw new AssertionError("No java.util.Objects instances for you!");
    }

只有这么一个构造函数,并且声明为private,构造函数中抛出一个AssertionError。这两点结合起来看,意味着:这个类不可以实例化。(这里我们不考虑反射打破这一规则的问题)

综合三点,(1)java.util包下面的类;(2)不可以被继承;(3)不能实例化。

可以推测,这个类八九不离十,是个工具类

三、公开的方法:

(1)判断两个对象是否相等

public static boolean equals(Object a, Object b) {
        return (a == b) || (a != null && a.equals(b));
    }

参数a和b都是Object类型,意味着这个方法很灵活,能够接收所有的对象引用。

方法内部也很有意思,用到了短路或,先判断链两个引用是否指向同一对象。如果不是,再调用a参数的equals方法,等于将

是否相等的任务委托给引用a所指向的对象去执行了。同时很细心地,先判断a是不是null。否则很容易引起NPE问题。

(2)判断两个对象是否相等。

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

不同于(1)中的函数,这个函数,直接指明了,如果参数a或者b其中有1个为null,而另一个不为null,那么就返回false。

等到else判断的时候,已经可以确定的是a和b都不是null了。同时也将判断委托给Arrays类的deepEquals0方法了。有兴趣的读者可以去看一下这个方法的具体实现。

(3)获取对象hashCode值方法:

    public static int hashCode(Object o) {
        return o != null ? o.hashCode() : 0;
    }

方法很简单,判断引用o所指对象是否为null,不为null,则返回o所指对象的hasCode方法执行结果,为null,返回0。

(4)还是获取hash值的方法,并且还是一个变长参数的方法。

    public static int hash(Object... values) {
        return Arrays.hashCode(values);
    }

真正执行方法的是Arrays类的方法:【public static int hashCode(Object a[])】

在这里也这个方法的具体实现:

    public static int hashCode(Object a[]) {
        if (a == null)
            return 0;

        int result = 1;

        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());

        return result;
    }

细心地读者可能会发现,变长参数values怎么可以传给一个数组类型的方法参数呢?这里,读者可以参考我的另一篇文章,我专门讲解了变长参数这个语法糖的背后真实实现。

(5)将一个Object引用所指的对象,转换成一个字符串。

 public static String toString(Object o) {
        return String.valueOf(o);
    }

这个方法,仍然是委托给String类去执行,调用String的valueOf方法。

其实String的valueOf这个方法也很有意思,有兴趣的读者也可以自行去了解。

(6)将一个Object引用所指的对象,转换成一个字符串。

    public static String toString(Object o, String nullDefault) {
        return (o != null) ? o.toString() : nullDefault;
    }

不同与(5)中方法的是,提供了一个默认值,而且这个默认值是由调用者所指定的。

(7)对两个引用所指对象的比较,使用到了泛型

    public static <T> int compare(T a, T b, Comparator<? super T> c) {
        return (a == b) ? 0 :  c.compare(a, b);
    }

其实这也是个委托实现的方法,重要的第三个参数,是如何实现的compare方法。

(8)  null检查

  public static <T> T requireNonNull(T obj) {
        if (obj == null)
            throw new NullPointerException();
        return obj;
    }

这个是最受我追捧,也是我使用最多的一个方法,它可以提前的探知你的参数是否为null,从而早暴露出NPE问题,并能够帮助你精确定位。有点类似与guava的Optional类检查null一样。

(9)null检查,但是提供用户指定的异常信息。

    public static <T> T requireNonNull(T obj, String message) {
        if (obj == null)
            throw new NullPointerException(message);
        return obj;
    }

三、JDK1.8新增的方法:

新增了三个方法,都是小方法,但也很实用。下面三个方法都是取自JDK1.8.0_151中源码。

(1)null判断,判断一个Object引用是不是指向null。

     /**
     * @see java.util.function.Predicate
     * @since 1.8
     */
    public static boolean isNull(Object obj) {
        return obj == null;
    }

(2)非null判断,判断一个Object引用是否不是指向null。

     /**
     * @see java.util.function.Predicate
     * @since 1.8
     */
    public static boolean nonNull(Object obj) {
        return obj != null;
    }

(3)null检查

     /**
     * @since 1.8
     */
    public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
        if (obj == null)
            throw new NullPointerException(messageSupplier.get());
        return obj;
    }

可以看出,这三个方法,都是为了适应java1.8引入的lambda表达式和函数式编程服务的。

【总结】:这个是一个轻量级的工具类,拥有很多很实用的小方法。值得推荐。

猜你喜欢

转载自blog.csdn.net/guohengcook/article/details/81272755