Java 后端岗位面试真题-灵析社区

菜鸟码转

题组I

1. SpingBoot 也有定时任务?是什么注解?(百度)

在 SpringBoot 中使用定时任务主要有两种不同的方式,一个就是使用 Spring 中的 @Scheduled 注解,另一个则是使用第三方框架 Quartz。

使用 Spring 中的 @Scheduled 的方式主要通过 @Scheduled 注解来实现。

使用 Quartz ,则按照 Quartz 的方式,定义 Job 和 Trigger 即可。

2. 什么情况线程会进入 WAITING 状态?(百度)

一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个 状态后不能自动唤醒,必须等待另一个线程调用notify方法或者notifyAll方法时才能够被唤醒。

• 调用Object对象的wait方法,但没有指定超时值。

• 调用Thread对象的join方法,但没有指定超时值。

• 调用LockSupport对象的park方法。

3. 简述多进程开发中 join 和 daemon 的区别?

join:当子线程调用join时,主线程会被阻塞,当子线程结束后,主线程才能继续执行。 daemon:当子进程被设置为守护进程时,主进程结束,不管子进程是否执行完毕,都会随着 主进程的结束而结束。

4. 异步和同步、阻塞和非阻塞之间的区别?(百度)

同步

当一个request发送出去以后,会得到一个response,这整个过程就是一个同步调用的过 程。哪怕response为空,或者response的返回特别快,但是针对这一次请求而言就是一 个同步的调用。

异步

当一个request发送出去以后,没有得到想要的response,而是通过后面的callbacks状 态或者通知的方式获得结果。可以这么理解,对于异步请求分两步:

• 调用方发送request没有返回对应的response (可能是一个空的response);

• 服务提供方将response处理完成以后通过callback的方式通知调用方。

对于1 )而言是同步操作(调用方请求服务方),对于2)而言也是同步操作(服务方回掉 调用方)。从请求的目的(调用方发送一个request,希望获得对应的response)来看,这 两个步骤拆分开来没有任何意义,需要结合起来看,而这整个过程就是一次异步请求。异步请 求有一个最典型的特点:需要callback、状态或者通知的方式来告知调用方结果。

阻塞 阻塞调用是指调用方发出request的线程因为某种原因(如:等待系统资源)被服务方挂起, 当服务方得到response后就唤醒挂起线程,并将response返回给调用方。

非阻塞

非阻塞调用是指调用方发出request的线程在没有等到结果时不会被挂起,并且直到得到 response 后才返回。 阻塞和非阻塞最大的区别就是看调用方线程是否会被挂起。

5. 为什么要分内核态和用户态?(百度)

假设没有这种内核态和用户态之分,程序随随便便就能访问硬件资源,比如说分配内存,程序 能随意的读写所有的内存空间,如果程序员一不小心将不适当的内容写到了不该写的地方,就 很可能导致系统崩溃。用户程序是不可信的,不管程序员是有意的还是无意的,都很容易将系 统干到崩溃。

正因为如此,Intel就发明了 ring0-ring3这些访问控制级别来保护硬件资源,ring。的就是 我们所说的内核级别,要想使用硬件资源就必须获取相应的权限(设置PSW寄存器,这个操 作只能由操作系统设置)。操作系统对内核级别的指令进行封装,统一管理硬件资源,然后向 用户程序提供系统服务,用户程序进行系统调用后,操作系统执行一系列的检查验证,确保这 次调用是安全的,再进行相应的资源访问操作。**内核态能有效保护硬件资源的安全。

6. Java常用集合及特点?(华为)

List: ArrayList、LinkedList、Vector、Stack Set: LinkedSet、HashSet、TreeSet Queue->Deque->LinkedListoMap: HashMap、LinkedHashMap、TreeMap Dictionary->HashTable->Properties。Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,默认长度为10,超 过会100%延长,变成20,浪费空间。ArrayList :基于数组,便于按index访问,超过数组需要扩容,扩容成本较高。LinkedList:使用链表实现,无需扩容。HashSet:底层数据结构是哈希表(无序,唯一),通过hashcode()和equals()保证元素 唯一。LinkedHashSet:底层数据结构是链表和哈希表(FIFO插入有序,唯一),由链表保证元 素有序,由哈希表保证元素唯一。TreeSet:底层数据结构是红黑树(唯一,有序),通过自然排序和比较器排序保证元素有序, 根据比较返回值是否是0来保证元素唯一性。TreeMap是有序的。HashMap :空间换时间,哈希冲突不大的情况下查找数据性能很高。LinkedHashMap基本特点:继承自HashMap,对Entry集合添加了一个双向链表。

