美文网首页互联网科技
2020最新美团社招Java岗面试分享——Spring全家桶+J

2020最新美团社招Java岗面试分享——Spring全家桶+J

作者: 风平浪静如码 | 来源:发表于2020-08-04 16:19 被阅读0次

    一. Spring

    1、谈谈你对Spring的理解

    关键点

    • 企业框架,目前最流行,没有之一
    • AOP、IOC、Spring MVC

    2、Spring中用到了哪些设计模式

    • 工厂模式,比如 BeanFactory
    • 代理模式,在Aop实现中用到了JDK的动态代理
    • 单例模式,Bean的创建默认就是单利的

    3、IoC的启动过程

    • Resource文件的定位,即找到bean的配置文件
    • 通过特定的reader解析该bean配置文件,抽象成beanDefinition类
    • 将beanDefinition向容器注册,写入到一个大的HashMap中

    4、BeanFactory 和 ApplicationContext 区别

    • 功能,BeanFactory负责读取bean配置,管理bean的加载,实例化,维护bean之间的依赖关系,负责bean的声明周期;ApplicationContext除了提供上述BeanFactory所能提供的功能之外,还提供了国际化支持、资源访问、事件传递、队Web的支持等功能
    • BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化;而ApplicationContext则相反,它是在容器启动时,一次性创建了所有的Bean。
    • BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册

    5、Bean 的生命周期

    • 实例化一个Bean,也就是我们通常说的new
    • 按照Spring上下文对实例化的Bean进行配置,也就是IOC注入
    • 如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的是Spring配置文件中Bean的ID
    • 如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(),传递的是Spring工厂本身(可以用这个方法获取到其他Bean)
    • 如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文,该方式同样可以实现步骤4,但比4更好,以为ApplicationContext是BeanFactory的子接口,有更多的实现方法
    • 如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用After方法,也可用于内存或缓存技术
    • 如果这个Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法
    • 如果这个Bean关联了BeanPostProcessor接口,将会调用postAfterInitialization(Object obj, String s)方法 注意:以上工作完成以后就可以用这个Bean了,那这个Bean是一个single的,所以一般情况下我们调用同一个ID的Bean会是在内容地址相同的实例
    • 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destroy方法
    • 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法

    6、Bean的作用域

    • singleton(默认): 在Spring的IoC容器中只存在一个对象实例,所有该对象的引用都共享这个实例。Spring 容器只会创建该bean定义的唯一实例,这个实例会被保存到缓存中,并且对该bean的所有后续请求和引用都将返回该缓存中的对象实例,一般情况下,无状态的bean使用该scope。
    • prototype:每次对该bean的请求都会创建一个新的实例,一般情况下,有状态的bean使用该scope。
    • request:每次http请求将会有各自的bean实例,类似于prototype。
    • session:在一个http session中,一个bean定义对应一个bean实例。
    • global session:在一个全局的http session中,一个bean定义对应一个bean实例。典型情况下,仅在使用portlet context的时候有效。

    7、AOP

    实现原理:默认,接口基于JDK动态代理,类为cglib

    注意事项

    • 嵌套失效问题
    • final类直接报错问题(动态代理的实现原理)

    8、Spring MVC

    请求过程

    • 用户请求DispatchServlet
    • DispatchServlet根据请求路径调用具体HandlerMapping返回一个HandlerExcutionChain
    • DispatchServlet调用HandlerAdapter适配器
    • HandlerAdapter调用具体的Handler处理业务
    • Handler处理结束返回一个具体的ModelAndView给适配器
    • 适配器将ModelAndView给DispatchServlet
    • DispatchServlet把视图名称给ViewResolver视图解析器
    • ViewResolver返回一个具体的视图给DispatchServlet
    • 渲染视图,展示给用户

    二. JVM

    1、内存划分

    JVM规范,将内存分为 程序计数器、Java栈,也叫虚拟机栈、本地方法栈、方法区、堆

    • 程序计数器 程序指令保存,从当前指令到下一个指令,从程序计数器获取下一个指令的地址,直到执行所有的指令;线程私有
    • Java栈(虚拟机栈) 保存方法栈帧,当调用一个方法,则新创建一个栈帧,当前的方法始终保持在栈帧的顶部;线程私有
    • 本地方法栈 保存本地方法栈帧,当调用一个本地方法,则新创建一个栈帧,当前的方法始终保持在栈帧的顶部;线程私有
    • 方法区 保存类信息、静态常量、常量;线程共享
    • 堆 保存对象,最最主要的垃圾收集之处;线程共享

    2、对象存活

    • 引用计数算法:对对象引用进行计数,引用则计数器 +1,引用失效 -1;计数为 0 时候认为不在引用,可以进行回收;不能处理对象循环引用问题
    • 可达性分析算法:主要采取此方式,采用虚拟机栈、类引用对象、常量对象、本地方法引用对象作为根,判断对象到根是否存在引用关系,不可达则认为不在引用,可以进行回收;

    3、GC回收算法

    • 标记-清除算法:对要回收的对象,先进行标志,后进行清除,你懂得;但是,久之,会存在内存不连续,比如,一次垃圾回收,回收了,(0,1)和(0,3)和(0,5)三个位置,但是没有回收(0,2)和(0,4),那下次的内存,就无法使用(0,1)-(0,5)的连续空间;
    • 复制算法:为改进上面的内存碎片问题而产生,对内存分为两部分,交替回收其中一部分,存活的对象复制到另一部分空间,你懂得;但是,有点浪费,比如,内存分为,(0,1)-(0,3)和(0,3)-(0,5)两个部分,某次回收(0,1)-(0,3)空间,将存活对象拷贝到(0,3)-(0,5),而后在(0,1)-(0,3)分配对象;
    • 标记-整理算法:为改进上面的内存浪费问题而产生,对要回收的对象,先进行标志,后进行清除,你懂得,然后,将存活的对象,进行整理,移动到边界位置,似的剩余空间连续;比如,一次垃圾回收,回收了,(0,1)和(0,3)和(0,5)三个位置,但是没有回收(0,2)和(0,4),然后,将(0,2)和(0,4)移动到(0,1)和(0,2),使得(0,3)-(0,5)的空间连续;

    4、类加载过程

    jvm中class类的加载过程,大致分为这几个步骤

    • 加载(load)
    • 根据全类名,加载类的二进制字节流
    • 将字节流转存方法区
    • 生成Class对象作为访问入口
    • 验证(verify)
    • class文件的格式验证,验证是否符合JVM规范
    • class中的元数据验证,验证是否符合Java规范
    • class的字节码验证,验证数据流控制流不会危害JVM环境
    • 准备(prepare)
    • 给变量分配内存
    • 初始化零值(比如int默认为0,boolean默认为false)
    • final变量直接赋值
    • 解析
    • 符号引用变为直接引用
    • 类、字段、方法、接口方法解析
    • 初始化
    • 初始化变量
    • 构造函数
    • static块

    5、双亲委派机制

    Java中,大概有三种类型加载器,启动类加载器(Bootstrap)<- 标准扩展类加载器(Extension)<- 应用程序类加载器(Application )<- 上下文类加载器(Custom),从右到左,尽量父类进行加载,当父类无法进行加载时候,才会使用子类进行加载

    • 意义
    • 防止同一个JVM,内存中出现两份class二进制字节码
    • 加载过程
    • 从已加载的类查找是否已经存在,存在不需要再次加载
    • 若不存在,则去parent中查找,存在不需要再次加载
    • 若不存在,递归在parent中查找,直到找到为止
    • 若找遍所有parent均不存在,且当前加载器已经没有parent加载器,则调用当前类加载器的findClass方法,如果能加载,结束
    • 如果不能,则递归返回child类加载器,继续调用findClass方法,如果能加载,结束
    • 如果找遍所有child的findClass方法,还是不能加载,则抛出异常
    • 破坏双亲委派机制
    • 将parent设为null
    • 重写load(String,boolean)方法,改变类的查找机制。

    三. 多线程

    1、死锁的四个条件

    • 互斥
    • 请求与保持
    • 不剥夺
    • 循环等待

    2、检查死锁

    • Jconsole查看死锁
    • Jstack查看死锁

    3、volatile

    • JMM
    • 内存可见性
    • 防止指令重排序
    • 不能保证原子性

    4、synchronized

    作用

    • 互斥访问
    • 内存可见性
    • 防止指令重排序

    用法

    • 修饰普通方法
    • 修饰静态方法
    • 修饰代码块

    注意点

    • 当一个线程在访问对象的 synchronized 方法,因为对象只有一把锁,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized实例方法,但是其他线程还是可以访问该实例对象的其他非synchronized方法
    • 实现原理为,对象监视器,Monitor

    5、volatile vs synchronized vs lock

    • 来源差异:volatile、synchronized为Java关键字; lock是Java类
    • 代价开销:volatile不是锁,代价最小; lock是一般基于AQS,相对比synchronized代价小; synchronized代价最大
    • 简单性:volatile、synchronized为Java关键字,JVM全权帮忙维护,只要我们能正确使用,不需要我们太多关心维护; lock是Java类,有很多方法可以调用,灵活性最好,但是需要自己控制锁的获取、释放

    6、进程间通信

    • 管道pipe
    • 命名管道FIFO
    • 消息队列MessageQueue
    • 共享存储SharedMemory
    • 信号量Semaphore
    • 套接字Socket
    • 信号 ( sinal )

    7、原子操作类

    • CountDownLatch

      • 某线程需要等待多个线程执行完毕,再执行。
      • 实现多个线程共同等待,同时开始执行任务,不可重用。(此类似于CyclicBarrier,可重用)
    • CyclicBarrier

      • CyclicBarrier允许一组线程互相等待,直到到达某个公共屏障点。
      • 与CountDownLatch不同的是该barrier在释放等待线程后可以重用,所以称它为循环(Cyclic)的屏障
    • CountDownLatch、CyclicBarrier差别

      • CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同: CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行; 而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
      • CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。
    • Semaphore

      • Semaphore是一个计数信号量,它的本质是一个”共享锁”。
      • 信号量维护了一个信号量许可集。线程可以通过调用acquire()来获取信号量的许可;当信号量中有可用的许可时,线程能获取该许可;否则线程必须等待,直到有可用的许可为止。 线程可以通过release()来释放它所持有的信号量许可。
      • Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。
    • Exchanger

      • 用于进行线程间的数据交换。
      • 两个线程通过exchange方法交换数据
      • 该工具类的线程对象是成对的
    • ThreadLocal

      • 每个Thread 维护一个 ThreadLocalMap 映射表,这个映射表的 key 是 ThreadLocal实例本身,value 是真正需要存储的 Object。
      • ThreadLocal 本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap 获取 value。值得注意的是图中的虚线,表示 ThreadLocalMap 是使用 ThreadLocal 的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。
      • ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
      • ThreadLocal里面使用了一个存在弱引用的map, map的类型是ThreadLocal.ThreadLocalMap. Map中的key为一个threadlocal实例。这个Map的确使用了弱引用,不过弱引用只是针对key。每个key都弱引用指向threadlocal。 当把threadlocal实例置为null以后,没有任何强引用指向threadlocal实例,所以threadlocal将会被gc回收。 但是,我们的value却不能回收,而这块value永远不会被访问到了,所以存在着内存泄露。因为存在一条从current thread连接过来的强引用。只有当前thread结束以后,current thread就不会存在栈中,强引用断开,Current Thread、Map value将全部被GC回收。最好的做法是将调用threadlocal的remove方法,这也是等会后边要说的。
      • 使用static的ThreadLocal,延长了ThreadLocal的生命周期,可能导致内存泄漏。
      • 分配使用了ThreadLocal又不再调用get(),set(),remove()方法,那么就会导致内存泄漏,因为这块内存一直存在。

    写在最后

    限于篇幅,本文只收录Spring、JVM、多线程的部分面试题;完整的面试题包含Kafka、Mysql、Tomcat、Docker、Spring、MyBatis、Nginx、Netty、Dubbo、Redis、Netty、Spring cloud、分布式、高并发、性能调优、微服务等架构技。笔者已经整理打包好了!

    需要的朋友点击石墨文档, 即可免费领取面试资料和视频学习资料

    以下是部分面试题截图

    相关文章

      网友评论

        本文标题:2020最新美团社招Java岗面试分享——Spring全家桶+J

        本文链接:https://www.haomeiwen.com/subject/ovnrrktx.html