Object和String

一、Object类

package java.lang;


public class Object {

    private static native void registerNatives();
    static {
        registerNatives();
    }
	//获取class对象
    public final native Class<?> getClass();
	//hash码
    public native int hashCode();
	//判断对象是否相等
    public boolean equals(Object obj) {
        return (this == obj);
    }
	//克隆对象
    protected native Object clone() throws CloneNotSupportedException;
	//toString 方法
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
	//唤醒在对象上等待的线程
    public final native void notify();
	//唤醒所有在对象上等待的线程
    public final native void notifyAll();
  	//线程等待
    public final native void wait(long timeout) throws InterruptedException;
	//线程等待多少时间
    public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos > 0) {
            timeout++;
        }

        wait(timeout);
    }
	//线程等待
    public final void wait() throws InterruptedException {
        wait(0);
    }
	//垃圾回收方法
    protected void finalize() throws Throwable { }
}

1.1、为什么要有hashCode和equals方法

equals方法用来判断两个对象是否相等

hashCode 方法返回该对象的哈希码值。它可以为像 HashMap 这样的哈希表有益,也有书上说是对象的内存地址

假如没有hashCode,集合中的一个对象的判断只能通过遍历每一个集合中的元素然后再用equals判断,这种方式效率不高,如果能为每一个对象都分配一个特殊的编码,根据这个编码可以计算出这个对象在集合中的位置,然后再用equals判断是否存在该对象,使用这种方式效率比较高,其实HashMapputget操作就是这么实现的。

1.2、为什么hashCode和equals方法要同时重写

由于不同对象的内容不一样,所以equals方法必须重写。

那为什么要一同重写hashCode,看下hashCode注释:

/**
 * Returns a hash code value for the object. This method is
 * supported for the benefit of hash tables such as those provided by
 * {@link java.util.HashMap}.
 * <p>
 * The general contract of {@code hashCode} is:
 * <ul>
 * <li>Whenever it is invoked on the same object more than once during
 *     an execution of a Java application, the {@code hashCode} method
 *     must consistently return the same integer, provided no information
 *     used in {@code equals} comparisons on the object is modified.
 *     This integer need not remain consistent from one execution of an
 *     application to another execution of the same application.
 * <li>If two objects are equal according to the {@code equals(Object)}
 *     method, then calling the {@code hashCode} method on each of
 *     the two objects must produce the same integer result.
 * <li>It is <em>not</em> required that if two objects are unequal
 *     according to the {@link java.lang.Object#equals(java.lang.Object)}
 *     method, then calling the {@code hashCode} method on each of the
 *     two objects must produce distinct integer results.  However, the
 *     programmer should be aware that producing distinct integer results
 *     for unequal objects may improve the performance of hash tables.
 * </ul>
 * <p>
 * As much as is reasonably practical, the hashCode method defined by
 * class {@code Object} does return distinct integers for distinct
 * objects. (This is typically implemented by converting the internal
 * address of the object into an integer, but this implementation
 * technique is not required by the
 * Java&trade; programming language.)
 *
 * @return  a hash code value for this object.
 * @see     java.lang.Object#equals(java.lang.Object)
 * @see     java.lang.System#identityHashCode
 */

翻译:

  1. 在应用程序的执行期间,只要对象的 equals 方法的比较操作所用到的信息没有被修改,那么对同一个对象的多次调用,hashCode 方法都必须始终返回同一个值。
  2. 如果两个对象根据 equals 方法比较是相等的,那么调用这两个对象中的 hashCode 方法都必须产生同样的整数结果。
  3. 如果两个对象根据 equals 方法比较是不相等的,那么调用者两个对象中的 hashCode 方法,则不一定要求 hashCode 方法必须产生不同的结果。但是给不相等的对象产生不同的整数散列值,是有可能提高散列表(hash table)的性能。

下面来做一个违背注释2的验证:

我们定义一个Person的类,只重写equals方法,然后作为HashMapkey

import java.util.Objects;

/**
 * @author DUCHONG
 * @since 2020-09-07 17:26:36
 **/
public class Person {

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private String name;
    private int age;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

}

main方法里面new了两个对象person1person2,由于重写了equals方法,nameage都相同,然后将person1放在hashMap中,利用person2作为keyget

import java.util.HashMap;

/**
 * @author DUCHONG
 * @since 2020-09-07 17:20
 **/
public class ObjectDemo {

    public static void main(String[] args) {


        Person person1 = new Person("demo2",2);
        Person person2 = new Person("demo2",2);

        System.out.println("person1.equals person2---"+person1.equals(person2));

        //只重写equals不重写hashCode,调用object的hashCode
        System.out.println("person1 hashCode---"+person1.hashCode());
        System.out.println("person1 hashCode---"+person2.hashCode());


        HashMap<Person,String> hashMap=new HashMap<>();
        hashMap.put(person1,"person1");

        //这里用person1相同的person2去获取
        System.out.println("name---"+hashMap.get(person2));
    }
}

结果

image-20200907174158422

看到了吧,获取到的值为null,由于hashmapput时是以keyhashCode来计算对象在集合中的位置的,由于person1person2hashCode 不同,自然分配到两个不同的位置,然后用equal相同的key是获取不到的。

但是namedemo2的那个person确实在集合中啊,再者说你把Person换替成String这个类,一个String类型的key对应一个value存在hashmap中,然后用相同的key却获取不到这个value,这就乱了,集合没法用了。

接下来重写hashCode方法,重新运行main方法

import java.util.Objects;

/**
 * @author DUCHONG
 * @since 2020-09-07 17:26:36
 **/
public class Person {

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private String name;
    private int age;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

结果

image-20200907175800945

同时重写equals()hashCode()可以正常获取,再去看String类的源码,也是同时重写了这两个方法的。

二、String类

既然上面提到了String类,这里就简单介绍下,jvm为了提供性能和减少内存开销,提供了一个常量池的,当一个String类被创建时,先会去查看常量池中有没有该对象,有,返回引用,没有则创建。

Java 6及以前,字符串常量池存放在永久代

Java 7中 oracle的工程师对字符串池的逻辑做了很大的改变,即将字符串常量池的位置调整到Java堆内

Java8元空间,字符串常量在堆

2.1、两种初始化方式

直接赋值:会自动进入常量池,也就是常量池中有一个对象的引用,当再创建同样的字符串时,发现常量池中有则直接使用。

使用new关键字:堆中有对象实例,还是先检查常量池中有没有,如果有,返回实例,如果没有则创建,都需要在堆中new一个出来,若常量池中无,则创建两个对象,有,则创建一个对象。

2.2、==和equals

==基本数据类型,比较的是引用数据类型比较的是内存地址

equals:属于Object类的方法,没有重写过equals方法,比较的是地址,字符串里面的equals被重写过了,比较的是

posted @ 2020-09-07 20:28  npe0  阅读(297)  评论(0编辑  收藏  举报