7. 介绍 Spring MVC 的工作流程?(华为)

用户向服务端发送一次请求,这个请求会先到前端控制器DispatcherServlet。DispatcherServlet接收到请求后会调用HandlerMapping处理器映射器。由此得知, 该请求该由哪个Controller来处理(并未调用Controller,只是得知)DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器适配器应该要去 执行哪个ControllerHandlerAdapter处理器适配器去执行Controller并得到ModelAndView(数据和视图), 并层层返回给DispatcherServletDispatcherServlet 将 ModelAndView 交给 ViewReslover 视图解析器解析,然后返 回真正的视图。DispatcherServlet将模型数据填充到视图中DispatcherServlet将结果响应给用

8. Redis 的特点是什么?(华为)

Redis本质上是一个Key-Value类型的内存数据库,很像Memcached,整个数据库统统 加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。 因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过10万次读写操作,是已知 性能最快的Key-Value DB。Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个 value的最大限制是1GB,不像Memcached只能保存1MB的数据,因此Redis可以 用来实现很多有用的功能。比方说用他的List来做FIFO双向链表,实现一个轻量级的高性能消息队列服务,用他的 Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire 时间,因此也可以被当作一个功能加强版的Memcached来用。Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因 此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。

9. Redis 雪崩和击穿了解吗?(华为)

缓存击穿问题:某个KEY失效的时候,正好有大量并发请求访问这个KEY。分析:跟穿透其实很像,属于比较偶然的。解决办法:KEY的更新操作添加全局互斥锁。完全以缓存为准,使用延迟异步加载的策 略(异步线程负责维护缓存的数据,定期或根据条件触发更新),这样就不会触发更新。缓存雪崩

• 问题:当某一时刻发生大规模的缓存失效的情况,导致大量的请求无法获取数据,从而将 流量压力传导到数据库上,导致数据库压力过大甚至宕机。原因:一般而言,缓存雪崩有2种可能性:大量的数据同一个时间失效:比如业务关系 强相关的数据要求同时失效Redis宕机分析:一般来说,由于更新策略、或者数据热点、缓存服务宕机等原因,可能会导致缓存 数据同一个时间点大规模不可用,或者都更新。所以,需要我们的更新策略要在时间上合 适,数据要均匀分享,缓存服务器要多台高可用。

• 解决办法:更新策略在时间上做到比较平均。如果数据需要同一时间失效,可以给这批数 据加上一些随机值,使得这批数据不要在同一个时间过期,降低数据库的压力。使用的热 数据尽量分散到不同的机器上。多台机器做主从复制或者多副本,实现高可用。做好主从 的部署,当主节点挂掉后,能快速的使用从结点顶上。实现熔断限流机制,对系统进行负 载能力控制。对于非核心功能的业务,拒绝其请求,只允许核心功能业务访问数据库获取 数据。服务降价:提供默认返回值,或简单的提示信息。

10. 什么是面向对象,谈谈你的理解?(华为)

世间万物都可以看成一个对象。每个物体包括动态的行为和静态的属性,这些就构成了一个对象。

11. 访问数据库除了 JDBC 还有什么?(华为)

自己封装JDBC的工具类Commons-Dbutils+dbcp【QueryRunner】SpringJDBC [JdbcTemplate]JPA【配置文件、domain实体类+注解、EntityManager]SpringDataJpaHibernate 框架Mybatis

题组II

1. GC root 有哪些?(华为)

Thread-存活的线程。Java虚拟机栈中的引用的对象。 • 方法区中的类静态属性引用的对象。(一般指被static修饰的对象,加载类的时候就加 载到内存中。)

