栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

JavaSE基础之Object共用方法、equals和hashCode、深浅拷贝
  • 1、Object有哪些共用方法
  • 2、hashCode()和equals()的区别
    • 2.1、equals()方法既然已经可以实现对比的功能了,为什么还需要hashCode()呢?
    • 2.2、hashCode()既然效率这么高为什么还要使用equals呢?
    • 2.3、扩展
  • 3、浅拷贝和深拷贝的区别?
    • 3.1、浅拷贝
    • 3.2、深拷贝

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);
    
            
            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(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);
    
            
            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(student2.getTeacher().getName());//老庞
    
        }
    }
    

(3)分析原理

  • 两个引用student1和student2指向不同的两个对象,两个引用student1和student2中的两个teacher引用指向的是两个对象,但是对teacher对象的修改只能影响student1对象,所以说是深拷贝。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/297996.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号