你知道Object中有哪些方法及其作用吗?

时间:2019-06-16
本文章向大家介绍你知道Object中有哪些方法及其作用吗?,主要包括你知道Object中有哪些方法及其作用吗?使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一、引言二、Object方法详解1.1、registerNatives()1.2、getClass()1.2.1、反射三种方式:1.3、hashCode()1.4、equals()1.4、clone()1.5、toString()1.6、wait()/ wait(long)/ waite(long,int)1.7、notify()/notifyAll()1.8、finalize()1.8.1、对象在内存中的状态1.8.2、垃圾回收机制1.8.3、强制垃圾回收三、总结

一、引言

Object是java所有类的基类,是整个类继承结构的顶端,也是最抽象的一个类。大家天天都在使用toString()、equals()、hashCode()、waite()、notify()、getClass()等方法,或许都没有意识到是Object的方法,也没有去看Object还有哪些方法以及思考为什么这些方法要放到Object中。本篇就每个方法具体功能、重写规则以及自己的一些理解。

二、Object方法详解

Object中含有:registerNatives()、getClass()、hashCode()、equals()、clone()、toString()、notify()、notifyAll()、wait(long)、wait(long,int)、wait()、finalize()共十二个方法。这个顺序是按照Object类中定义方法的顺序列举的,下面我也会按照这个顺序依次进行讲解。

1.1、registerNatives()

public class Object {
    private static native void registerNatives();
    static {
        registerNatives();
    }
}

什么鬼?哈哈哈,我刚看到这方法,一脸懵逼。从名字上理解,这个方法是注册native方法(本地方法,由JVM实现,底层是C/C++实现的)向谁注册呢?当然是向JVM,当有程序调用到native方法时,JVM才好去找到这些底层的方法进行调用。

Object中的native方法,并使用registerNatives()向JVM进行注册。(这属于JNI的范畴,9龙暂不了解,有兴趣的可自行查阅。)

static JNINativeMethod methods[] = {
    {"hashCode",    "()I",                    (void *)&JVM_IHashCode},
    {"wait",        "(J)V",                   (void *)&JVM_MonitorWait},
    {"notify",      "()V",                    (void *)&JVM_MonitorNotify},
    {"notifyAll",   "()V",                    (void *)&JVM_MonitorNotifyAll},
    {"clone",       "()Ljava/lang/Object;",   (void *)&JVM_Clone},
};

为什么要使用静态方法,还要放到静态块中呢?

上一篇整理了类加载流程我们知道了在类初始化的时候,会依次从父类到本类的类变量及类初始化块中的类变量及方法按照定义顺序放到< clinit>方法中,这样可以保证父类的类变量及方法的初始化一定先于子类。所以当子类调用相应native方法,比如计算hashCode时,一定可以保证能够调用到JVM的native方法。

1.2、getClass()

public final native Class getClass():这是一个public的方法,我们可以直接通过对象调用。

类加载的第一阶段类的加载就是将.class文件加载到内存,并生成一个java.lang.Class对象的过程。getClass()方法就是获取这个对象,这是当前类的对象在运行时类的所有信息的集合。这个方法是反射三种方式之一。

1.2.1、反射三种方式:
  1. 对象的getClass();
  2. 类名.class;
  3. Class.forName();
class extends ObjectTest {
    private void privateTest(String str) {
        System.out.println(str);
    }

    public void say(String str) {
        System.out.println(str);
    }
}

public class ObjectTest {
    public static void main(String[] args) throws Exception {
        ObjectTest  = new ();
        //获取对象运行的Class对象
        Class<? extends ObjectTest> aClass = .getClass();
        System.out.println(aClass);

        //getDeclaredMethod这个方法可以获取所有的方法,包括私有方法
        Method privateTest = aClass.getDeclaredMethod("privateTest", String.class);
        //取消java访问修饰符限制。
        privateTest.setAccessible(true);
        privateTest.invoke(aClass.newInstance(), "private method test");

        //getMethod只能获取public方法
        Method say = aClass.getMethod("say", String.class);
        say.invoke(aClass.newInstance(), "Hello World");
    }
}
//输出结果:
//class test.
//private method test
//Hello World