• 方法区中的常量引用的对象。本地方法栈中的JNI( native方法)引用的对象。Monitor Used-用于同步监控的对象。

2. 传统 I/O 跟 NIO 的区别?(华为)

所有I/O都被视为单个的字节的移动,通过一个称为Stream的对象一次移动一个字节。 流1/。用于与外部世界接触。它也在内部使用,用于将对象转换为字节,然后再转换回对 象。传统流IO的好处是使用简单,将底层的机制都抽象成流,但缺点就是性能不足。而 且IO的各种流是阻塞的。这意味着,当一个线程调用read()或write()时,该线程被阻 塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。原来的I/O库([在java.io](http://xn--java-ft4g.io/).*中)与NIO最重要的区别是数据打包和传输的方式。原来的 I/O以流的方式处理数据,而NIO以块的方式处理数据。NIO性能的优势就来源于缓冲的机制(buffer机制),不管是读或者写都需要以块的形 式写入到缓冲区中。NIO实际上让我们对IO的操作更接近于操作系统的实际过程。NIO作为非阻塞式的IO,它的优点就在于

1、它由一个专门的线程去处理所有的IO 事件,并负责分发;

2、事件驱动,只有事件到了才会触发,而不是同步的监听这个事件;

3、线程之间通过wait,notify等方式通讯。保证每次上下文切换都是有意义的。减少无谓 的线程切换。

• 当我们在执行持续性的操作(如上传下载)时,IO的方式是要优于NIO的。分清情况, 合理选用。NIO相对于IO流的优势:非阻塞buffer机制流替代块

3. KAFKA 是什么?主要应用场景有哪些?(腾讯)

Kafka是一个分布式流式处理平台。这到底是什么意思呢?

流平台具有三个关键功能:

• 消息队列:发布和订阅消息流,这个功能类似于消息队列,这也是Kafka也被归类为消 息队列的原因。

• 容错的持久方式存储记录消息流:Kafka会把消息持久化到磁盘,有效避免了消息丢失 的风险。

• 流式处理平台:在消息发布的时候进行处理,Kafka提供了一个完整的流式处理类库。 Kafka主要有两大应用场景:

•消息队列:建立实时流数据管道,以可靠地在系统或应用程序之间获取数据。

•数据处理:构建实时的流数据处理程序来转换或处理数据流。

4. MYSQL 索引分类?(腾讯)

单列索引

• 普通索引:MySQL中基本索引类型,没有什么限制,允许在定义索引的列中插入重复值 和空值,纯粹为了查询数据更快一点。

•唯一索引:索引列中的值必须是唯一的,但是允许为空值,

• 主键索引:是一种特殊的唯一索引,不允许有空值。

组合索引:

多个字段组合上创建的索引,只有在查询条件中使用了这些字段的左边字段时,索引才会被使 用,使用组合索引时遵循最左前缀集合。

全文索引:

只有在MyISAM引擎上才能使用,只能在CHAR,VARCHAR,TEXT类型字段上使用全文 索引,介绍了要求,说说什么是全文索引,就是在一堆文字中,通过其中的某个关键字等,就能找到该字段所属的记录行,比如有"你是个靓仔,靓女..."通过靓仔,可能就可以找到该条记录

空间索引:

空间索引是对空间数据类型的字段建立的索引,MySQL中的空间数据类型有四种, GEOMETRY、POINT、LINESTRING、POLYGON。在创建空间索引时,使用 SPATIAL 关 键字。要求,弓I擎为MyISAM,创建空间索引的列,必须将其声明为NOT NULL。

5. 了解线程 & 进程的区别吗?(腾讯)

操作系统中可以拥有多个进程,一个进程里可以拥有多个线程,线程在进程内执行

进程和线程的区别

•容易创建新线程。创建新进程需要重复父进程

•线程可以控制同一进程的其他线程。进程无法控制兄弟进程,只能控制其子进程

•进程拥有自己的内存空间。线程使用进程的内存空间,且要和该进程的其他线程共享这个 空间;而不是在进程中给每个线程单独划分一点空间。

•(同一进程中的)线程在共享内存空间中运行,而进程在不同的内存空间中运行

• 线程可以使用wait(),notify(),notifyAll()等方法直接与其他线程(同一进程) 通信;而,进程需要使用“进程间通信”(IPC)来与操作系统中的其他进程通信。

6. 常见分布式锁的几种实现方式?(腾讯)

• 基于数据库实现分布式锁
• 基于缓存实现分布式锁
• 基于Zookeeper实现分布式锁

7. 信号量与信号的区别?(腾讯)

• 信号:(signal)是一种处理异步事件的方式。信号是比较复杂的通信方式,用于通知接 受进程有某种事件发生,除了用于进程外,还可以发送信号给进程本身。

• 信号量:(Semaphore)进程间通信处理同步互斥的机制。是在多线程环境下使用的一 种设施,它负责协调各个线程,以保证它们能够正确、合理的使用公共资源。 简单地说,信号就是一种异步通信,通知进程某种事件的发生;信号量是进程/线程同步与互 斥的一种机制,保证进程/线程间之间的有序执行或对公共资源的有序访问。

8. 场景题:1 亿个数据取出最大前 100 个有什么方法?(腾讯)

•最容易想到的方法是将数据全部排序,然后在排序后的集合中进行查找,最快的排序算法 的时间复杂度一般为O (nlogn),如快速排序。

•局部淘汰法,该方法与排序方法类似,用一个容器保存前10000个数,然后将剩余的所 有数字——与容器内的最小数字相比,如果所有后续的元素都比容器内的10000个数还 小,那么容器内这个10000个数就是最大10000个数。如果某一后续元素比容器内最 小数字大,则删掉容器内最小元素,并将该元素插入容器,最后遍历完这1亿个数,得 到的结果容器中保存的数即为最终结果了。此时的时间复杂度为O (n+mN),其中m 为容器的大小,即10000。

•分治法,将1亿个数据分成100份,每份100万个数据,找到每份数据中最大的 10000个,最后在剩下的10010000个数据里面找出最大的10000个。如果100 万数据选择足够理想,那么可以过滤掉1亿数据里面99%的数据。100万个数据里面 查找最大的10000个数据的方法如下:用快速排序的方法,将数据分为2堆,如果大 的那堆个数N大于10000个,继续对大堆快速排序一次分成2堆,如果大的那堆个 数N大于10000个,继续对大堆快速排序一次分成2堆,如果大堆个数N小于 10000个,就在小的那堆里面快速排序一次,找第10000-n大的数字;递归以上过程, 就可以找到第1w大的数。参考上面的找出第1w大数字,就可以类似的方法找到前 10000大数字了。此种方法需要每次的内存空间为10A64=4MB, —共需要101次这 样的比较。

• Hash法,如果这1亿个书里面有很多重复的数,先通过Hash法,把这1亿个数字 去重复,这样如果重复率很高的话,会减少很大的内存用量,从而缩小运算空间,然后通 过分治法或最小堆法查找最大的10000个数。

• 采用最小堆法,首先读入前10000个数来创建大小为10000的最小堆,建堆的时间复 杂度为0(mlogm)(m为数组的大小即为10000),然后遍历后续的数字,并于堆 顶(最小)数字进行比较。如果比最小的数小,则继续读取后续数字;如果比堆顶数字大, 则替换堆顶元素并重新调整堆为最小堆。整个过程直至1亿个数全部遍历完为止。然后 按照中序遍历的方式输出当前堆中的所有10000个数字。该算法的时间复杂度为O (nmlogm),空间复杂度是10000 (常数)。

9. 乐观锁和悲观锁的理解及如何实现,有哪些实现方式?(腾讯)

悲观锁:

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会 上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多 这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里 面的同步原语synchronized关键字的实现也是悲观锁。

乐观锁:

顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更 新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适 用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制, 其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是 使用了乐观锁的一种实现方式CAS实现的。

10. 谈谈你对 SQL 注入式攻击的理解?(腾讯)

所谓SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的 查询字符串,欺骗服务器执行恶意的SQL命令。

如何防范SQL注入式攻击?

在利用表单输入的内容构造SQL命令之前,把所有输入内容过滤一番就可以了。过滤输入内 容可以按多种方式进行。

• 对于动态构造SQL查询的场合

a. 替换单引号,即把所有单独出现的单引号改成两个单引号,防止攻击者修改SQL命令的含义。

b. 删除用户输入内容中的所有连字符

c. 对于用来执行查询的数据库帐户,限制其权限。用不同的用户帐户执行查询、插入、更新、 删除操作。

• 用存储过程来执行所有的查询。

• 限制表单或查询字符串输入的长度。

• 检查用户输入的合法性。

• 将用户登录名称、密码等数据加密保存。

• 检查提取数据的查询所返回的记录数量。

11. CI 服务有什么用途?(腾讯)

CI (Continuous Integration)-- 持续集成服务 -- 主要用于整合团队开发 中不同开发者提交到开发仓库中的项目代码变化,并即时整合编译,检查整合 编译错误的服务。它需要一天中多次整合编译代码的能力,若出现整合错误, 可以优异地准确定位提交错误源。

题组 Ⅲ

1. 计算机插上电源操作系统做了什么?(阿里)

- 加电 – – – – 打开电源开关,给主板和内部风扇供电。

- 启动引导程序 – – – – CPU 开始执行存储在 ROMBIOS 中的指令。

- 开机自检 – – – – 计算机对系统的主要部件进行诊断测试。

- 加载操作系统 – – – – 计算机将操作系统文件从磁盘读到内存中。

- 检查配置文件,定制操作系统的运行环境 – – – – 读取配置文件,根据用户的设置对操作系统进行定制。

- 准备读取命令和数据 – – – – 计算机等待用户输入命令和数据。

2. 一个对象的两个方法加 synchronized,一个线程进去 sleep,另一个线程可以进入到另一个方法吗?(阿里)

不能

3. 线程池参数有哪些?(阿里)

- corePoolSize 核心线程大小。

- maximumPoolSize 线程池最大线程数量。

- keepAliveTime 空闲线程存活时间。

- unit 空间线程存活时间单位。

- workQueue 工作队列。

- threadFactory 线程工厂。

- handler 拒绝策略。

4. 线程池拒绝策略有哪些?(阿里)

- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出 RejectedExecutionException 异常(默认拒绝策略)。

- ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。

- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。

- ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务。

5. hreadPoolExecutor 线程池,corePoolSize=5,maximumPoolSize=10,queueCapacity=10,有 20 个耗时任务交给这个线程池执行,线程池会如何执行这 20 个任务?(阿里)

- 如果当前线程数 < corePoolSize,如果是则创建新的线程执行该任务。

- 如果当前线程数 >= corePoolSize,则将任务存入 BlockingQueue。

- 如果阻塞队列已满,且当前线程数 < maximumPoolSize,则新建线程执行该任务。

- 如果阻塞队列已满,且当前线程数 >= maximumPoolSize,则抛出异常。

6. Java8 新特性有哪些了解?(阿里)

- 接口的默认方法。

- Lambda 表达式。

- 函数式接口。

- 方法和构造函数引用。

- Lamda 表达式作用域。

- 内置函数式接口。

- Optional。

- Streams(流)。

- ParallelStreams(并行流)。

- Maps。

- DateAPI(日期相关 API)。

- Annotations(注解)。

7. 栈会溢出吗?什么时候溢出?方法区会溢出吗?(阿里)

栈是线程私有的,它的生命周期与线程相同,每个方法在执行的时候都会创建一个栈帧,用来存储局部变量表,操作数栈,动态链接,方法出口等信息。局部变量表又包含基本数据类型,对象引用类型。如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常,方法递归调用产生这种结果。如果Java虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是无法申请到足够的内存去完成扩展,或者在新建立线程的时候没有足够的内存去创建对应的虚拟机栈,那么Java虚拟机将抛出一个OutOfMemory异常。(线程启动过多)。方法区会发生溢出。

HotSpot jdk1.7 之前字符串常量池是方法区的一部分,方法区叫做“永久代”,在 1.7 之前无限的创建对象就会造成内存溢出,提示信息:PermGenspace 而是用 jdk1.7 之后,开始逐步去永久代,就不会产生内存溢出。

方法区用于存放 Class 的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等,如果动态生成大量的 Class 文件,也会产生内存溢出。常见的场景还有:大量 JSP 或动态产生 JSP 文件的应用(JSP 第一次运行时需要编译为 java 类)、基于 OSGi 的应用(即使是同一个类文件,被不同的类加载器加载也会视为不同的类)

8. 哪些情况下的对象会被垃圾回收机制处理掉?(京东)

利用可达性分析算法,虚拟机会将一些对象定义为 GCRoots,从 GCRoots 出发沿着引用链向下寻找,如果某个对象不能通过 GCRoots 寻找到,虚拟机就认为该对象可以被回收掉。

- 哪些对象可以被看做是 GCRoots 呢?

1)虚拟机栈(栈帧中的本地变量表)中引用的对象;

2)方法区中的类静态属性引用的对象,常量引用的对象;

