我的jdk源码(十七):Objects类

一、概述

    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觉浅】,获取第一时间更新哦!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java觉浅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值