转载请注明出处: 贴一贴我的后端开发面试题。
本文是面试回寝室后凭记忆罗列出来的问题,大概90%的问题都在这里面了,有几个问题的实在是想不起来了= =,有些问题自我感觉回答的不好,所以我是查了资料后重新整理了再贴上答案的。如有错误或不适合的,欢迎大家评论点出,谢谢!
虽然面试的是Java实习生职位,但问题不局限于Java语言。
技术无关:
面试过程中只有三个技术无关的话题:
自我介绍。
感觉自己有什么优缺点?
你现在有女朋友了吗? = =..
这类的话题一般稍微准备一下,都不会有什么问题。
技术相关:
1. Spring MVC 如何接受并处理一个请求的?
首先我们会在
web.xml
中注册一个DispatcherServlet
,并令这个servlet
接收所有的请求,项目启动后Spring会扫描配置文件,根据配置加载和实例化类,其中扫描到的带有@Controller
或者@RestController
注解的类则是请求要映射到的类,Spring MVC扫描里面所有和请求映射有关的注解, 如@RequestMapping
、@ResponseBody
、@RequestParam
等。当接收到一个请求时,它会根据请求的url映射到对应的controler,并根据返回值判断是渲染jsp页面还是返回普通文本,亦或是返回json。
2. AOP实现原理。
AOP是通过动态代理来实现的,有两种常用的技术,一是
JDK的动态代理
,二是CGLIB
,而无论是前者还是后者,都是生成动态生成类的字节码来实现的。JDK的动态代理只能处理接口实现的方法
,而CGLIB则没有这个限制。因为字节码是动态生成的,所以可以在生成的字节码当中,在目标方法前后插入定义好的方法的调用。
3. 注解是怎么用的?为什么要使用注解?
当在一个类、方法或者字段上标上注解后,可以通过
obj.getClass().isAnnotationPresent(..)
来判断一个目标是否被特定的注解标识,通过obj.getClass().getAnnotation(..)
来获取标志是注解,以此获得注解上的信息。使用注解可以帮助我们在项目的编译期或运行时给类、方法或对象添加一个额外的信息,给编程增加了很大的灵活性。比如用@Override
来标志这是重写父类的方法,那么编译器就可以在编译期检查该方法是否真的是重写父类的方法,将错误扼杀在编译器。
4. 线程有几种状态?生命周期是怎样的?
线程有五种状态:
创建
、就绪
、运行
、阻塞
、死亡
。
调用start
方法时,线程就会进入就绪状态。
在线程得到cpu时间片
时进入运行状态。
线程调用yield
方法可以让出cpu时间回到就绪状态。
线程运行时可能由于IO
、调用sleep
、wait
、join
方法或者无法获得同步锁
等原因进入阻塞状态。
当线程获得到等待的资源资源或者引起阻塞的条件得到满足时(调用notify
或notifyAll
),会从阻塞状态进入就绪状态。
当线程的run
方法执行结束或者调用interrupt
方法时,线程就进入死亡状态。
5. Java中如何实现同步。
Java实现同步的方法有:
使用
synchronized
关键字为方法或代码块加锁。使用
volatile
修饰变量,但是volatile不保证原子性
。使用
ReentrantLock
或者ReentrantReadWriteLock
, 这种方法比synchronized
更灵活。使用
Semaphore
,允许最多n个线程同时访问资源。
6. HashMap
与Hashtable
的区别。
HashMap
是线程不安全
的,Hashtable
是线程安全的。
HashMap
的key和value接受null,Hashtable
不接受。
HashMap
继承自AbstractMap
,Hashtable
继承自Directory
。
7. JVM是否了解?
这里我回答了最近正在看《深入理解Java虚拟机》一书,本想着这方面的问题能答上一些的,没想到面试官直接说
那看样子还不是很了解,就不问这块的问题了= =.. 心塞
但是我估摸着大概如果问的话会问:
7.1. JVM的内存模型。
JVM的内存一共分为5个部分:
程序计数器
: 里面存放着线程执行的指令。
方法区
: 存放类的信息,如:类名、方法、成员变量等,也存放着常量池。
虚拟机栈
: 存放着局部变量表、操作数栈、方法出口信息等方法执行所需信息。
本地方法栈
: 存放程序调用native方法的信息。
堆
: 这五个部分中最大的,对象的内存分配都是在堆内存中。
7.2. 常用的虚拟机参数。
-Xmx
: 指定最大堆内存
-Xms
: 指定初始化堆内存大小。
-Xmn
: 指定年轻代内存初始内存大小,同时也是最大内存大小。
-XX:NewSize
: 指定年轻代内存大小。
-XX:NewRatio
: 指定年轻代和老年代的内存比例。
-XX:MaxHeapSize
: 指定程序最大内存。
-XX:+PrintGC
: 打印GC日志。
-XX:+PrintGCDetails
: 打印详细的GC日志。
-Xloggc
: 打印GC日志保存位置。
7.3. 垃圾回收算法。
引用计数算法
:该算法对每一个对象都有一个引用计数,没增加一次引用就+1,减少一次引用-1,在回收时将引用计数为0的对象清理掉。这种算法简单,但是无法解决循环引用的问题(比如: A引用B, B也引用A,但是A和B都没有被其它任何对象引用)。
标记-清除算法
:该算法分为两个阶段, 第一阶段遍历找出所有需要被回收的对象,并做上标记,第二阶段对清理所有被标记的对象,这种算法效率比较低,并且会产生较多的内存碎片。
标记-整理算法
:该算法的第一阶段和标记-清除算法是一样的,而第二阶段它不是直接清理掉垃圾对象,而且将存活的对象往同一侧移动,移动完成后清理掉另一侧所有的对象。这种算法不会产生内存碎片,但是效率低下。
复制算法
:该算法将内存分为两个区域,进行垃圾回收时,就将还活着的对象复制到另一块内存区域中,然后再将整片内存区域清空。这种算法简单快速,而且不会产生内存碎片,但是因为将内存分成两块,所以可用的内存会少很多。
分代收集算法
:将内存细分为多个区域,不同区域GC的频率,并对不同的区域采用适当的收集算法。如JVM将内存分为年轻代和老年代,普通对象最开始分配在年轻代(大对象会直接分配到老年代),同一个对象在经过几次GC后还存活着,就认为这个对象的生命周期会比较长,将其移入老年代,GC主要发生在年轻代。
7.4 类加载机制。
Java中主要有
Bootstrap类加载器
、ExtClassLoader
、AppClassLoader
,其中Bootstrap类加载器
主要加载JAVA_HOME/lib
目录下的类库,ExtClassLoader
加载JAVA_HOME/lib/ext
目录下的类库,AppClassLoader
加载classpath
指向目录下的类库。Java的类加载器使用
双亲委派模型
,除了顶层的Bootstrap类加载器
外,其余的类加载器都有父类加载器,当一个类加载器要加载一个类时,它不会直接去加载,而是委托父类加载器尝试加载,父类加载器如果无法完成,则继续委托其父类加载器加载,如果在期间有某一个类加载器发现已经加载过这个类,则会将已经加载的类返回,子类不再加载。若所有的类加载器都未加载过这个类,那么最开始尝试加载的加载器才会去加载这个类。使用这样的加载机制的好处是: 对于同一个类,如:java.lang.String
,能保证整个程序中都是使用的这一个类,否则如果用户在自己的项目中也写了一个java.lang.String
类,那么项目中将存在两个String类,一个是java提供的String类,一个是用户自定义的String类,不仅使项目变得混乱,而且不安全。
8. MyBatis和Hibernate各有什么优缺点?
我个人因为只简单接触过而没有实际应用过Hibernate
,所以没能从比较好的角度来回答这个问题。
Hibernate
的优点是它是一个完全的ORM框架
,使用Hibernate
可以做到不用手写SQL,而且无须关心使用何种数据库,可移植性较好,当需要更变数据库时需要做的修改很少甚至为0。其缺点是需要根据数据库的设计在实体进行又一次的配置,且帮程序员做了太多事,如果需要进行调优的话需要对Hibernate
有比较深的了解。MyBatis
的优缺点差不多和Hibernate
相反,我们需要手写SQL
语句和配置结果集和实体类的映射
,即使是简单的单表操作也需要写SQL(可以通过拦截器
来实现CommonMapper,或者可以使用生成器来生成代码),因此MyBatis
要进行SQL调优也简单直接。其次是MyBatis
的二级缓存
功能较弱,是针对namespace
的。
9. MySQL平时是怎么分析效率和进行SQL优化的?
较常使用的方法是
explain SQL
查看执行计划,根据查询计划可以知道是否使用了索引
,是否进行来全表扫描
以及查询的顺序,依此我们可以建立适当的索引和连接查询调优。
还有一个是开启慢查询记录执行时间长的SQL语句。
通常会在
WHERE
、JOIN ON
和ORDER BY
使用到字段上加上索引。避免查询时判断
NULL
,否则可能会导致全表扫描。避免使用
OR
来连接查询条件,否则可能导致全表扫描,可以改用UNION
或UNION ALL
。避免
LIKE
查询,否则可能导致全表扫描。不使用
SELECT *
,只查询必须的字段,避免加载无用数据。能用
UNION ALL
的时候就不用UNION
,UNION
过滤重复数据要耗费更多的cpu资源。
10. MySQL除了InnoDB还有哪些引擎,有什么区别?
因为平时都是用的
InnoDB
,对其它引擎的了解甚少,所以这个问题没答上= =,这里直接贴一个链接好了。
相关链接: MySQL存储引擎介绍
11. 如何动态改变页面上的元素?
使用
$(..).css({..})
来改变元素的样式。使用
$(..).attr(..)
改变元素的属性。使用
$(..).html(..)
改变元素的html内容。使用
$(..).text(..)
改变元素的文本内容。使用
$(..).remove(..)
删除元素。使用
$(..).append(..)
添加元素。
12. 分页的实现。
如果是使用
JSP
等后端模板的话,一般会将需要分页的JSP代码
抽成一个单独的JSP文件
,并在页面中动态计算分页按钮的展示方式,在母页中include
该JSP文件
,然后在前端点击分页按钮时,通过AJAX
请求下一页的内容,服务器端将渲染后的HTML
返回给前端,前端通过$(..).html()
等方式替换展示内容。
如果是在前后端分离的项目中,一般会使用一些前端的框架,如:React.js
、Vue.js
等,每次只向后台请求分页的数据,一般数据交互格式使用JSON
,并替换已有的数据,触发页面内容的改变。
13. 解释一下RESTful,平时是怎么用的
RESTful
是无状态的,采用URL
+HTTP请求方法
来描述资源
和行为
。
一般在前后端分离的项目中,后端会提供REST接口
给前端,其HTTP请求方法
一般为:
GET
: 获取资源。
POST
: 更新资源。
PUT
: 创建资源。
DELETE
: 删除资源。其次,
RESTful
由于是无状态的,一般会采用JWT
或OAuth
的方式来认证一个用户,Token
是保存在前端的,为了安全性一般会配合HTTPS
使用。
14. 有没有抓过包?GET和POST格式是怎么样的?
HTTP请求
分为三部分:请求行
、请求头
、请求体
:请求行
: 第一行是METHOD URL protocal
,如GET http://abc.com HTTP/1.1
。请求头
: 从第二行开始,每一行的内容都是一个请求头参数值,直到遇到一个空行
为止。请求体
: 请求头和请求体中间隔着一行空行
作为分界,请求体包含着本次请求携带的内容。GET
方式的请求没有请求体,其是将参数追加到URL
后面,URL
中?
后面的内容为请求参数。POST
方式则三部分都有,且POST
的请求头应当包含Content-Type
来指明请求体中内容的类型。
15. 如果是上传文件的话又是怎样的?后端如何处理?
上传文件的话会设置
Content-Type
为multipart/form-data
,并指定boundary
的值来标识请求体中内容的分界,而在请求体中,不同的内容(如:文件A和文件B)之间使用boundary
的值来标识分界,并且请求体中每部分内容都会有Content-Disposition
和Content-Type
来指明这部分内容的类型和信息。后端的话使用
ServletFileUpload
来解析请求,获得FileItem
的List
,遍历Item
,然后通过Item
获得输入流,从输入流中读取上传文件的数据,再构建FileOutputStream
输出到磁盘中保存。
如果使用Spring MVC
,则可以在接收请求的方法中接收CommonsMultipartFile
,并使用transferTo
方法保存到磁盘中。
16. AJAX实现原理。
这个也没答上来,平时都是使用jQuery
封装的AJAX
或者其他AJAX
框架。
AJAX是利用浏览器的AJAX引擎来实现的异步请求,通过
XMLHttpRequest
对象来发送请求,由AJAX引擎向服务器发送和接收响应,再回调给用户处理,达到不阻塞用户界面和无刷新的目的。
资料来源: AJAX工作原理及其优缺点。
17. Linux下怎么查找一个文件?
如果是查找二进制文件,可以使用
whereis
。
如果是查找命令,可以使用which
。
如果是其他文件,可以使用find
命令(其实什么都可以找),-name
指定搜索的名称或者匹配串,-maxdepth
指定搜索的深度。
也可以使用locate
命令查找,但是最新变动的文件可能会找不到,因为该命令实际上是搜索数据库,该数据库每天自动更新,可以手动执行updatedb
更新。
18. Linux怎么查找一个进程?
使用
ps
命令可以查看进程状态,ps -ef
查看所有进程,配合grep
命令可以进行筛选, 如查看tomcat
进程的命令是:ps -ef | grep tomcat
。
19. Linux下只知道文件所在目录和内容,如何查找文件?
这题没也没答上= =
使用
grep
命令可以实现:grep -rn /path/to/target/dir -e "pattern"
-r
: 递归-n
: 显示行数-w
: 完全匹配
例子:grep -rn. -e "ERROR"
输出:./面试:143:输出: ./面试:144:上面的命令搜索当前目录及其子目录中的文件,并输出含有ERROR内容的行
详细答案及来源:How to find all files containing specific text on Linux?
20. Redis你用它来做什么?
Redis
在我接触过的项目中主要做了两件事:
缓存
。
存储需要计算的信息
。
Redis
也可以用来做消息订阅
、队列
等。
21. Redis如果运行过程中崩溃了怎么办。
这个问题我估摸着面试官想问的是Redis
的数据保障的方法,不然崩溃了除了重启还能怎么办?
Redis
有提供数据持久化的功能,一种是快照
,一种是AOF
。快照
是在某一个时间点将所有数据写入到磁盘中,AOF
是将被执行的命令复制到硬盘中,快照
的文件体积要比AOF
的文件体积小。前者在恢复时速度
比后者快,但是因为是间隔持久化
,所以会有一定量的数据丢失
。后者因为是实时写入
的,所以数据的完整性比较好,如果丢失的话一般也就丢失一秒的数据。其次需要做主从复制,这样一份数据可以保存在多台服务器上,且可以避免
Redis
崩溃到重启完成这段时间内无法提供正常服务,同时从服务器可以分担主服务器的读压力。
22. Redis集群。
没配置过所以没答上= =
相关连接: Redis集群教程。
23. 剩下的问题都是偏向个人的,没有什么通用性的回答,大概问了我:
前一家公司实习的时候主要做什么?
讲一下做过的项目?
项目中有没有遇到什么难点?怎么解决的?
有没有做过什么有亮点的东西?
其中在问题3根据我的回答,继续将情况复杂化让我给出解决方案,一步一步问。
最后
最后. 如果不是熟悉的技术真的不要往简历上写= = 我因为在项目需要,学习过Android,但是项目完成后就
没碰过了(近一年),把对Android有一定的了解写上简历,结果问了三个问题就答不上了= =
虽然最终拿到了offer,但是因为各方面原因,最后还是放弃了,在此也提醒一下,秋招千万不要错过= =,拖到这个时候,好的实习的真不好找(成都)。
学习技术不能知其然而不知其所以然,日后不仅会持续更新面试内容,同时本专栏会持续发布Java
、数据库
、Linux
、算法
等方面的学习文章。欢迎关注。
最后,如有错误或不适合的,请大家评论点出,共同进步,谢谢!