反射主要用来获取运行时的信息,可以将java这种静态语言动态化,可以在编写代码时将一个子对象赋值给父类的一个引用,在运行时通过反射可以或许运行时对象的所有信息,即多态的体现。对于反射知识还是很多的,这里就不展开讲了。

1.3、hashCode()

public native int hashCode(); 这是一个public的方法,所以子类可以重写它。这个方法返回当前对象的hashCode值,这个值是一个整数范围内的(-2^31 ~ 2^31 - 1)数字。

对于hashCode有以下几点约束

  • 在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改;
  • 如果两个对象 x.equals(y) 方法返回true,则x、y这两个对象的hashCode必须相等。
  • 如果两个对象x.equals(y) 方法返回false,则x、y这两个对象的hashCode可以相等也可以不等。但是,为不相等的对象生成不同整数结果可以提高哈希表的性能。
  • 默认的hashCode是将内存地址转换为的hash值,重写过后就是自定义的计算方式;也可以通过System.identityHashCode(Object)来返回原本的hashCode。
public class HashCodeTest {
    private int age;
    private String name;

    @Override
    public int hashCode() {
        Object[] a = Stream.of(age, name).toArray();

        int result = 1;
        for (Object element : a) {
            result = 31 * result + (element == null ? 0 : element.hashCode());
        }
        return result;
    }
}

推荐使用Objects.hash(Object… values)方法。相信看源码的时候,都看到计算hashCode都使用了31作为基础乘数,为什么使用31呢?我比较赞同与理解result * 31 = (result<<5) - result。JVM底层可以自动做优化为位运算,效率很高;还有因为31计算的hashCode冲突较少,利于hash桶位的分布。

1.4、equals()

public boolean equals(Object obj);用于比较当前对象与目标对象是否相等,默认是比较引用是否指向同一对象。为public方法,子类可重写。

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

为什么需要重写equals方法?

因为如果不重写equals方法,当将自定义对象放到map或者set中时;如果这时两个对象的hashCode相同,就会调用equals方法进行比较,这个时候会调用Object中默认的equals方法,而默认的equals方法只是比较了两个对象的引用是否指向了同一个对象,显然大多数时候都不会指向,这样就会将重复对象存入map或者set中。这就破坏了map与set不能存储重复对象的特性,会造成内存溢出

重写equals方法的几条约定:

  1. 自反性:即x.equals(x)返回true,x不为null;
  2. 对称性:即x.equals(y)与y.equals(x)的结果相同,x与y不为null;
  3. 传递性:即x.equals(y)结果为true, y.equals(z)结果为true,则x.equals(z)结果也必须为true;
  4. 一致性:即x.equals(y)返回true或false,在未更改equals方法使用的参数条件下,多次调用返回的结果也必须一致。x与y不为null。
  5. 如果x不为null, x.equals(null)返回false。

我们根据上述规则来重写equals方法。

public class EqualsTest{
    private int age;
    private String name;
    //省略get、set、构造函数等
     @Override
    public boolean equals(Object o) {
        //先判断是否为同一对象
        if (this == o) {
            return true;
        }
        //再判断目标对象是否是当前类及子类的实例对象
        //注意:instanceof包括了判断为null的情况,如果o为null,则返回false
        if (!(o instanceof )) {
            return false;
        }
         that = () o;
        return age == that.age &&
                Objects.equals(name, that.name);
    }

     public static void main(String[] args) throws Exception {
         EqualsTest1 equalsTest1 = new EqualsTest1(23"9龙");
        EqualsTest1 equalsTest12 = new EqualsTest1(23"9龙");
        EqualsTest1 equalsTest13 = new EqualsTest1(

原文地址:https://www.cnblogs.com/9dragon/p/11037283.html