8月份21道最新Java面试题剖析(数据库+JVM+微服务+高并发)
发布日期:2021-05-08 01:45:38 浏览次数:22 分类:精选文章

本文共 4399 字,大约阅读时间需要 14 分钟。

Java 面试备考与开发分享

Java 面试备考

纵观几年来的Java面试题,你会发现每家都差不多。你仔细观察就会发现,HashMap的出现几率未免也太高了吧!连考察的知识点都一样,什么hash碰撞啊,并发问题啊!再比如JVM,无外乎考内存结构,GC算法等!因此,如果是为了面试,完全是有套路可以准备的!记住,基础再好,也架不住面试官天马行空的问,所以刷面试题还是很有必要的!

HashMap 的底层结构

Map是以键值对来存储对象的,它的底层实际上是数组和链表来组成的。经典的一张图如下:

当使用put方法时,先查找出数组位置是否存在对象,通过key.hashcode对数组长度取余;存在,则把里面的链表拿出来,判断链表里面是否存在key值与传递过来的key值一样的对象,存在,则把传递过来的value取代链表key对应的value,不存在,则直接通过链表的add()方法加到链表后面;

当使用get方法时,先查找出数组位置是否存在对象,通过key.hashcode对数组长度取余;如果不存在,则返回为空,如果存在,则遍历链表,判断链表里面是否存在key值与传递过来的key值一样的对象,存在,则把key值对应的value取出返回,不存在,则返回为空;

线程安全的 Map (ConcurrentHashMap)

简单的说了下这两 1.7 和 1.8的区别,本想问下要不要深入的讲下(源码级别),结果面试官说不用了。

回答:

jdk1.7中采用 Segment + HashEntry的方式进行实现,结构如下:

Segment数组的意义就是将一个大的table分割成多个小的table来进行加锁,而每一个Segment元素存储的是HashEntry数组+链表,这个和HashMap的数据存储结构一样。

而jdk1.8中则去除 Segment + HashEntry + Unsafe 的实现,改为 Synchronized + CAS + Node + Unsafe 的实现,结构图如下:

如上图所示,取消了Segment字段,数组中存储的就是Node。它与HashMap中的HashEntry定义很相似,但是有一些差别。它对value和next属性设置了volatile同步锁,它不允许调用setValue方法直接改变Node的value域。

另外,将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构,在hash碰撞过多的情况下会将链表转化成红黑树。

项目 MySQL 的数据量和并发量

关于容量:单表行数超过 500 万行或者单表容量超过2GB,此时就要答分库分表的中间件了!那后面题目的走向就变为mycat、sharing-jdbc等分库分表中间件的底层原理了!

关于并发量:如果并发数过1200,此时就要答利用MQ或者redis等中间件,作为补偿措施,而不能直接操作数据库。那后面的题目走向就是redis、mq的原理了!

回答:

数据量估计就三四百万吧,并发量就五六百左右!

数据库了解多少

回答:

了解常见数据库调优方法,索引优化等!

数据库索引

从数据结构角度:

B-Tree索引,数据结构就是一颗B+树。

Hash索引,Hash索引比较的是进行Hash运算之后的Hash值,所以它只能用于等值的过滤,不能用于基于范围的过滤。基本不用!

R-Tree索引,仅支持geometry数据类型,也基本不用!

至于非主键的二级索引,这个实际上问的就是非聚簇索引!非聚簇索引本身就是一颗B+树,其根节点指向聚簇索引的B+树。

Spring Boot 与 Spring 的区别

回答:

Spring Boot可以建立独立的Spring应用程序;

内嵌了如Tomcat,Jetty和Undertow这样的容器,也就是说可以直接跑起来,用不着再做部署工作了;

无需再像Spring那样搞一堆繁琐的xml文件的配置;

可以自动配置Spring;

提供了一些现有的功能,如量度工具,表单数据验证以及一些外部配置这样的一些第三方功能;

提供的POM可以简化Maven的配置。

SpringBoot 自动配置

回答:

先答为什么需要自动配置?

顾名思义,自动配置的意义是利用这种模式代替了配置 XML 繁琐模式。以前使用 Spring MVC ,需要进行配置组件扫描、调度器、视图解析器等,使用 Spring Boot 自动配置后,只需要添加 MVC 组件即可自动配置所需要的 Bean。所有自动配置的实现都在 spring-boot-autoconfigure 依赖中,包括 Spring MVC 、Data 和其它框架的自动配置。

接着答spring-boot-autoconfigure 依赖的工作原理?

spring-boot-autoconfigure 依赖的工作原理很简单,通过 @EnableAutoConfiguration 核心注解初始化,并扫描 ClassPath 目录中自动配置类对应依赖。比如工程中有木有添加 Thymeleaf 的 Starter 组件依赖。如果有,就按按一定规则获取默认配置并自动初始化所需要的 Bean。