3)本地方法栈中 JNI(Native 方法)引用的对象;

- 对象不可达,一定会被垃圾收集器回收么?

即使不可达,对象也不一定会被垃圾收集器回收,1)先判断对象是否有必要执行 finalize() 方法,对象必须重写 finalize() 方法且没有被运行过。2)若有必要执行,会把对象放到一个队列中,JVM 会开一个线程去回收它们,这是对象最后一次可以逃逸清理的机会。

9. 静态代理和动态代理的区别,什么场景使用?(京东)

代理是一种常用的设计模式,目的是:为其他对象提供一个代理以控制对某个对象的访问,将两个类的关系解耦。代理类和委托类都要实现相同的接口,因为代理真正调用的是委托类的方法。区别:

- 静态代理:由程序员创建或是由特定工具生成,在代码编译时就确定了被代理的类是哪一个是静态代理。静态代理通常只代理一个类;

- 动态代理:在代码运行期间,运用反射机制动态创建生成。动态代理代理的是一个接口下的多个实现类;

实现步骤:

a.实现 InvocationHandler 接口创建自己的调用处理器;

b.给 Proxy 类提供 ClassLoader 和代理接口类型数组创建动态代理类;

c.利用反射机制得到动态代理类的构造函数;

d.利用动态代理类的构造函数创建动态代理类对象;

