Java面试题JavaSE基础之Object公用方法equals和hashCode深浅拷贝

Posted 一宿君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试题JavaSE基础之Object公用方法equals和hashCode深浅拷贝相关的知识,希望对你有一定的参考价值。

1、Object有哪些共用方法

Object是所有类的父类,任何类都默认继承Object。

  • clone保护方法:实现对象的浅拷贝,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
  • equals方法:如果不重写,在Object中与 == 号作用一样,子类一般需要重写该方法。
  • hashCode方法:该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法(后续我们着重解释),这个方法在一些具有哈希功能的Collection中经常用到。
  • getClass方法:这是final标注的方法,获取运行时类型。
  • wait方法:使当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait() 方法一直等待,直到获取锁或者被中断。wait(long timeout) 设定一个超时时间间隔,如果在规定时间内获得锁,就返回。

调用该方法后当前线程会进入睡眠状态,直到以下时间发生:

  • 其他线程调用了该对象的notify方法。
  • 其他线程调用了该对象的notifyAll方法。
  • 其他线程调用了interrupt中断该线程。
  • 时间间隔到了。
  • 此时刻线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。

解释说明:

  • notify:表示唤醒在该对象上等待的某个线程;
  • notifyAll:表示唤醒在该对象上等待的所有线程;
  • toString:转换成字符串,一般子类都有重写,否则打印句柄。

2、hashCode()和equals()的区别

主要从两个角度区分:一个是性能,另一个是可靠性

2.1、equals()方法既然已经可以实现对比的功能了,为什么还需要hashCode()呢?

因为重写的equals()方法里面,一般比较的内容比较全面和复杂,这样效率就比较低了,而利用hashCode()方法来进行对比,则只需要对比生成的hash值就可以了,效率比较高。

2.2、hashCode()既然效率这么高为什么还要使用equals呢?

因为hashCode()并不是完全可靠的,有时候不同的对象他们生成的hashCode也会一样(这点在往HashMap集合中put值时,发生的hash碰撞冲突,就是因为这个原因),所以hashCode()只能说是大部分时候可靠,并不是绝对可靠的,所以我们可以得出两个重要结论,当面试的时候只要能说出这两个结论,并理解原理即可:

  • equals()方法相等的两个对象他们的hashCode()一定相等,也就是用equals()对比是绝对可靠的;
  • hashCode()方法相等的两个对象他们的equals()不一定相等,也就是hashCode()不是绝对可靠的。

2.3、扩展

1、阿里巴巴开发规范明确规定:

  • 只要重写equals(),就必须要重写hashCode();
  • 因为Set存储的是不重复的对象,依据hashCode()和equals()进行判断,所以Set存储的对象必须重写这个方法;
  • 如果自定对象作为Map键,那么必须重写hashCode()和equals();
  • String重写了hashCode()和equals()方法,所以我们可以非常愉快的使用String对象作为key来使用。

2、什么时候需要重写?

  • 一般的地方不需要重载hashCode(),只有当类需要放在HashTable、HashMap、HashSet等等哈希结构的集合时才会重载hashCode();

3、那为什么需要重载hashCode()呢?

  • 如果你重写了equals(),比如说基于对象的内容实现的,而保留hashCode()的实现不变,那么很可能某两个对象明明是“相等”的,而hashCode却不一样。

这样的话,当你用其中一个作为键保存到HashMap、Hashtable或者HashSet中时,再以“相等”的找另一个键值去查找他们的时候,则根本找不到。

4、为什么equals相等,hashCode就一定相等,而hashCode相等,却不要求equals相等?

  • 因为是按照hashCode来访问小内存块,所以hashCode必须相等;
  • HashMap获取一个对象是比较key的hashCode相等和equals为true;
  • 之所以hashCode相等,而equals却可以不等,就比如Object A和Object B两个对象都有属性name,那么将name属性作为键key,hashCode都以那么来计算,相当于name是一个字符串“name”,所以hashCode绝对是一样的,但是两个对象在内存种不是指向同一个对象的,那么属性的内容不同,所以equals为false。

5、为什么需要hashCode?

  • 通过hashCode可以很快的查找到小内存块;
  • 通过hashCode比较比equals方法块,方get时先比较hashCode,如果hashCode不同,则直接放回true,效率贼六。

3、浅拷贝和深拷贝的区别?

3.1、浅拷贝

