这个面试官很Nice,一开始要求我视频面试,后面网络很差,而且我讲话那边听到是一卡一卡的,就换成语音了,后面还是一卡一卡,就换成流量来通话。基本不问项目和框架(问到的基本不会),问我很多算法题和智力题(基本都会),十分开心。但是没说下一面是什么时候,估计还是凉了。
1. 自我介绍
2. 说说ACM比赛经历,其中印象最深刻的一场比赛讲一讲。
3. 说说Python爬虫经历
4. Java基本类型
谢谢。
5. 现在打来一个电话,还有一个黑名单,如何判断这个电话是否在黑名单里面
一开始答了用Trie字典树来做,然后就让我讲讲Trie是怎么样的。接下来面试官说还有其他方法吗。想了一两分钟,说电话号码一般小于18位,把电话号码直接存在long里面,用哈希表或者set保存就行了。然后就让我讲哈希表。
6. 哈希表
讲了拉链表法,红黑树优化。
7. 单源最短路
讲了Dijkstra算法,还有堆优化。
8. 知道什么排序,讲讲快排
9. Python合并两个数组并去重(不会)
想了几分钟,实在太久没用过了,只好说不会。
>>> a = [1,3,5,7]
>>> b = [1,3,4,6,8]
>>> c = list(set(a+b))
>>> a
[1, 3, 5, 7]
>>> b
[1, 3, 4, 6, 8]
>>> c
[1, 3, 4, 5, 6, 7, 8]
在网上找到的...感觉这不是面试官想要的答案。
10. 讲了那么多算法,来讲讲Java的线程池吧。(不会)
说了知道Executors。然后就没有然后了。占坑。
线程池文章
11. 讲SQL语句
(1)查询年龄最大十个人:差点忘了LIMIT关键字。 select * from tb order by age desc limit 10;
(2)不同岗位的人数:一开始直接用count(),面试官提示需要知道岗位,然后在前面加了个岗位字段。 select job, count() from tb group by job;
12. 类加载过程(只会一点)
该来的还是来了,看了很多,结果面试支支吾吾的讲不出来。
加载
在加载阶段,虚拟机完成以下三件事情:
- 通过一个类的全限定名(包名.外部类名)来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
加载阶段可以用系统提供的引导类加载器完成,也可以由用户自定义的类加载器去完成。
数组类本身不通过类加载器创建,由Java虚拟机直接创建。 但是数组类的元素类型(去掉所有维度后的类型)最终是要靠类加载器去创建。
验证
验证阶段大致会完成下面四个阶段的验证工作:文件格式验证、元数据验证、字节码验证、符号引用验证。
- 文件格式验证:主要目的是保证输入的字节流能正确地解析并存储于方法区之内,格式上符合描述一个Java类型信息的要求。这个阶段的验证是基于二进制字节流进行的,只有通过这个阶段验证,字节流才会进入内存的方法区进行存储。
- 元数据验证:主要目的是对类的元数据信息进行语义校验,保证不存在不符合Java语言规范的元数据信息。
- 字节码验证:主要目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。保证被校验类的方法在运行时不会做出危害虚拟机安全的事件。JDK1.6后给方法体Code属性的属性表添加
StackMapTable
属性(栈图)进行优化。 - 符号引用验证:目的是确保解析动作能正常执行。
准备
准备阶段是正式为类变量(被static修饰的变量,不包括实例变量)分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。
public static int value = 233;
变量value
在准备阶段过后的初始值为0而不是233,这时候尚未开始执行任何方法,把value
赋值是程序被编译后,所以赋值的动作将在初始化阶段才会执行。
如果变成
public static final int value = 233;
那么准备阶段变量value就会被初始化为类字段的字段属性表中ConstantValue属性所指定的值233。
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
符号引用
符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。与虚拟机实现的内存布局无关,引用的目标不一定已经加载到内存中。符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
直接引用
直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。与虚拟机实现的内存布局相关,如果有直接引用,那引用目标必定在内存中存在。
初始化
类初始化是类加载过程的最后一步,也是真正开始执行类中定义的Java程序代码(或者说是字节码) 的一步。
初始化阶段是执行类构造器<clinit>()
方法的过程。
-
<clinit>()
方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,收集顺序是由语句在源文件中出现的顺序决定的。静态语句块中只能访问到定义在静态语句块之前的变量,定义在之后的变量可以赋值,不能访问。 - 虚拟机会保证子类的
<clinit>()
方法执行前,父类的<clinit>()
方法已经执行完毕。虚拟机中第一个执行<clinit>()
的类一定是Object类。 - 不是每个类都有
<clinit>()
方法。 - 执行接口的
<clinit>()
方法不需要先执行父接口的<clinit>()
方法。只有当父接口中定义的变量使用,父接口才会初始化。接口实现类初始化时也不会执行接口的<clinit>()
方法。 - 虚拟机保证一个类的
<clinit>()
方法在多线程环境中被正确加锁、同步。
13. ThreadLocal(只会一点)
面试前和宿友说,感觉会问ThreadLocal,在上课时讨论了好久,成功毒奶。
结果自己记得不多,只讲了实现线程安全,ThreadLocalMap。
ThreadLocal实现原理
ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
- 每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals.这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本。
- 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
- 在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
- ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。如果对应的ThreadLocal没有外部的强引用,ThreadLocal就会被回收。当系统进行ThreadLocalMap清理时(比如新的变量加入表中,就会自动进行一次清理,虽然JDK不一定会进行彻底的扫描),就会自然将垃圾数据回收,即对应的Entry,当然包括entry.value。也就是说不用调用remove也有可能回收对应的对象。
- initialValue是被重写的。如果没有set就调用get默认会抛出异常。但是重写该方法就会返回这个方法返回的东西。
- 如果将一些大对象设置到ThreadLocal中,如果没有remove掉,可能会使系统出现内存泄漏。
14. 讲讲SpringMVC(不会)
虽然看过,但是印象不深了。现在回想起来,还好面试官没有当场喷我,不懂装懂很可怕。
SpringMVC工作流程描述:
- 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
- DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象(包含一个处理器Handler(controller中的处理方法)及若干拦截器HandlerInterceptor。)的形式返回;
- DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
- 提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作: (1)HttpMessageConveter:将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息。 (2)数据转换:对请求消息进行数据转换。如String转换成Integer、Double等。 (3)数据根式化:对请求消息进行数据格式化。如将字符串转换成格式化数字或格式化日期等。 (4)数据验证:验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中。
- Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
- DispathcerServlet执行render()方法,根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
- ViewResolver 结合Model和View,来渲染视图。
- 将渲染结果返回给客户端。
15. 9个球,一个重量不一样,最少多少次找出来
两次。每一次分成三组,每组三个,第二次分成三组,每组一个。
16. 一个数组中一个数出现次数超过一半
先说一种较差的,再说一种最优的。就是用一个cnt计数,用一个num标记当前是哪个数。然后遍历数组,当cnt=0的时候,就将遍历到的数赋给num,cnt++。当当前遍历的数不是num的时候,cnt--;当是num的时候,cnt++。最后判断这个num出现的次数是否大于n/2,如果是的话,就是这个数,否则就不是。
17. 如果广东那边的公司给offer,而我们在北京,会怎么选择
没有如果,这辈子都没有offer的。
18. 有什么想问的
问了评价一下这次面试,面试官不发表评论。(感觉自己有点没礼貌,凉)
问了对方用什么技术。
总结
这段时间感觉自己并没有怎么提高,倒是刷了一些题。框架不会的还是不会,简历写上去的坑要填上啊,连Python都忘光了还写上去被怼惨。类加载机制也是虽然看过很多遍,还是忘光了。最好在看完一个东西的时候口头复述一遍。
网友评论