2019年最新阿里Java工程师面试题

一、单选题(共10题,每题5分)

关于设计模式遵循的原则,说法错误的是?
 
A、组合优于继承
B、针对实现编程
C、对扩展开放,对修改关闭
D、降低对象之间的耦合
参考答案:B
答案解析:

设计模式(面向对象)有七大原则,分别是: 1.开放-封闭原则 2.单一职责原则 3.依赖倒转原则 4.迪米特法则(也称为最小知识原则) 5.接口隔离原则 6.合成/聚合复用原则 7.里氏代换原则 详情参考 :https://www.cnblogs.com/qingjiaowoxiaoxioashou/p/6273658.html

 
Mysql索引使用的B-Tree描述错误的是?
 
A、每个非叶子结点由n-1个key和n个指针组成,其中d<=n<=2d;
B、每个叶子结点至少包含一个key和两个指针
C、所有叶结点都在同一层,深度等于树高h
D、一个结点中的key从左至右递减排列
参考答案:D
答案解析:

B-Tree是满足条件: d>=2,即B-Tree的度; h为B-Tree的高; 每个非叶子结点由n-1个key和n个指针组成,其中d

 
3下列关于Java并发的说法中正确的是()
 
A、CopyOnWriteArrayList适用于写多读少的并发场景
B、ReadWriteLock适用于读多写少的并发场景
C、ConcurrentHashMap的写操作不需要加锁,读操作需要加锁
D、只要在定义int类型的成员变量i的时候加上volatile关键字,那么多线程并发执行i++这样的操作的时候就是线程安全的了
参考答案:B
答案解析:
B
对于D选项,volatite只保证线程在“加载数据阶段”加载的数据是最新的,并不能保证线程安全。
 
**一个线程执行的过程有三个阶段:**
加载(复制)主存数据到操作栈 --> 对操作栈数据进行修改 --> 将操作栈数据写回主存
volatite关键字,让编译器不去优化代码使用缓存等,以保证线程在“加载数据阶段”加载的数据都是最新的
 
**比如:**
某一时刻i=6是最新的值,volatile保证线程A,B都同时加载了这个最新的值,
然后A执行i(A)+1=7,然后将7写回主存,
B也执行i(B)+1=7,然后也将7写回内存,
这样,执行两次加法,i却只增加了1
 
4以下为求0到1000以内所有奇数和的算法,从中选出描述正确的算法( )
 
A、①s=0;②i=1;③s=s+i;④i=i+2;⑤如果i≤1000,则返回③;⑥结束
B、①s=0;②i=1;③i=i+2;④s=s+i;⑤如果i≤1000,则返回③;⑥结束
C、①s=1;②i=1;③s=s+i;④i=i+2;⑤如果i≤1000,则返回③;⑥结束
D、①s=1;②i=1;③i=i+2;④s=s+i;⑤如果i≤1000,则返回③;⑥结束
参考答案:A
答案解析:

A和B选项的区别在于第3步和第4步,B中当i=999时,先执行步骤3,则i=i+2=1001,接着第4步s=s+1001,如此看来这种3、4步的写法求得的S会多加了一个i=1001.

 
5下面关于垃圾收集的说法正确的是
 
A、一旦一个对象成为垃圾,就立刻被收集掉。
B、对象空间被收集掉之后,会执行该对象的finalize方法
C、finalize方法和C++的析构函数是完全一回事情
D、一个对象成为垃圾是因为不再有引用指着它,但是线程并非如此
参考答案:D
 
 
6Java虚拟机在运行Java代码时,首先进行的操作是()
 
A、编译代码
B、校验代码
C、加载代码
D、连接代码
参考答案:C
答案解析:

1)编译器负责把java文件编译为class文件, 2)JAVA虚拟机(JVM)对class文件,进行加载、校验、执行, 故本题的答案为C。

 

7以下哪个不是Collection的子接口?
 
A、List
B、Set
C、SortedSet
D、Map
参考答案:D
答案解析:

2019年最新阿里Java工程师面试题_第1张图片

补图:

2019年最新阿里Java工程师面试题_第2张图片

 
OpenStack中Mongodb推荐使用( )盘部署。
 
A、企业级SAS
B、企业级SSD
C、企业级SATA
D、企业级ML-SAS
参考答案:B
答案解析:

 
9以下代码的循环次数是 ?
 1 public class Test {  
 2  public static void main(String args\[\]) {  
 3  int i = 7;  
 4  do {  
 5  System.out.println(--i);  
 6  --i;  
 7  } while (i != 0);  
 8  System.out.println(i);  
 9  }  
10 }

 

A、0
B、1
C、7
D、无限次
参考答案:D
答案解析:

 
10关于Redis 分布式锁的特点描述错误的是?
 
A、互斥(只能有一个客户端获取锁)
B、释放锁就是设置key已过期
C、容错(只要大部分 redis 节点创建了这把锁就可以)
D、不能死锁
参考答案:B
答案解析:

选项B 应该是 释放锁就是删除 key。