(1)定义

  • 复制的对象中所有的变量都含有与原来的对象中属性相同的值,而所有的对其他对象的引用仍然指向原来对象中所引用的对象。即对象的浅拷贝会对“主对象”进行拷贝,但是不会复制“主对象”中的所引用的对象。
  • 总的来说,浅拷贝只对复制所考虑的对象,而不会复制所考虑对象中的引用对象。

(2)浅拷贝实例:

  • Student

    public class Student implements Cloneable {
        private String name;
        private int age;
        private Teacher teacher;
    
        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;
        }
    
        public Teacher getTeacher() {
            return teacher;
        }
    
        public void setTeacher(Teacher teacher) {
            this.teacher = teacher;
        }
    
        @Override
        public Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    
  • Teacher

    public class Teacher implements Cloneable {
        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;
        }
    }
    
  • 测试浅拷贝类ShallowCopy

    public class ShallowCopy {
        public static void main(String[] args) throws CloneNotSupportedException {
    
            Teacher teacher = new Teacher();
            teacher.setName("老庞");
            teacher.setAge(35);
    
            Student student1 = new Student();
            student1.setName("一宿君");
            student1.setAge(21);
            student1.setTeacher(teacher);
    
            /**
             * 浅拷贝一个student1
             */
            Student student2 = (Student)student1.clone();
            System.out.println("拷贝后******************");
            System.out.println(student2.getName());//一宿君
            System.out.println(student2.getAge());//21
            System.out.println(student2.getTeacher().getName());//老庞
            System.out.println(student2.getTeacher().getAge());//21
    
            System.out.println("修改老师的属性值后******************");
            teacher.setName("老葛");
            System.out.println(student1.getTeacher().getName());//老葛
            System.out.println(student2.getTeacher().getName());//老葛
    
        }
    }
    

(3)分析原理

  • 结果分析:两个引用student1和student2指向不同的两个对象,但是引用student1和student2的两个teacher指向的还是student1中的那个teacher引用对象,所以是浅拷贝。

3.2、深拷贝

(1)定义

  • 深拷贝是一整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝,深拷贝相对于浅拷贝速度叫慢并且花销较大。
  • 总的来说,深拷贝把要复制的对象所引用的对象都复制一遍。

(2)案例分析
Student和Teacher的属性仍然使用上面的例子,只是对clone做了修改

  • Teacher
        //Teacher类也要加上clone方法
    	@Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    
  • Student
    @Override
        public Object clone() throws CloneNotSupportedException {
            //浅拷贝(只拷贝一层)
            //return super.clone();
    
            //浅拷贝(外部类对象)
            Student student = (Student)super.clone();
            //深拷贝(内部类引用,也要拷贝)
            student.setTeacher((Teacher)student.getTeacher().clone());
            
            return student;
        }
    
  • DeepCopy
    public class DeepCopy {
        public static void main(String[] args) throws CloneNotSupportedException {
            Teacher teacher = new Teacher();
            teacher.setName("老庞");
            teacher.setAge(35);
    
            Student student1 = new Student();
            student1.setName("一宿君");
            student1.setAge(21);
            student1.setTeacher(teacher);
    
            /**
             * 浅拷贝一个student1
             */
            Student student2 = (Student)student1.clone();
            System.out.println("拷贝后******************");
            System.out.println(student2.getName());//一宿君
            System.out.println(student2.getAge());//21
            System.out.println(student2.getTeacher().getName());//老庞
            System.out.println(student2.getTeacher().getAge());//21
    
            System.out.println("修改老师的属性值后******************");
            teacher.setName("老葛");
            System.out.println(student1.getTeacher().getName());//老葛
            System.out.println(student2.getTeacher().getName());//老庞(新拷贝的对象没有被修改)
    
        }
    }
    

(3)分析原理

  • 两个引用student1和student2指向不同的两个对象,两个引用student1和student2中的两个teacher引用指向的是两个对象,但是对teacher对象的修改只能影响student1对象,所以说是深拷贝。

3.3、总结

  • 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
  • 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。

以上是关于Java面试题JavaSE基础之Object公用方法equals和hashCode深浅拷贝的主要内容,如果未能解决你的问题,请参考以下文章

Java面试题JavaSE基础之Java面向对象

018期JavaSE面试题(十八):JVM之垃圾回收

JavaSE编程基础面试题

java面试题大全带答案,热度飙升!

018期JavaSE面试题(十八):JVM之垃圾回收

尚硅谷全套课件整理:Java前端大数据安卓面试题