重拾JavaSE基础——Object类

Object 类是所有类的父类,处于Java的最顶端。 Object 类有几个方法值得我们关注

registerNatives 方法

大家会发现Object类的最上面有这样的代码,不只是 Object 类,只要需要用到本地方法的类都有这样的代码

private static native void registerNatives();
static {
    registerNatives();
}

代码的第一行定义了 registerNatives 方法,在类加载是执行 registerNatives 方法,看名字大家都能猜出来他是负责注册本地方法的,包含 registerNatives ()方法的类被加载的时候,除了 registerNatives ()方法以外的所有本地方法会被注册,经过注册的方法才能被使用

在Java调用本地方法的时候需要首先通过 System.loadLibrary() 将包含本地方法实现的动态文件加载进内存,当Java程序需要调用本地方法时,虚拟机在加载的动态文件中定位并链接该本地方法,从而得以执行本地方法。 registerNatives 让程序主动将本地方法链接到调用方,当Java程序需要调用本地方法时就可以直接调用,而不需要虚拟机再去定位并链接。

关于本地方法以后再总结

equlas 方法

比较调用者和入参的内存地址是否相同

public boolean equals(Object obj) {
    return (this == obj);
}

对于 String 字符串类型其实更加在意内容是否相同,所以对他进行了重写,在我们自己写的 JavaBean 中,我们应该对内部的成员变量也判断一下,我们可以使用 intelilJ IDEA 生成这个方法

@Override
public boolean equals(Object o) {
    // 判断地址是否相同
    if (this == o) return true;
    // 判断参数是否为空,判断是否为相同的类
    if (o == null || getClass() != o.getClass()) return false;
    // 转换类型
    UserDTO userDTO = (UserDTO) o;
    // 判断内部
    return Objects.equals(username, userDTO.username) &&
            Objects.equals(password, userDTO.password) &&
            Objects.equals(age, userDTO.age) &&
            Objects.equals(sex, userDTO.sex);
}

这里有个小问题,如果让我自己写这个 equals 方法,我可能回写成这个样子

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o instanceof BasicUserDTO){

        BasicUserDTO userDTO = (BasicUserDTO) o;
        return Objects.equals(username, userDTO.username) &&
            Objects.equals(password, userDTO.password) &&
            Objects.equals(age, userDTO.age) &&
            Objects.equals(sex, userDTO.sex);
    } else {
        return false;
    }
}

如果存在一个 BasicUserDTO 的子类 UserDTO ,以下用上面两种方法会有不同结果

BasicUserDTO basicUserDTO = new BasicUserDTO();
basicUserDTO.setAge(10);
basicUserDTO.setUsername("Rhythm");
basicUserDTO.setPassword("123456");
basicUserDTO.setSex('M');

UserDTO userDTO = new UserDTO();
userDTO.setAge(10);
userDTO.setUsername("Rhythm");
userDTO.setPassword("123456");
userDTO.setSex('M');

System.out.println(basicUserDTO.equals(userDTO));

IDEA 生成的 equals 方法返回的是 false ,我自己写的则返回 false ,这里顺便复习一下 getClass 返回的是类名,而用 intanceof 是用于判断前面的引用变量是否是后面的类、子类或实现类,大部分情况还是用 getClass 比较合适,不需要考虑继承

IDEA 生成的equals方法中使用到 Objects.equals ,内部对入参和当前对象进行了非空判断,避免空指针

toString 方法

这个方法返回的是类名和该类的哈希值,当我们执行 System.out.println(变量) 时默认调用这个方法

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

对于我们自己写的 JavaBean 来说,我们更希望实现返回该实体类的所有字段的值,所以我们都会选择重写这个方法

@Override
public String toString() {
    return "UserDTO{" +
            "username='" + username + '/'' +
            ", password='" + password + '/'' +
            ", age=" + age +
            ", sex=" + sex +
            '}';
}

hashCode 方法

生成哈希值,是个本地方法,具体实现由操作系统提供

public native int hashCode();

在集合中使用散列码查询效率会比较高

在Java应用程序的执行过程中,无论什么时候在同一个对象上调用它, hashCode 方法都必须返回相同的值,不过前提条件是不对对象上的 equals 比较中使用的信息进行修改。此整数不需要在应用程序的一次执行与同一应用程序的另一次执行之间保持一致。如果根据 equals(Object) 方法,两个对象是 相等 的,那么在每个对象上调用 hashCode 方法必须产生 相同 的整数结果。

意思就是只要 equlas 方法中判断的那些变量值不变,散列码就不会改变,如果调用 equals 方法返回 true ,他们的散列码必须相同

如果两个对象根据 equals(Object) 方法是 不相等 的,那么在每个对象上调用 hashCode 方法必须产生 不同的 整数结果,这是不需要的。

equals 方法返回 false ,就不用保证散列码一致了

但是,为了 不相等的对象生成不同的整数结果可能会提高哈希表的性能

在相当实际的情况下,类对象定义的 hashCode 方法确实会为不同的对象返回不同的整数。

意思就是我们还是要重写这个方法, hashCode 方法是个本地方法,不需要我们实现

getClass 方法

public final native Class<?> getClass();

返回运行时类型,在堆中 this 指针指着的那个类型

finalize 方法

protected void finalize() throws Throwable { }

用于释放资源,当垃圾收集器准备收集这个对象时被调用,调用后下一次 GC 的时候对象才会被释放,这个方法基本不用,有以下几个原因

  • 程序只有在内存濒临不够用的时候才会去释放对象
  • 垃圾回收并非“析构”
  • 垃圾回收只跟内存打交道,释放的内存时经过再三考虑的,程序不会再去使用

这个方法资源的释放比较佛,大家还是自己释放资源吧

clone 方法

protected native Object clone() throws CloneNotSupportedException;

克隆方法,这里需要注意一下,该方法只是复制了一份引用,新的引用还是指向原来的方法,这种方式称为 浅克隆

重拾JavaSE基础——Object类

完美的复制一份才叫 深克隆 ,想实现深克隆

重拾JavaSE基础——Object类

在Java中要做到完美的克隆时比较难的,因为难免会用到别人的jar包,如果别人的jar包内部没有重写clone方法,那就不能做到完美地克隆了

重拾JavaSE基础——Object类

wait 方法

public final void wait() throws InterruptedException {
    wait(0);
}

使得当前线程进入睡眠状态,直到有线程调用此对象的 notify 方法或执行 notifyAll 方法的时候会被唤醒。

public final native void wait(long timeout) throws InterruptedException;

可以传入睡眠的时间

public final void wait(long timeout, int nanos) throws InterruptedException

也可以传入额外时间,不过这个方法是用微毫秒来计算的,公式为 100 000 * timeout + nanos

进入睡眠的线程如何被唤醒呢:

notify
notifyAll
interrupt

notify 方法

public final native void notify();

唤醒在该对象上睡眠的某个线程

notifyAll 方法

public final native void notifyAll();

唤醒在该对象上睡眠的所有线程

原文 

https://segmentfault.com/a/1190000022139020

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » 重拾JavaSE基础——Object类

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址