object类以及为什么要重写equals和hashcode?

所有类的父类,可以说是超级父类,他有的方法所有类都有:

protected Object clone();//获得一个对象的副本 默认浅克隆

boolean euqals(Object obj)
int hashcode();//与equals成对出现

String toString();//将对象转换成字符串来输出

protected void finalize();//提示GC回收对象

Class getclass();//获得正在运行的类或接口的class类对象-->反射的基础

//与线程的通信有关
void notify();
void notifyAll();//唤醒其他处于等待的线程
void wait();//当前线程等待
void wait(long timeout);
void wait(long timeout,int nanos);

在java中,如果两个对象的hashcode都相同,可以认为两个对象相等,不同对象的hashcode可能相等

我们来讨论以下三个问题:

如何比较两个对象?为什么要重写equals和hashcode?

我们用equals来比较两个对象是否相等:

        BaoLei baoLei=new BaoLei();
        BaoLei baoLei1=new BaoLei();
        System.out.println(baoLei.equals(baoLei1));
//equals的底层
public boolean equals(Object obj) {
    return (this == obj);
}

可以看到object的equals的底层用的是==,因此返回值是FALSE,看看两个对象的hashcode

System.out.println(baoLei1.hashCode());
System.out.println(baoLei.hashCode());

两个对象的hashcode也是不一样的,可能有人会觉得是因为对象的属性不一样,那么我们来创建两个属性完全一样的两个属于同一个类的对象,并且看看他们是否equals对方,并且hashcode是否相等

Student student=new Student(36,"张三");
Student student1=new Student(36,"张三");
System.out.println(student == student1);
System.out.println(student.equals(student1));
System.out.println(student.hashCode());
System.out.println(student1.hashCode());

输出结果两个都不相等,但是如果我们想让他相等,那么在类里面重写equals来让他相等:

public boolean equals(@NonNull Object obj){
    if(!(obj instanceof Student))
        return false;
    Student stu=(Student)obj;
    return Objects.equals(this.id,stu.id)&&Objects.equals(this.id,stu.id);
}

此时equals返回值变成TRUE,hashcode还是FALSE,会导致一些错乱,因此我们也要重写hashcode,但我不会写hashcode,我就仿照着string底层来重写一下hashcode:

//string底层重写的hashcode方法
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;//获得每个变量的hashcode放入数组

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];//乘以31加上每个变量的hashcode
        }
        hash = h;
    }
    return h;
}

//仿写hashcode
public int hashCode(){
        int hash=0;
        hash=hash*31+id.hashCode();
        hash=hash*31+name.hashCode();
        return hash;
  }

结果二者都是TRUE,因此重写equals时也要重写hashcode,可以用alt+insert快捷键选择重写,也可以直接增加注解@@EqualsAndHashCode,用@Data来替换有参无参构造,set/get方法,这里面也包含重写hashcode和equals

什么是克隆?object.clone默认是什么克隆?深克隆和浅克隆什么区别?

需要实现coloneable接口不然会出现异常

public class Student implements Cloneable {
    private Integer id;
    private String name;
    private String[] hobby;
    public Student(Integer id, String name, String[] hobby) {
        System.out.println("all args constructor");
        this.id = id;
        this.name = name;
        this.hobby = hobby;
    }
    public Student() {
        System.out.println("no args constructor");
    }
    //重写克隆方法,浅克隆
    @Override
    public Student clone()  {
        Student student = null;
        try {
            student = (Student)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student ;
    }
}

这是个标识接口,表示该类可以被克隆,不加的话是无法用clone

String[] hobby={"basketball","volleyball"};
        Student student=new Student(36,"张三",hobby);
        Student clone = student.clone();
        System.out.println(student);
        //输出all args constructor
        // Student(id=36, name=张三, hobby=[basketball, volleyball])
        System.out.println(clone);
        //输出Student(id=36, name=张三, hobby=[basketball, volleyball])
        System.out.println(clone==student);//false
        //将原对象属性值修改,再次输出
        student.setId(10);
        student.setName("李四");
        student.getHobby()[0]="play";
        System.out.println(student);
        //Student(id=10, name=李四, hobby=[play, volleyball])
        System.out.println(clone);
        //Student(id=36, name=张三, hobby=[play, volleyball])
        System.out.println(clone==student);//false

说明克隆对象不执行构造方法而且对于属性修饰符为final的,值不会改变,但是数组的值会随着原来对象值的改变而改变,这就是浅克隆,也就是浅克隆拿的是表面的地址,当地址里面的值发生改变的时候,浅克隆得到的数据也会随之改变,而深克隆拿的是底下的值,就算地址改变了,也不会改变值

//深克隆
public Student clone()  {
    Student student = null;
    try {
        student = (Student)super.clone();
        student.setHobby(hobby.clone());//加上这句给hobby重新赋值,就成为深克隆
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
    }
    return student ;
}

GC可以回收哪些引用?

判断对象是否有用算法:

  1. 引用计数法

  2. 可达性分析法

强引用:

在运行期间,宁愿抛出堆栈内存溢出问题也不会回收这种类型的对象

String[] hobby={"basketball","volleyball"};
Student student=new Student(36,"张三",hobby);//强引用,表示对象必须的,有用的对象
软引用:

有用非必须对象,只有内存不足时候会回收软引用对象,即使将对象变为无用(空),手动调用gc来回收,也会判断内存是否足够才会回收

String[] hobby={"basketball","volleyball"};
Student student = new Student(36,"张三",hobby);
SoftReference<Student> softReference=new SoftReference<>(student);
student=null;
System.gc();
弱引用:

有用非必须,将对象变为无用(空),手动调用gc来回收,计算机就会将它回收

String[] hobby={"basketball","volleyball"};
Student student = new Student(36,"张三",hobby);
WeakReference<Student> weakReference=new WeakReference<>(student);
student=null;
System.gc();
虚引用:

没用的对象,即使不将对象变成无用,程序也会认为它是无用的

String[] hobby={"basketball","volleyball"};
Student student = new Student(36,"张三",hobby);
ReferenceQueue<Student> referenceQueue=new ReferenceQueue<>();
PhantomReference<Student> phantomReference=new PhantomReference<>(student,referenceQueue);
posted @ 2022-10-31 19:50  Liku007  阅读(30)  评论(0编辑  收藏  举报