一、概述
Objects类是一Object类的个工具类,类似Collections类,提供一些静态的工具方法,着重于校验空指针以及获取hash值等,弥补我们在写代码时不小心忽略空指针等异常情况,从jdk1.7被加入进来, Objects类被final修饰不能被继承,拥有私有的构造函数。
二、源码解析
1. 类的声明
public final class Objects
没什么好说的,就是被final修饰, 表示Objects类为最终类,不能被继承 。
2. 构造方法
private Objects() {
throw new AssertionError("No java.util.Objects instances for you!");
}
不能被实例化,直接抛出异常。
3. equals()方法
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
源码中针对对象a进行了非空判断,不为空才会使用a.equals(b),从而保证不会抛出空指针异常。此处说一下==和equals方法的特点与区别。
a. ==是对象引用比较
* 如果比较的对象都是基本数据类型,则比较数据的大小
* 如果有引用类型,则比较两个对象的内存地址
注意:自动拆装箱有个坑,如下:
Integer n1=128;
Integer n2=128;
n1==n2;
Integer n3=127;
Integer n4=127;
n3==n4;
Integer包装类型中存在自动装箱的情况,当数据范围在-128~127之间时,创建的对象会在方法区的常量池中开辟空间(可复用),数据超出范围就会在堆区中开辟空间,由于指向不同对象所以n1==n2判等的结果为false,n3与n4指向常量池同一地址所以判等结果为true。
b. equals()方法是逻辑比较
* 一般情况下是调用Object类的equals()方法,比较两个对象是否是同一个对象
* 特殊类比如String等重写equals()方法后,要根据自己定义的规则进行比较判断。
4. deepEquals()方法
public static boolean deepEquals(Object a, Object b) {
//如果是同一个对象,直接返回true
if (a == b)
return true;
//只要有一个对象为null,直接返回false,也就是说,即使两个对象都为null,也返回false
else if (a == null || b == null)
return false;
else
//调用Arrays类的deepEquals0()方法进行深度判断
return Arrays.deepEquals0(a, b);
}
//Arrays类的deepEquals0()方法,可以看出源码根据不同的类型进行了单独的逻辑处理
static boolean deepEquals0(Object e1, Object e2) {
assert e1 != null;
boolean eq;
//如果两个对象都是数组类型,则调用deepEquals()方法
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;
}
//用于数组的嵌套调用比较两个数组元素是否相同
public static boolean deepEquals(Object[] a1, Object[] a2) {
if (a1 == a2)
return true;
if (a1 == null || a2==null)
return false;
int length = a1.length;
if (a2.length != length)
return false;
for (int i = 0; i < length; i++) {
Object e1 = a1[i];
Object e2 = a2[i];
if (e1 == e2)
continue;
if (e1 == null)
return false;
// Figure out whether the two elements are equal
boolean eq = deepEquals0(e1, e2);
if (!eq)
return false;
}
return true;
}
由源码我们可以看出,如果参数是Object类型的数组,则调用Arrays.deepEquals方法,在参数数组的循环中,递归调用deepEquals0,直到出现不相同的元素,或者循环结束;如果参数是基本类型的数组,则根据该类型调用Arrays.equals方法。Arrays工具类依照八种基本类型对equals方法做了重载。
5. hashCode()方法
public static int hashCode(Object o) {
return o != null ? o.hashCode() : 0;
}
如果对象o不为null,则调用Object类的hashCode()犯法返回其hash值,否则返回0。这里也是做了空指针安全的处理,因为如果直接调用null的hashCode()则会抛出异常,而不是返回0。
6. hash()方法
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
//Arrays类的hashCode的方法
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;
}
Objects类hash()方法是调用了Arrays类的hashCode()方法,传入的所有参数对象都放进一个values数组中。Arrays类的hashCode()方法是循环遍历所有的元素进行了计算,可以看出,返回的hash值并不是所有元素的hashcode值的总和,并且即使数组中仅有一个元素,返回的hash值也不是这个元素本身的hash值。
7. toString系列方法
//调用String类的valueOf()方法,即使对象为null,也能返回字符串"null"
public static String toString(Object o) {
return String.valueOf(o);
}
// 可以指定对象为null时,返回默认的字符串。如果不为null,则调用Object类的toString()方法
public static String toString(Object o, String nullDefault) {
return (o != null) ? o.toString() : nullDefault;
}
//String类中的valueOf()方法
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
//Object类的toString()方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
8. compare()方法
public static <T> int compare(T a, T b, Comparator<? super T> c) {
return (a == b) ? 0 : c.compare(a, b);
}
先判断ab对象是否相同,就算都为null,也能返回整数0,不相同再调用比较器实例c的内部比较规则。
9. requireNonNull系列方法
//检查指定类型的对象引用不为空null。当参数为null时,抛出空指针异常。设计这个方法主要是为了在方法、构造函数中做参数校验。
public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}
//此方法是requireNonNull的重载方法,当被校验的参数为null时,根据第二个参数message抛出自定义的异常消息。
public static <T> T requireNonNull(T obj, String message) {
if (obj == null)
throw new NullPointerException(message);
return obj;
}
//检查指定的对象引用不为空null,如果是空,抛出自定义的空指针异常。
//与requireNonNull(Object, String)方法不同,本方法允许将消息的创建延迟,直到空检查结束之后。 虽然在非空例子中这可能会带来性能优势, 但是决定调用本方法时应该小心,创建message supplier的开销低于直接创建字符串消息。
public static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) {
if (obj == null)
throw new NullPointerException(messageSupplier.get());
return obj;
}
requireNonNull()方法的设计是为了在方法、构造函数中做参数校验,当我们通过带参的构造函数创建对象时,创建对象的同时就可以进行参数校验。同时也简化了很多代码。
10. isNull()方法
public static boolean isNull(Object obj) {
return obj == null;
}
判断参数是否为null,如果是,则返回true。
11. nonNull()方法
public static boolean nonNull(Object obj) {
return obj != null;
}
判断参数是否不为null,如果是,则返回true。
三、总结
总的来说Objects类提供了不少便利的方法,可以让我们少写一些非空校验,大家可以灵活的运用到我们的日常代码中。 敬请期待《 我的jdk源码(十八): LinkedHashSet 》。
更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!