快手秋招一面(已过,约2面)

从7月15来深圳实习后,至今已有快一个半月时间了。

目前深圳这边还留着一个很大的烂摊子需要解决,就是这个房子转租的问题,当时租的两室一厅,租金一个月2580r。

当时和对象一起住,现在易琳开学走后只剩我一个人住,很头疼,所以才狠下决心好好秋招。

房子在深圳翻身这边,靠近南山区,如果有意向来深圳实习或者工作的uu们,欢迎私信来看房。

————————————————

快手面试一上来先让我自我介绍。

然后做题。发现是力扣经典题:全排列。5分钟不到直接OC。

下面仅精选部分印象深刻的设问展示:

然后就这道题开始发问:

问题1:我看你用到了Deque,你对Deque了解吗?Deque和ArrayList有什么区别?ArrayDeque和ArrayList有什么区别?

问题2:你能说一下ArrayList的扩容原理吗?

问题3:看你简历上写熟悉泛型(其实我写的是了解),你能说一下为什么泛型只能用引用类而不用基本数据类型吗?

问题4:你提到过类型擦除...这里忘记又问了啥,感觉挺深的。

然后开始问实习经历:

问题1:请你介绍一下你的实习经历。

问题2:看你使用到Solr,你能说一下技术选型是出于什么原因吗?为什么不用es?

问题3:能说一下facet(分面)吗?

上述问题因为都准备过,所以balabala了一大堆。

然后开始问项目:

问题1:为什么要使用RocketMQ来实现异步秒杀?有什么地方特别慢吗?

问题1:假如用户领取优惠券,前端返回领取成功,数据写入到Redis中成功,但是发送到RocketMQ时因为某种原因导致消息丢失了会出现什么问题?应该怎么处理?

问题2:假如消息重复发送了怎么办?问的是RocketMQ重复消费的问题怎么处理?

最后开启八股文模式:

问题1:说说事务的隔离级别

问题2:Redis为什么快?

就这个问题扯到单线程,然后从单线程扯到RDB和AOF,问RDB日志生成的时候redis是单线程的,是不是要阻塞很久?如何解决的?(写时复制)

————————————————

总结:较简单。很大一部分原因是多了一个实习经历来转嫁压力,如果一直挖电商项目能给我挖吐。

刚好面试官说他没用过Solr,只能听我准备好的话术长篇大论一番。

需要着重加强的是:(1)Deque和List底层原理、(2)泛型(类型擦除)、(3)Solr选型原因、(4)RocketMQ消息丢失+重复消费、(5)写时复制。

————————————————

以下是针对本次面试薄弱知识点的总结,如有谬误敬请指正:

说一下Deque。

基本信息:

Deque是Double Ended Queue的缩写,指的是双端队列,它是一个接口,有2个具体的实现类分别是ArrayDeque和LinkedList。

ArrayDeque是基于数组实现的线性双向队列,LinkedList是基于链表实现的链式双向队列,二者都可以在队首或队尾插入或删除元素,可以作为栈或者队列使用,既具有FIFO的特性又具有LIFO的特性,既可以是队列也可以是栈。

说一下ArrayDeque。

基本信息:

ArrayDeque是Java集合框架中Deque接口(Deque接口继承了Queue接口)的具体实现类,它是基于可扩展的循环数组实现的线性双向队列

底层实现:

ArrayDeque底层是用一个可扩展的循环数组来存储元素。

通过维护2个指针来控制元素的插入和删除。

Head指针指向队列第一个元素,Tail指针指向队列的下一个插入位置(一开始head和tail是重叠的,队列为空时指向同一个位置)。

插入:时间复杂度O(1)

AddFirst()会将Head指针-1,若到达数组开头,绕回末尾,将新元素插入,数组满调用doubleCapacity()扩容。

AddLast()会先将新元素插入,然后将tail指针+1,如果到达数组末尾,绕回开头,如果数组满了使用doubleCapacity()扩容。

删除:时间复杂度O(1)

RemoveFirst()删除并返回列表第一个元素。首先获取head指针指向的元素,然后将其置为null,将head指针+1,如果head到达数组末尾绕回到开头。

RemoveLast()删除并返回队列最后一个元素。先将tail指针-1,如果tail到达数组开头,绕回到数组末尾,获取tail指针指向的元素,将其置为null,返回删除的元素。

扩容机制:单次扩容时间复杂度O(n)

如果数组已满调用doubleCapacity()方法将数组容量加倍。

首先会创建一个新的数组,长度是旧数组的两倍。然后会将旧数组中的元素复制到新数组中,保持元素的顺序不变。最后重新设置head和tail指针,适应新的数组。

ArrayList和ArrayDeque的区别?

1、 底层实现:ArrayList底层是动态数组,ArrayDeque的底层是循环动态数组。

2、 访问元素:ArrayList支持随机访问O(1),ArrayDeque不支持随机访问,访问元素需要遍历O(n)。

3、 插入效率:ArrayList末O(1),首和中间O(n)。ArrayDeque首末O(1),不支持在中间插入。

4、 删除效率:ArrayList末O(1),首和中间O(n)。ArrayDeque首末O(1),不支持在中间删除。