RedLock 算法

这个场景是假设有一个 redis cluster,有 5 个 redis master 实例。然后执行如下步骤获取一把锁:

获取当前时间戳,单位是毫秒; 跟上面类似,轮流尝试在每个 master 节点上创建锁,过期时间较短,一般就几十毫秒; 尝试在大多数节点上建立一个锁,比如 5 个节点就要求是 3 个节点 n / 2 + 1; 客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了; 要是锁建立失败了,那么就依次之前建立过的锁删除; 只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁。

 

二、多选题(共10题,每题5分)

1关于MySQL的脏读、幻读和不可重复读说法正确的有?
 
A、脏读 :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务再修改数据。那么第一个事务两次读到的的数据可能是不一样的,因此称为是脏读。
B、不可重复读:是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。
C、幻读:当某事物正在执行插入或删除操作同时,第二个事物也在操作此表的数据,就会显示有一行还未存在的数据,就像发生了幻觉一样。
D、脏读、幻读和不可重复是MySQL固有的缺陷,无法避免
参考答案:C
答案解析:

脏读 : 脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。 不可重复读 : 是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务再修改数据。那么第一个事务两次读到的的数据可能是不一样的,因此称为是不可重复读。 幻读: 当某事物正在执行插入或删除操作同时,第二个事物也在操作此表的数据,就会显示有一行还未存在的数据,就像发生了幻觉一样。

解决办法:如果在操作事务完成数据处理之前,任何其他事务都不可以操作此数据,则可避免该问题。

 
JVM管理的内存包括哪几个运行时数据内存?
 
A、方法区
B、虚拟机栈
C、本地方法栈
D、堆和程序计数器
参考答案:A,B,C,D
答案解析:

Java虚拟机管理的内存包括几个运行时数据内存:方法区、虚拟机栈、本地方法栈、堆、程序计数器,其中方法区和堆是由线程共享的数据区,其他几个是线程隔离的数据区

 
Redis 内存淘汰机制有哪些?
 
A、noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错
B、allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key
C、volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key
D、allkeys-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。
参考答案:A,B,C
答案解析:

redis 内存淘汰机制有以下几个:

noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。 allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。 allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。 volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的 key(这个一般不太合适)。 volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个 key。 volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。

 
4Hystrix 的设计原则描述正确的有哪些?
 
A、对依赖服务调用时出现的调用延迟和调用失败进行控制和容错保护
B、在复杂的分布式系统中,阻止某一个依赖服务的故障在整个系统中蔓延。比如某一个服务故障了,导致其它服务也跟着故障
C、提供 fail-fast(快速失败)和快速恢复的支持
D、提供 fallback 优雅降级的支持
参考答案:A,B,C,D
答案解析:

Hystrix 的设计原则

  • 对依赖服务调用时出现的调用延迟和调用失败进行控制和容错保护。
  • 在复杂的分布式系统中,阻止某一个依赖服务的故障在整个系统中蔓延。比如某一个服务故障了,导致其它服务也跟着故障。
  • 提供 fail-fast(快速失败)和快速恢复的支持。
  • 提供 fallback 优雅降级的支持。
  • 支持近实时的监控、报警以及运维操作。

 

关于Kafka、ActiveMQ、RabbitMQ、RocketMQ说法正确的有?
 
A、ActiveMQ 基于 erlang 开发,并发能力很强,性能极好,延时很低
B、RocketMQ topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,在同等机器下,可以支撑大量的 topic
C、RabbitMQ时效性是微秒级,这是 RabbitMQ 的一大特点,延迟最低
D、Kafka 单机吞吐量 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景
参考答案:B,C,D
答案解析:

ActiveMQ 基于 Java 开发的, RabbitMQ 是基于 erlang 开发的。 所以选项A 错误。 B、C、D都正确。

特性 ActiveMQ RabbitMQ RocketMQ Kafka
单机吞吐量 万级,比 RocketMQ、Kafka 低一个数量级 同 ActiveMQ 10 万级,支撑高吞吐 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景
topic 数量对吞吐量的影响     topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源
时效性 ms 级 微秒级,这是 RabbitMQ 的一大特点,延迟最低 ms 级 延迟在 ms 级以内
可用性 高,基于主从架构实现高可用 同 ActiveMQ 非常高,分布式架构 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用
消息可靠性 有较低的概率丢失数据 基本不丢 经过参数优化配置,可以做到 0 丢失 同 RocketMQ
功能支持 MQ 领域的功能极其完备 基于 erlang 开发,并发能力很强,性能极好,延时很低 MQ 功能较为完善,还是分布式的,扩展性好 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用
 
分布式服务接口的幂等性如何设计(比如不能重复扣款)?
 
A、对于每个请求必须有一个唯一的标识
B、每次处理完请求之后,必须有一个记录标识这个请求处理过了
C、每次接收请求需要进行判断,判断之前是否处理过
D、恰当的使用数据库的事务,能彻底避免幂等性问题
参考答案:A,B,C
答案解析:

分布式服务接口,只靠数据库的事务是保证不了幂等性的。 选项D错误。

所谓幂等性,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款、不能多插入一条数据、不能将统计值多加了 1。这就是幂等性。

其实保证幂等性主要是三点:

  • 对于每个请求必须有一个唯一的标识,举个栗子:订单支付请求,肯定得包含订单 id,一个订单 id 最多支付一次,对吧。
  • 每次处理完请求之后,必须有一个记录标识这个请求处理过了。常见的方案是在 mysql 中记录个状态啥的,比如支付之前记录一条这个订单的支付流水。
  • 每次接收请求需要进行判断,判断之前是否处理过。比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId 已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。
  • 实际运作过程中,你要结合自己的业务来,比如说利用 redis,用 orderId 作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。

要求是支付一个订单,必须插入一条支付流水,order_id 建一个唯一键 unique key。你在支付一个订单之前,先插入一条支付流水,order_id 就已经进去了。你就可以写一个标识到 redis 里面去,set order_id payed,下一次重复请求过来了,先查 redis 的 order_id 对应的 value,如果是 payed 就说明已经支付过了,你就别重复支付了。

 
有哪些方案可以实现分布式事务?
 
A、XA 方案
B、TCC 方案
C、本地消息表
D、可靠消息最终一致性方案
参考答案:A,B,C,D
答案解析:

分布式事务的实现主要有以下 5 种方案:

XA 方案 TCC 方案 本地消息表 可靠消息最终一致性方案 最大努力通知方案 详情参考 分布式事务

 
关于缓存雪崩的事前事中事后的解决方案正确的有?
 
A、事前:进行系统压力测试,在负载均衡层做限流处理,过载丢弃请求或者进入队列
B、事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
C、事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
D、事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
参考答案:A,B,C,D
答案解析:

缓存雪崩的事前事中事后的解决方案如下。

  • 事前:进行系统压力测试,在负载均衡层做限流处理,过载丢弃请求或者进入队列
  • 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
  • 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
  • 事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
 
如何保证缓存与数据库的双写一致性?
 
A、优先采用 Cache Aside Pattern 读写模式
B、最初级的缓存不一致解决思路:先删除缓存,再更新数据库。
C、比较复杂的数据不一致问题:分析更新数据的时候,根据数据的唯一标识,将操作路由之后,发送到一个内部队列中,每个工作线程串行拿到对应的操作,然后一条一条的执行
D、如果一个内存队列中可能积压的更新操作特别多,那么你就要加机器,让每个机器上部署的服务实例处理更少的数据,那么每个内存队列中积压的更新操作就会越少。
参考答案:A,B,C,D
答案解析:

Cache Aside Pattern 最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern。

读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。 更新的时候,先更新数据库,然后再删除缓存。 为什么是删除缓存,而不是更新缓存?

原因很简单,很多时候,在复杂点的缓存场景,缓存不单单是数据库中直接取出来的值。

比如可能更新了某个表的一个字段,然后其对应的缓存,是需要查询另外两个表的数据并进行运算,才能计算出缓存最新的值的。

另外更新缓存的代价有时候是很高的。是不是说,每次修改数据库的时候,都一定要将其对应的缓存更新一份?也许有的场景是这样,但是对于比较复杂的缓存数据计算的场景,就不是这样了。如果你频繁修改一个缓存涉及的多个表,缓存也频繁更新。但是问题在于,这个缓存到底会不会被频繁访问到?

举个栗子,一个缓存涉及的表的字段,在 1 分钟内就修改了 20 次,或者是 100 次,那么缓存更新 20 次、100 次;但是这个缓存在 1 分钟内只被读取了 1 次,有大量的冷数据。实际上,如果你只是删除缓存的话,那么在 1 分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低。用到缓存才去算缓存。

其实删除缓存,而不是更新缓存,就是一个 lazy 计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让它到需要被使用的时候再重新计算。像 mybatis,hibernate,都有懒加载思想。查询一个部门,部门带了一个员工的 list,没有必要说每次查询部门,都里面的 1000 个员工的数据也同时查出来啊。80% 的情况,查这个部门,就只是要访问这个部门的信息就可以了。先查部门,同时要访问里面的员工,那么这个时候只有在你要访问里面的员工的时候,才会去数据库里面查询 1000 个员工。

 

10 以下对NIO的的描述,正确的有?
 
A、Java IO和NIO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的。
B、JavaNIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。
C、使用纯粹的NIO设计相较IO设计,数据处理也受到影响。在IO设计中,我们从InputStream或 Reader逐字节读取数据。
D、使用NIO的API调用时看起来与使用IO时有所不同,但这并不意外,因为并不是仅从一个InputStream逐字节读取,而是数据必须先读入缓冲区再处理。
参考答案:A,B,C,D
答案解析:

全对,IO和NIO的主要对比如下:

IO NIO
面向流 面向缓冲
阻塞IO 非阻塞IO
选择器

你可能感兴趣的:(2019年最新阿里Java工程师面试题)