使用场景:Retrofit 中直接调用接口的方法;Spring 的 AOP 机制;

10. 谈谈你对解析与分派的认识。(京东)

解析指方法在运行前,即编译期间就可知的,有一个确定的版本,运行期间也不会改变。解析是静态的,在类加载的解析阶段就可将符号引用转变成直接引用。

分派可分为静态分派和动态分派,重载属于静态分派,覆盖属于动态分派。静态分派是指在重载时通过参数的静态类型而非实际类型作为判断依据,在编译阶段,编译器可根据参数的静态类型决定使用哪一个重载版本。动态分派则需要根据实际类型来调用相应的方法。

11. 如何将一个 Java 对象序列化到文件里?(京东)

ObjectOutputStream.writeObject() 负责将指定的流写入,ObjectInputStream.readObject() 从指定流读取序列化数据。
//写入
try{
  ObjectOutputStream os = new ObjectOutputStream(new
FileOutputStream("D:/student.txt"));
  os.writeObject(studentList);
  os.close();
}catch(FileNotFoundExceptione){
  e.printStackTrace();
}catch(IOExceptione){
  e.printStackTrace();
}

题组 Ⅳ

1. 说一下泛型原理,并举例说明。(京东)

泛型就是将类型变成参数传入,使得可以使用的类型多样化,从而实现解耦。Java 泛型是在 Java1.5 以后出现的,为保持对以前版本的兼容,使用了擦除的方法实现泛型。擦除是指在一定程度无视类型参数 T,直接从 T 所在的类开始向上 T 的父类去擦除,如调用泛型方法,传入类型参数 T 进入方法内部,若没在声明时做类似 publicTmethodName(TextendsFathert){}, Java 就进行了向上类型的擦除,直接把参数t当做 Object 类来处理,而不是传进去的 T。即在有泛型的任何类和方法内部,它都无法知道自己的泛型参数,擦除和转型都是在边界上发生,即传进去的参在进入类或方法时被擦除掉,但传出来的时候又被转成了我们设置的 T。在泛型类或方法内,任何涉及到具体类型(即擦除后的类型的子类)操作都不能进行,如 newT(),或者 T.play()(play 为某子类的方法而不是擦除后的类的方法)。