5、 扩容机制:ArrayList是扩展为原先的1.5倍。ArrayDeque是扩展为原先的2倍。

6、 适用场景:ArrayList适用于需要快速随机访问元素的场景,常用来实现动态数组。ArrayDeque适用于在队列两端频繁插入和删除操作的场景。通常用来实现栈(LIFO)和队列(FIFO)。

泛型的类型参数为什么不能是基本数据类型?

泛型类型参数必须是引用类型而不能是基本数据类型。

原因:

1、因为泛型要求包容的是引用类型,而引用类型包含了对象类型,因此一个数据类型最起码得是对象类型才能作为泛型的类型参数,而基本数据类型在Java中不属于对象,所以不能被转化。

2、泛型信息在编译的时候会被类型擦除,会被替换为他们的原始类型(通常是 Object)。而Object类的子类包括像:包装类,集合类,I/O类,多线程类这些,不包括基本数据类型。

类型擦除是什么?

定义:

类型擦除指的是:泛型信息在编译的时候会被擦除,会被替换为他们的原始类型(通常是 Object,比如Box<T>的原始类型是Box,List<E>的原始类型是List)。泛型仅用于在编译时进行类型检查和类型推断,但是在运行时会被擦除,在编译完成之后生成的字节码文件之中不会包含泛型类型参数的信息。

擦除的原因:

泛型是在Java5中加入的新特性,Java5是在2004年发布的。在Java5之前没有泛型,在java5之前的代码是这样写的:List list = new ArrayList(),List是一个原始类型,因为没有泛型类型参数,list可以包含任何类型的对象。

类型擦除的机制使得Java 能够与旧版本的代码保持兼容,使得现有的非泛型代码可以与新加入的泛型代码共同工作。

擦除底层流程:

编译时:泛型类型信息在编译时被擦除,类型参数被替换为它们的原始类型。在需要的地方,编译器会插入类型转换来确保类型安全String str = (String) stringBox.get()。

运行时:JVM不知道泛型类型的存在,所有的类型检查和类型转换在编译时处理完毕。

引用类型?

类(Class):如 String、Integer、ArrayList。

接口(Interface):如 List、Map。

数组(Array):如 int[]、String[]。

枚举(Enum):如 enum Color { RED, GREEN, BLUE }

对象类型?

类的实例化对象是对象类型,如 new String("Hello") 是 String 类的一个对象。

数组也是对象类型,即使是基本类型的数组,如 int[] 也是一个对象类型,因为数组在 Java 中被视为对象。

枚举类型的实例也是对象类型。               

泛型是什么?

泛型是Java 5引入的一种机制,允许在类、接口和方法中使用类型参数,使得代码更加通用和类型安全。

优点:

(1)通用:能提高代码的重用性,编写一次代码,可以适用于多种类型。

(2)消除显式类型转换。使用泛型可以减少显式类型转换的需求,从而减少类型转换错误(ClassCastException)。在非泛型代码中,必须手动进行类型转换,这可能会在运行时导致错误:

(3)类型安全:在编译时检查类型,编译器会在编译时对类型进行严格检查,如果试图将不兼容的类型传递给泛型集合或泛型方法,编译器会立即报告错误,减少运行时的ClassCastException(该异常在尝试将对象转换为不兼容的类时被抛出)。

类型安全拿下面例子举例:List<String> stringList = new ArrayList<>()其中List<E> 是一个泛型接口,其中的 E 表示元素的类型。通过指定 E 为String,你创建了一个只能包含字符串的列表。ArrayList<E> 是一个实现了 List<E> 接口的泛型类。通过指定 E 为 String,你创建了一个只能包含字符串的 ArrayList 实例。

通过在 List 和 ArrayList 中使用泛型,在编译时就定义了列表中只能包含 String 类型的元素。编译器会在编译时检查所有的操作,确保类型安全。例如,如果你试图将一个 Integer 添加到 stringList 中,stringList.add(123),编译器会报错:不兼容的类型。

缺点:

(1)类型擦除:泛型在编译时会进行类型擦除,也就是替换为他们的原始类型(通常是Object,比如Box<T>的原始类型是Box,List<E>的原始类型是List),导致无法在运行时获取到类型信息。

#23届找工作求助阵地##我的实习求职记录#
全部评论
好了好了,知道你有对象了
1 回复 分享
发布于 08-30 18:13 北京
没问项目吗
点赞 回复 分享
发布于 08-30 21:51 广东
直接和对象住一起???不是哥们,你还保有清白之身吗
点赞 回复 分享
发布于 08-30 23:55 广东
RDB日志生成 不是另外开一个子线程嘛?
点赞 回复 分享
发布于 09-03 15:08 上海
好像认识你对象
点赞 回复 分享
发布于 09-06 09:11 美国

相关推荐

11-08 10:39
门头沟学院 C++
点赞 评论 收藏
分享
喜欢走神的孤勇者练习时长两年半:池是池,发是发,我曾池,我现黑
点赞 评论 收藏
分享
9 47 评论
分享
牛客网
牛客企业服务