java.util.Objects
Objects 与 Object 区别
Object 是 Java 中所有类的基类,位于java.lang包。所有对象(包括数组)都实现此类的方法。
Objects 【public final class Objects extends Object】是 Object 的工具类,位于java.util包。它从jdk1.7开始才出现,被final修饰不能被继承,拥有私有的构造函数。
此类包含static实用程序方法,用于操作对象或在操作前检查某些条件。 这些实用程序包括 null 或 null方法 ,用于计算对象的哈希代码,返回对象的字符串,比较两个对象,以及检查索引或子范围值是否超出范围。
Objects 方法介绍与分析
1、equals
equals方法 指某个其他对象是否“等于”此对象。(判断两个对象是否相等)
equals方法在非null对象引用上实现等价关系:
- 自反性 :对于任何非空的参考值x , x.equals(x)应该返回true 。
- 对称性 :对于任何非空引用值x和y , x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
- 传递性 :对于任何非空引用值x , y和z ,如果x.equals(y)回报true个y.equals(z)回报true,然后x.equals(z)应该返回true 。
- 一致性 :对于任何非空引用值x和y ,多次调用x.equals(y)始终返回true或始终返回false,前提是未修改对象上的equals比较中使用的信息。
- 对于任何非空的参考值x , x.equals(null)应该返回false 。
在比较两个对象的时候,Object.equals方法容易抛出空指针异常。
首先我们来看看在Object类中定义的equals方法 [源码] ↓
可以看到其源码,在Object类型的equals方法是直接通过== 来比较的,和 ==是没有任何区别的。【故我们通常会重写Object类的equals方法】
请注意,Object类 通常需要在重写此方法时覆盖hashCode方法,以便维护hashCode方法的常规协定,该方法声明相等对象必须具有相等的哈希代码。
a、使用对象的equals方法(Object)
运行结果:
所以,对于Object类,则“字符串常量与变量对象比较的时候,常量要写在equals外边,变量放在equals()括号里边。”就是这个原因。如果是两个变量比较的时候,就都需要加非空判断。b、使用Objects中的equals方法
注意理解这两个对象,这两个对象都不指向任何地址,也可以理解为都是一个“空指针”【null是没有内存空间的】。
现在,Objects.equals方法中已经做了非空判断,所以不会抛出空指针异常,它是null-save空指针安全的,而且也可以简化代码。
源码如下:
解释:可以比较两个基本数据类型的数值是否相等,而equals方法依旧调用的是Object对象中的方法,比较两个Object对象的地址值是否引用的同一块地址【因为由Object类的equals方法的源码可以看出实际上就是“==”的比较】,如果不同则为false。
2、deepEquals
顾名思义,深度比较两个对象。
如果两个对象是深层相等,那返回true,否则返回false。
当参数是数组对象,其方法内部采用的是Arrays.deepEquals0方法的算法。
使用Objects.deepEquals方法有个好处,当我们在写业务代码时,可以直接使用此方法来判断两个复杂类型,比如使用了泛型的列表对象List、或者通过反射得到的对象,不清楚对象的具体类型。
源码如下:
解释:Objects中还有一个deepEquals(Object x,Object y)的方法,此方法的功能比较强大,不仅可以比较数值是否相同,而且还可以比较两个对象中的内容是否相同,如果相同即为true。
简短的说明下Arrays.deepEquals0方法:
- 如果参数是Object类型的数组,则调用Arrays.deepEquals0方法,在参数数组的循环中,递归调用deepEquals0,直到出现不相同的元素,或者循环结束;
- 如果参数是基本类型的数组,则根据该类型调用Arrays.equals方法。Arrays工具类依照八种基本类型对equals方法做了重载。
调用了Arrays.java中的deepEquals0方法:
Arrays.java中的deepEquals方法:
Arrays.java中的euqals方法:以boolean[ ]为例
public static boolean equals(byte[] a, byte[] a2) {
if (a==a2)
return true;
if (a==null || a2==null)
return false;
int length = a.length;
if (a2.length != length)
return false;
for (int i=0; i<length; i++)
if (a[i] != a2[i])
return false;
return true;
}
上面涉及的代码比较多,大意就是:
a、如果deepEquals(Object a, Object b)中的a和b是两个数组对象,对每一个索引,用==(如果元素是对象,也会使用equals)作用于两个数组的对应元素,全通过返回true;
b、如果deepEquals(Object a, Object b)中的a和b是两个非数组,==为true或者对象的equals为true则返回true。
Objects中的equals和deepEquals二者方法的区别
链接:二者方法的区别
3、isNull
判空方法,如果参数为空则返回true。从jdk1.8开始。
源码如下:
API Note: 该方法的存在是用于java.util.function.Predicate类;filter(Objects::isNull)。
来看下Predicate类中,使用到本方法的代码:
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull // 双冒号,代表方法引用。
: object -> targetRef.equals(object); // 此处为lambda表达式。接收object对象,返回参数targetRef与该对象的比较结果。
}
4、nonNull
判断非空方法,如果参数不为空则返回true。从jdk1.8开始。
源码如下:
APINote: 该方法的存在是用于java.util.function.Predicate类,filter(Objects::nonNull)。
5、requireNonNull
requireNonNull(T obj)
检查指定类型的对象引用是否为空null。当参数为null时,抛出空指针异常,否则正常输出obj。
设计这个方法主要是为了在方法、构造方法中做参数校验。【常用于要求对象不能为空的场景】
源码如下:
举例1:
举例2:
当我们通过带参的构造方法创建对象时,创建对象的同时就可以进行参数校验。同时也简化了很多代码。
requireNonNull(T obj, String message)
该方法是requireNonNull的重载方法,当被校验的参数为null时,根据第二个参数message抛出自定义的异常消息。
源码如下:
举例:
6、requireNonNull系列
requireNonNull(T obj, Supplier messageSupplier)
检查指定的对象引用是否为空null,如果是空,抛出自定义的空指针异常。从jdk1.8开始。
与方法requireNonNull(Object, String)不同,此方法允许创建要延迟的消息,直到进行空检查。 虽然这可以在非空情况下赋予性能优势,但在决定调用此方法时,应注意创建消息提供者的成本小于仅直接创建字符串消息的成本。
源码如下:
requireNonNullElse
如果第一个参数是非 null ,则返回第一个参数obj,反之调用requireNonNull(T obj, String message)方法来判断【第二个参数非null,则直接返回第二个参数】。
源码如下:
举例
requireNonNullElseGet
如果它是非 null ,则返回第一个参数,否则返回非 null值 supplier.get() 。
源码如下:
小结
requireNonNull就相当于是一个检查是否为null的过滤器,如果为null则抛出异常,反之返回原对象。
requireNonNullElse把上一个方法升级了,如果当前对象是null,则让defaultObj替补上去,看看此对象是否为空。
第三个则是反复嵌套两次。
7、hashCode
返回一个整型数值,表示该对象的哈希码值。若参数对象为空,则返回整数0;若不为空,则直接调用了Object.hashCode方法。
源码如下:
举例:
Object支持hashCode方法是为了提高哈希表(例如java.util.Hashtable 提供的哈希表)的性能。
以集合Set为例,当新加一个对象时,需要判断现有集合中是否已经存在与此对象相等的对象,如果没有hashCode()方法,需要将Set进行一次遍历,并逐一用equals()方法判断两个对象是否相等,此种算法时间复杂度为o(n)。通过借助于hasCode方法,先计算出即将新加入对象的哈希码,然后根据哈希算法计算出此对象的位置,直接判断此位置上是否已有对象即可。
(注:Set的底层用的是Map的原理实现)
8、hash
为一系列的输入值生成哈希码,该方法的参数是可变参数。
源码如下:
它是将所有的输入值都放到一个数组,然后调用Arrays.hashCode(Object[])方法来实现哈希码的生成。
对于当一个对象包含多个成员,重写Object.hashCode方法时,hash方法非常有用。
该方法对于有多个字段的对象实现哈希值很有用,例如: 一个对象有三个字段x,y,z, 则可以通过如下方式获取对象的哈希值:
警告: 当提供的参数是一个对象的引用,返回值不等于该对象引用的哈希码。这个值可以通过调用hashCode方法来计算。
9、toString
toString(Object o)
返回指定对象的字符串形式。如果参数为空 对象null,则返回字符串“null”。
源码如下:
String类的valueOf方法:String.valueOf(Object obj)方法的内部实现为
java.lang.Object类的toString()方法:Object.toString()方法的内部实现为
返回字符串值,即:类的全限定名 + @ + 16进制码
toString(Object o, String nullDefault)
返回指定对象的字符串表示形式。如果参数为空对象null,则返回第二个参数nullDefault所指定的对象。
源码如下:
10、compare
如果两个参数相同则返回整数0。因此,如果两个参数都为空对象null,也是返回整数0。
注意:如果其中一个参数是空对象null,是否会抛出空指针异常NullPointerException取决于排序策略,如果有的话,则由Comparator来决定空值null。
源码如下:
11、Index系列
checkIndex
检查index是否在0 (含)到length (不包括)范围内。
如果以下任何不等式为真,则定义index超出范围:
- index < 0
- index >= length
- length < 0 ,这是前者的不平等所暗示的
源码如下:
checkFromToIndex
检查是否在子范围从fromIndex (包括)到toIndex (不包括)是范围界限内0 (包括)到length (不包括)。
如果以下任何不等式为真,则子范围被定义为超出界限:
- fromIndex < 0
- fromIndex > toIndex
- toIndex > length
- length < 0,这是前者的不平等所暗示的
源码如下:
简单理解就是1-2是不是在区间0-3内;
checkFromIndexSize
检查是否在子范围从fromIndex (包括)到fromIndex + size (不包括)是范围界限内0 (包括)到length (不包括)。
如果以下任何不等式为真,则子范围被定义为超出界限:
- fromIndex < 0
- size < 0
- fromIndex + size > length ,考虑到整数溢出
- length < 0 ,这是前者的不平等所暗示的
源码如下:
简单理解就是是否越界,意思就是1+(1+size)是不是在区间0-3内;
ps:
近期要把JDK的一些常用类库的源码整理出来,欢迎大家指正和讨论。
如果有错误的地方,欢迎各位指正。
如果您觉得还不错,点个赞的话 我会很感激的,谢谢~