2. 讲下 Kafka、RabbitMQ、RocketMQ 之间的区别是什么?(京东)

性能

消息中间件的性能主要衡量吞吐量,Kafka 的吞吐量比 RabbitMQ 要高出 1~2 个数量级,RabbitMQ 的单机 QPS 在万级别,Kafka 的单机 QPS 能够达到百万级别。RocketMQ 单机写入 TPS 单实例约 7 万条/秒,单机部署 3 个Broker,可以跑到最高 12 万条/秒,消息大小 10 个字节,Kafka 如果开启幂等、事务等功能,性能也会有所降低。

数据可靠性

Kafka 与 RabbitMQ 都具备多副本机制,数据可靠性较高。RocketMQ 支持异步实时刷盘,同步刷盘,同步 Replication,异步 Replication。服务可用性 Kafka 采用集群部署,分区与多副本的设计,使得单节点宕机对服务无影响,且支持消息容量的线性提升。RabbitMQ 支持集群部署,集群节点数量有多种规格。RocketMQ 是分布式架构,可用性高。

功能

Kafka 与 RabbitMQ 都是比较主流的两款消息中间件,具备消息传递的基本功能,但在一些特殊的功能方面存在差异,RocketMQ 在阿里集团内部有大量的应用在使用。

3. 介绍下 MySQL 聚簇索引与非聚簇索引的区别(InnoDB 与 Myisam 引擎)?(京东)

