之所以写这篇文章,是因为工作中接触到一个开源项目代码,而这个开源代码使用到了这个类。同时如果不是前面的包名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 int compare(T a, T b, Comparator super T> c) {
return (a == b) ? 0 : c.compare(a, b);
}
其实这也是个委托实现的方法,重要的第三个参数,是如何实现的compare方法。
(8) null检查
public static T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
这个是最受我追捧,也是我使用最多的一个方法,它可以提前的探知你的参数是否为null,从而早暴露出NPE问题,并能够帮助你精确定位。有点类似与guava的Optional类检查null一样。
(9)null检查,但是提供用户指定的异常信息。
public static 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 requireNonNull(T obj, Supplier messageSupplier) {
if (obj == null)
throw new NullPointerException(messageSupplier.get());
return obj;
}
可以看出,这三个方法,都是为了适应java1.8引入的lambda表达式和函数式编程服务的。
【总结】:这个是一个轻量级的工具类,拥有很多很实用的小方法。值得推荐。