• 【面试 JDK】【第一篇】Object类面试详解


    1.Object类有哪些方法

      1》clone()方法

    保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。

      2》getClass()方法

    final方法,获得运行时类型。

      3》toString()方法

    常用方法,一般子类都会覆盖

      4》finalize()方法

    该方法用于释放资源。因为无法确定该方法什么时候被调用,很少使用。

      5》equals()方法

    该方法是非常重要的一个方法。一般equals和==是不一样的,但是在Object中两者是一样的。子类一般都要重写这个方法。

      6》hashCode()方法

    该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到。

    一般必须满足obj1.equals(obj2)==true。可以推出obj1.hash- Code()==obj2.hashCode(),但是hashCode相等不一定就满足equals。不过为了提高效率,应该尽量使上面两个条件接近等价。

       7》wait()方法

    wait方法就是使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。

    调用该方法后当前线程进入睡眠状态,直到以下事件发生。

    (1)其他线程调用了该对象的notify方法。

    (2)其他线程调用了该对象的notifyAll方法。

    (3)其他线程调用了interrupt中断该线程。

    (4)时间间隔到了。

    此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。

      8》notify()方法

    该方法唤醒在该对象上等待的某个线程

      9》notifyAll()方法

    该方法唤醒在该对象上等待的所有线程

    ===============================================================

    2.==和equals()方法的区别

      1》Object中的equals()方法就是做==

      2》String中重写了equals()方法,在==比较之后还做了值的比较。

        ==比较地址是否相等

        equals先比较地址是否相等,若不相等,再比较值是否相等。

      3》上面说的==和equals()是在引用数据类型来说,在基本数据类型上 只有==比较,值是否相等。基本数据类型没有用equals()方法的比较。

    ===============================================================

    3.clone()方法引申出来的克隆,关于深克隆和浅克隆的区别

      1》浅克隆概念:仅克隆原对象的值给新的对象,原对象中如果有属性为对象,则仅克隆此属性的引用地址给新对象。即原对象和新对象对于一个属性对象是同一个引用地址。

      2》深克隆概念:不仅克隆原对象的值给新对象,也会将原对象中对象属性重新开辟空间给新对象。

    相关代码:

    package com.sxd.test2;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    
    @Data
    @AllArgsConstructor
    public class Teacher {
    
        private int id;
        private String name;
    }
    View Code
    package com.sxd.test2;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    
    @Data
    @AllArgsConstructor
    public class Student implements Cloneable{
        private int id;
        private String name;
        private Teacher teacher;
    
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    View Code
    package com.sxd.test2;
    
    import org.junit.Test;
    
    public class DataTest {
    
        @Test
        public void test() throws CloneNotSupportedException {
            Student s1 = new Student(1,"小明",new Teacher(1,"张老师"));
            Student s2 = (Student) s1.clone();
            System.out.println(s1 ==s2);
            System.out.println(s1.getName()+"《》《》《》"+s2.getName());
            s2.setName("小张");
            System.out.println(s1.getName()+"《》《》《》"+s2.getName());
            System.out.println(s1.getTeacher() +"《》《》《》"+ s2.getTeacher());
            Teacher teacher = s1.getTeacher();
            teacher.setName("王老师");
            System.out.println(s1.getTeacher() +"《》《》《》"+ s2.getTeacher());
    
        }
    }
    View Code

      1》Object中的clone()方法,提供的是浅克隆,且必须要实现Cloneable接口,否则会抛异常。

      2》要实现深克隆的话,有三种方式

        2.1》就是在student重写clone()方法,teacher作为student的一个引用对象属性,也要实现Cloneable接口, 重写clone()方法

          不足:如果teacher中又有引用对象属性,那就要一层一层都要做这样的事情。

        2.2》通过序列化和反序列化实现深度克隆,所有相关类只要实现Serializable接口,然后把要克隆的对象写入流中,再从流中将该对象读取出来,就可以重建对象,实现深度克隆。

    相关代码:

    package com.sxd.test2;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    
    import java.io.Serializable;
    
    @Data
    @AllArgsConstructor
    public class Teacher implements Serializable{
    
        private int id;
        private String name;
    }
    View Code
    package com.sxd.test2;
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    
    import java.io.*;
    
    @Data
    @AllArgsConstructor
    public class Student implements Serializable{
        private int id;
        private String name;
        private Teacher teacher;
    
        public Object deepClone() throws IOException, ClassNotFoundException {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(outputStream);
            oos.writeObject(this);
    
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(inputStream);
    
            return ois.readObject();
        }
    }
    View Code
    package com.sxd.test2;
    
    import org.junit.Test;
    
    import java.io.IOException;
    
    public class DataTest {
    
        @Test
        public void test() throws IOException, ClassNotFoundException {
            Student s1 = new Student(1,"小明",new Teacher(1,"张老师"));
            Student s2 = (Student) s1.deepClone();
            System.out.println(s1 ==s2);
            System.out.println(s1.getName()+"《》《》《》"+s2.getName());
            s2.setName("小张");
            System.out.println(s1.getName()+"《》《》《》"+s2.getName());
            System.out.println(s1.getTeacher() +"《》《》《》"+ s2.getTeacher());
            Teacher teacher = s1.getTeacher();
            teacher.setName("王老师");
            System.out.println(s1.getTeacher() +"《》《》《》"+ s2.getTeacher());
    
        }
    }
    View Code

        2.3》JSON转化

    相关代码:

    package com.sxd.test2;
    
    
    import lombok.AllArgsConstructor;
    import lombok.Data;
    
    
    @Data
    @AllArgsConstructor
    public class Teacher{
    
        private int id;
        private String name;
    }
    View Code
    package com.sxd.test2;
    
    import com.google.gson.Gson;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    
    
    @Data
    @AllArgsConstructor
    public class Student{
        private int id;
        private String name;
        private Teacher teacher;
    
        public Student deepClone2(){
            Gson gson = new Gson();
            String json = gson.toJson(this);
            return gson.fromJson(json,this.getClass());
    
        }
    }
    View Code
    package com.sxd.test2;
    
    import org.junit.Test;
    
    import java.io.IOException;
    
    public class DataTest {
    
        @Test
        public void test() throws IOException, ClassNotFoundException {
            Student s1 = new Student(1,"小明",new Teacher(1,"张老师"));
            Student s2 = (Student) s1.deepClone2();
            System.out.println(s1 ==s2);
            System.out.println(s1.getName()+"《》《》《》"+s2.getName());
            s2.setName("小张");
            System.out.println(s1.getName()+"《》《》《》"+s2.getName());
            System.out.println(s1.getTeacher() +"《》《》《》"+ s2.getTeacher());
            Teacher teacher = s1.getTeacher();
            teacher.setName("王老师");
            System.out.println(s1.getTeacher() +"《》《》《》"+ s2.getTeacher());
    
        }
    }
    View Code

    ====================================================================

    4.final、finally、finalize()的区别

      1》final 修饰符 类是终极类,不能被继承/方法是最终方法,不能被重写/属性是常量,不能被改变

      2》finally 异常处理清除块,无论如何都会被执行

      3》finalize()  Object的方法,java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

    ====================================================================

    5.getClass()反射机制

      5.1》

        1)a.getClass()获得对象运行时类型类,是运行时

        2)A.class获取一个类的类型类,是编译时就决定了的

    代码:

    package com.sxd.test2;
    
    import org.junit.Test;
    
    public class DataTest {
    
        class A{
    
        }
    
        class B extends  A{
    
        }
    
        @Test
        public void test() {
            A a = new A();
            B b = new B();
            A ab = new B();
    
            System.out.println(a.getClass()+"   "+A.class);
            System.out.println(b.getClass()+"   "+B.class);
            System.out.println(ab.getClass());
            ab = a;
            System.out.println(ab.getClass());
        }
    }
    View Code

      5.2》Class.forName()和ClassLoader.loadClass区别【参考:https://www.cnblogs.com/zabulon/p/5826610.html

    Class.forName(className)方法,内部实际调用的方法是  Class.forName(className,true,classloader);
    
    第2个boolean参数表示类是否需要初始化,  Class.forName(className)默认是需要初始化。
    
    一旦初始化,就会触发目标对象的 static块代码执行,static参数也也会被再次初始化。
    
        
    
    ClassLoader.loadClass(className)方法,内部实际调用的方法是  ClassLoader.loadClass(className,false);
    
    第2个 boolean参数,表示目标对象是否进行链接,false表示不进行链接,由上面介绍可以,
    
    不进行链接意味着不进行包括初始化等一些列步骤,那么静态块和静态对象就不会得到执行

      5.3》ClassLoad java类加载器

      参考:https://blog.csdn.net/slim68/article/details/70849101

      http://www.cnblogs.com/sxdcgaq8080/p/8834248.html

            第一,java默认提供的3个ClassLoader以及加载顺序,分别的作用

    类的生命周期,就是从类的加载到类实例的创建与使用,再到类对象不再被使用时可以被GC卸载回收。这里要注意一点,由java虚拟机自带的三种类加载器加载的类在虚拟机的整个生命周期中是不会被卸载的,只有用户自定义的类加载器所加载的类才可以被卸载。

            第二,双亲委派模型是什么,

    每一个ClassLoader都有一个父类加载器,是包含关系,不是继承关系!当一个ClassLoader需要加载一类时,会先交给BootstrapClassLoader试图加载,如果没加载到,则交给扩展加载类,下来是App加载类,一层一层,父级向下,如果所有父级都没加载到,则返回给自己进行加载,如果依旧没有,则报ClassNotFoundException.

    特别强调:双亲,不是说真的父亲母亲的双亲,而是parents,父级一级一级向下顺序,加载class的过程!

            第三,使用双亲委派模型的好处,

      一句话概述:使用双亲委派模型,就是为了防止内存中出现多份相同的字节码!

       简要描述:如果没有双亲委派模型,那一个类可以由多个ClassLoader加载到内存中,内存中出现多个不同的这个类。那怎么判断这些类是同一个类呢?通过类名?显然不行,所以不采用双亲委派模型,那么类在内存中的唯一性,将无法保证!

            第四,JVM搜索类时,如何判断两个class是相同的? 

        a.类名相同;b.是由同一个类加载器加载

        否则,相互转化,会报ClassCaseException

     ===================================================================

    6.重写equals()方法就一定要重写hashCode()方法,为什么

       1》Object中hashCode()是本地方法,返回的就是引用地址,equals()是比较两个对象的引用地址是否相等。所以在object中两个是一致的

       2》自定义对象,对象的某个属性相等就认定两个对象相等。这个是你重写equals()方法实现的,但是如果不重写hashCode()方法,显然,两个对象的hashCode是不一致的,违反了java中对【两个对象equals比较相等,那他们的hashCode比较也一定相等的原则】。

       3》在使用上,例如:a.equals(b)但是a.hashCode != b.hashCode。那么hashMap.put(a,1),hashMap.get(b) ==1 却不一定相等。因为在hashMap中进行put和get操作都是要根据对象a和b的hashCode值进行定位操作,但是a.hashCode != b.hashCode 所以会出现这样的问题。

      所以重写equals方法就尽量要重写hashCode方法!!

     ===================================================================

    7.toString()方法在String中是否被重写了【了解】

      1》String中的toString()方法就是 return this;

      2》Object中的toString()方法是返回运行时类型类名+hashCode码

     ===================================================================

    8.wait() notify() notifyAll()方法等多线程详见第九篇

     http://www.cnblogs.com/sxdcgaq8080/p/8549799.html

     ===================================================================

    9.jdk8有哪些新特性

    参考地址:http://www.runoob.com/java/java8-new-features.html

      1》Lambda表达式

    Lambda表达式,允许函数作为方法的参数传递

    代码例如:【将函数作为参数传入方法,优雅】

    package com.sxd.test2;
    
    public interface Operator {
        int operation(int a,int b);
    }
    View Code
    package com.sxd.test2;
    
    public class OperatorImpl {
        static Operator add = (int a,int b) ->a+b;
        static Operator less = (a,b) -> a-b;
        static Operator multiply = (int a,int b) ->{return  a*b;};
        static Operator division = (int a, int b) -> a/b;
    }
    View Code
    package com.sxd.test2;
    
    import org.junit.Test;
    
    
    public class DataTest {
    
        private int operate(int a,int b,Operator op){
            return  op.operation(a,b);
        }
    
        @Test
        public void test() {
            System.out.println(operate(3,2,OperatorImpl.add));
            System.out.println(operate(3,2,OperatorImpl.less));
            System.out.println(operate(3,2,OperatorImpl.multiply));
            System.out.println(operate(3,2,OperatorImpl.division));
    
        }
    }
    View Code

      2》Stream--集合的高级迭代器,可以串行或并行对集合进行各种操作和汇聚,得到一个新的结果。http://www.cnblogs.com/sxdcgaq8080/p/7383215.html

      3》时间日期API http://www.cnblogs.com/sxdcgaq8080/p/7193274.html

      4》在Java 8中,Base64编码已经成为Java类库的标准,Base64工具类提供了一套静态方法。http://www.runoob.com/java/java8-base64.html

    ===================================================================

  • 相关阅读:
    Spring Boot源码分析-配置文件加载原理
    Spring Boot源码分析-启动过程
    Spring Cloud Alibaba基础教程:Nacos服务发现与配置管理
    JVM(九):垃圾回收算法
    JVM(八):Java 对象模型
    JVM(七):JVM内存结构
    JVM(六):探究类加载过程-下
    JVM(五):探究类加载过程-上
    JVM(四):深入分析Java字节码-下
    JVM(三):深入分析Java字节码-上
  • 原文地址:https://www.cnblogs.com/sxdcgaq8080/p/8548699.html
Copyright © 2020-2023  润新知