聚集索引是指数据库表行中数据的物理顺序与键值的逻辑(索引)顺序相同。一个表只能有一个聚簇索引,因为一个表的物理顺序只有一种情况,所以,对应的聚簇索引只能有一个。聚簇索引的叶子节点就是数据节点,既存储索引值,又在叶子节点存储行数据。

Innodb 创建表后生成的文件有:

frm:创建表的语句;

idb:表里面的数据+索引文件非聚集索引(MyISAM引擎的底层实现)的逻辑顺序与磁盘上行的物理存储顺序不同。非聚簇索引的叶子节点仍然是索引节点,只不过有指向对应数据块的指针。索引命中后,需要回表查询。

Myisam 创建表后生成的文件有:

frm:创建表的语句 MYD:表里面的数据文件(myisam data)

MYI:表里面的索引文件(
myisam index)

innodb 的次索引指向对主键的引用(聚簇索引)

myisam 的次索引和主索引都指向物理行(非聚簇索引)

4. SpringAOP 底层原理(美团)

aop 底层是采用动态代理机制实现的:接口+实现类

- 如果要代理的对象,实现了某个接口,那么 SpringAOP 会使用 JDKProxy ,去创建代理对象。

- 没有实现接口的对象,就无法使用JDKProxy去进行代理了,这时候 SpringAOP 会使用 Cglib 生成一个被代理对象的子类来作为代理

5. HashMap 的扩容机制是怎样的?(美团)

一般情况下,当元素数量超过阈值时便会触发扩容。每次扩容的容量都是之前容量的 2 倍。HashMap 的容量是有上限的,必须小于 1<<30,即 1073741824。如果容量超出了这个数,则不再增长,且阈值会被设置为 Integer.MAX_VALUE。

JDK7 中的扩容机制:

- 空参数的构造函数:以默认容量、默认负载因子、默认阈值初始化数组。内部数组是空数组。

- 有参构造函数:根据参数确定容量、负载因子、阈值等。

- 第一次 put 时会初始化数组,其容量变为不小于指定容量的2的幂数,然后根据负载因子确定阈值。

- 如果不是第一次扩容,则新容量=旧容量x2,新阈值=新容量x负载因子。

JDK 8的扩容机制:

- 空参数的构造函数:实例化的 HashMap 默认内部数组是 null,即没有实例化。第一次调用 put 方法时,则会开始第一次初始化扩容,长度为 16。

