Java面试题JavaSE基础之Object公用方法equals和hashCode深浅拷贝
Posted 一宿君
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java面试题JavaSE基础之Object公用方法equals和hashCode深浅拷贝相关的知识,希望对你有一定的参考价值。
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深浅拷贝的主要内容,如果未能解决你的问题,请参考以下文章