MyBatis 定义的接口,怎么找到实现的?

回答:

一共五步:

  • Mapper 接口在初始SqlSessionFactory 注册的;

  • Mapper 接口注册在了名为 MapperRegistry 类的 HashMap中, key = Mapper class value = 创建当前Mapper的工厂;

  • Mapper 注册之后,可以从SqlSession中get;

  • SqlSession.getMapper 运用了 JDK动态代理,产生了目标Mapper接口的代理对象;

  • 动态代理的 代理类是 MapperProxy ,这里边最终完成了增删改查方法的调用。

  • Java 内存结构

    回答:

    JVM内存结构主要有三大块:堆内存、方法区和栈。堆内存是JVM中最大的一块由年轻代和老年代组成,而年轻代内存又被分成三部分,Eden空间、From Survivor空间、To Survivor空间,默认情况下年轻代按照8:1:1的比例来分配;

    方法区存储类信息、常量、静态变量等数据,是线程共享的区域,为与Java堆区分,方法区还有一个别名Non-Heap(非堆);栈又分为java虚拟机栈和本地方法栈主要用于方法的执行。

    对象是否可 GC?

    回答:

    这个算法的基本思路是通过一些列称为“GC Roots”的对象作为起始点,从这些点开始向下搜索,搜索走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明对象需要被回收。

    如图:

    上图中o3,o4对象没有任何GC Roots可达到,所有这两个对象不可用了,需要被GC回收。

    Java可作为GC Roots的对象包括下面几种:

    虚拟机栈中引用的对象

    方法区中类静态属性引用的对象
    方法区中产量引用的对象
    本地方法栈中JNI引用的对象

    Minor GC 和 Full GC

    回答:

    堆内存是JVM中最大的一块由年轻代和老年代组成。那么,从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC。

    Major GC 是清理老年代。

    Full GC 是清理整个堆空间—包括年轻代和老年代。

    垃圾回收算法

    回答:

    标记-清除算法、标记整理算法、复制算法、分代收集算法。

    垃圾回收器 G1

    回答:

    G1 GC是Jdk7的新特性之一、Jdk7+版本都可以自主配置G1作为JVM GC选项。G1 将整个堆划分为一个个大小相等的小块(每一块称为一个region),每一块的内存是连续的,每个块也会充当 Eden、Survivor、Old三种角色,但是它们不是固定的,这使得内存使用更加地灵活。

    执行垃圾收集时,收集线程在标记阶段和应用程序线程并发执行,标记结束后,G1 也就知道哪些区块基本上是垃圾,存活对象极少,G1 会先从这些区块下手,因为从这些区块能很快释放得到很大的可用空间,这也是为什么 G1 被取名为 Garbage-First 的原因。

    ElasticSearch 和 Hbase

    回答:

    并没有深入了解过!

    Spring RestTemplate 的具体实现

    回答:

    其实RestTemplate和sl4fj这种门面框架很像,本质就是在Http的网络请求中增加一个马甲,本身并没有自己的实现。

    底层可以支持多种httpclient的http访问,上层为ClientHttpRequestFactory接口类,底层如下所示:

    RestTemplate则封装了组装、发送 HTTP消息,以及解析响应的底层细节。

    Http 请求过程

    回答:

    利用DNS进行域名解析
    发起TCP的3次握手
    建立TCP连接后发起http请求
    服务器响应http请求,浏览器得到html代码
    浏览器解析html代码,并请求html代码中的资源(如js、css、图片等)
    浏览器对页面进行渲染呈现给用户

    多线程的常用方法和接口类及线程池的机制

    回答:

    常用方法:
    start,run,sleep,wait,notify,notifyAll,join,isAlive,currentThread,interrupt

    常用接口类:

    Runnable、Callable、Future、FutureTask

    线程池的机制:

    在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,所以出现了池化技术!

    简单的线程池包括如下四个组成部分即可:

    线程池管理器(ThreadPoolManager):用于创建并管理线程池

    工作线程(WorkThread): 线程池中线程
    任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行
    任务队列:用于存放没有处理的任务。提供一种缓冲机制

    Java 基础与框架

    总结我的 Java 基础还是不错,但是一些主流的框架源码还是处在使用的状态,需要继续去看源码

    死锁

    回答:

    死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,如果系统资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。

    产生死锁的原因主要是:

    (1) 因为系统资源不足。

    (2) 进程运行推进的顺序不合适。
    (3) 资源分配不当等。

    技术研究成果

    自己研究比较新的技术,说下成果!

    公司情况

    你有什么想问的?

    结语

    欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!

    上一篇:3年Java开发都知道的Redis数据结构和通用命令
    下一篇:35个Java代码优化的细节,你知道几个?

    发表评论

    最新留言

    做的很好,不错不错
    [***.243.131.199]2025年04月04日 18时44分38秒