- 有参构造函数:用于指定容量。会根据指定的正整数找到不小于指定容量的 2 的幂数,将这个数设置赋值给阈值(threshold)。第一次调用 put 方法时,会将阈值赋值给容量,然后让阈值=容量x负载因子。

- 如果不是第一次扩容,则容量变为原来的 2 倍,阈值也变为原来的 2 倍。(容量和阈值都变为原来的 2 倍时,负载因子还是不变)。

此外还有几个细节需要注意:

- 首次 put 时,先会触发扩容(算是初始化),然后存入数据,然后判断是否需要扩容;

- 不是首次 put,则不再初始化,直接存入数据,然后判断是否需要扩容;

6. ConcurrentHashMap 的存储结构是怎样的?(美团)

Java7 中 ConcurrnetHashMap 使用的分段锁,也就是每一个 Segment 上同时只有一个线程可以操作,每一个 Segment 都是一个类似 HashMap 数组的结构,它可以扩容,它的冲突会转化为链表。但是 Segment 的个数一但初始化就不能改变,默认 Segment 的个数是 16 个。

Java8 中的 ConcurrnetHashMap 使用的 Synchronized 锁加 CAS 的机制。结构也由 Java7 中的 Segment 数组+ HashEntry 数组+链表进化成了 Node 数组+链表/红黑树,Node 是类似于一个 HashEntry 的结构。它的冲突再达到一定大小时会转化成红黑树,在冲突小于一定数量时又退回链表

7. 线程池大小如何设置?(美团)

CPU 密集型任务 (N+1):这种任务消耗的主要是 CPU 资源,可以将线程数设置为 N(CPU核心数)+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间。

I/O 密集型任务 (2N):这种任务应用起来,系统会用大部分的时间来处理 I/O 交互,而线程在处理 I/O 的时间段内不会占用 CPU 来处理,这时就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务的应用中,我们可以多配置一些线程,具体的计算方法是 2N

8. G1 收集器有哪些特点?(美团)

- G1 的全称是 Garbage-First,意为垃圾优先,哪一块的垃圾最多就优先清理它。

- G1 GC 最主要的设计目标是:将 STW 停顿的时间和分布,变成可预期且可配置的。被视为 JDK1.7 中 HotSpot 虚拟机的一个重要进化特征。它具备一下特点:

- 并行与并发:G1 能充分利用 CPU、多核环境下的硬件优势,使用多个 CPU(CPU 或者 CPU 核心)来缩短 Stop-The-World 停顿时间。部分其他收集器原本需要停顿 Java 线程执行的 GC 动作,G1 收集器仍然可以通过并发的方式让 java 程序继续执行。

- 分代收集:虽然 G1 可以不需要其他收集器配合就能独立管理整个 GC 堆,但是还是保留了分代的概念。

- 空间整合:与 CMS 的“标记-清理”算法不同,G1 从整体来看是基于“标记-整理”算法实现的收集器;从局部上来看是基于“标记-复制”算法实现的。

- 可预测的停顿:这是 G1 相对于 CMS 的另一个大优势,降低停顿时间是 G1 和 CMS 共同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为 M 毫秒的时间片段内。

G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来)

9. 有哪些手段来排查 OOM 的问题?(美团)

- 增加两个参数 -XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/tmp/heapdump.hprof,当 OOM 发生时自动 dump 堆内存信息到指定目录。

- 同时 jstat 查看监控 JVM 的内存和 GC 情况,先观察问题大概出在什么区域。

- 使用 MAT 工具载入到 dump 文件,分析大对象的占用情况,比如 HashMap 做缓存未清理,时间长了就会内存溢出,可以把改为弱引用

10. B+ 树的叶子节点链表是单向还是双向?(美团)

双向链表

11. MVCC 是什么?它的底层原理是什么?(美团)

MVCC,多版本并发控制,它是通过读取历史版本的数据,来降低并发事务冲突,从而提高并发性能的一种机制。事务版本号、表的隐藏列、undolog 和 readview。

阅读量:2136

点赞量:0

收藏量:2