中軟
無筆試,問的問題都很基礎灸异,但是自己都沒答好府适,腦殼是懵的。一剛開始答得不對绎狭,面試官也不多做解釋细溅,越答越沒信心。
1儡嘶、equals 和 hashCode 的區(qū)別喇聊。
我答成了 String 的 == 與 equals 的區(qū)別。
... ...
變形問題:Set/HashMap 是如何去重的?
Object 的 equals 方法默認是兩個對象的引用的比較蹦狂,和 == 是一樣的誓篱,意思就是指向同一內存,地址則相等凯楔,否則不相等窜骄。
public boolean equals(Object obj) {
return (this == obj);
}
Object 的 hashCode 是一個本地方法衣赶。
public native int hashCode();
This is typically implemented by converting the internal address of the object into an
integer, but this implementation technique is not required by the Java? programming language.
這句話看得我有點懵欧穴,Java 中的默認 hashCode 到底是不是地址轉換出來的妨退,有很多人把默認 toString 打印出來的 @ 后面當做地址畏梆。還是說默認是地址,但不是必須這樣實現趾娃?我覺得應該是后者扔水,默認就是地址舞骆,所以大家都不一樣廷没,但是允許重寫糊饱。不管一不一樣,Hash 取模就會有沖突颠黎。
總結: equals 比較兩個對象是否相等另锋,默認是比較地址,可以重寫狭归,應該具有對稱性夭坪、反射性、傳遞性过椎、一致性台舱。比如 String 重寫后是比較值。
hashCode 主要用于 hash 計算,不同對象的 hashCode 可以相同竞惋。要求 equals 為 true 的兩個對象的 hashCode 一定相等。通常重寫 equals 方法也要重寫 hashCode 方法灰嫉。
有人就喜歡鉆牛角尖拆宛,說不一定要重寫 hashCode 。是的讼撒,是不一定浑厚,因為 hashCode 主要用于 set/map 集合判重邏輯,只要你保證不用于 set/map 集合根盒,不重寫也不會有問題钳幅。
對于變形題目,Set/Map 怎么判重的:HashSet 的內部是實現 HashMap炎滞。
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
重新計算 hash 值:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
把 key 的 hashCode 高16位低16位進行異或敢艰,目的是為了保證高16位也參與運算,減少沖突册赛。
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}
這是1.8版本的钠导,hash 取模看對應桶是否為空森瘪,為空直接加入牡属;不為空從第一個開始比較 hash 值是否相同,再比較地址是否相同或者 key != null 比較 equals扼睬。
1.8 加入了紅黑樹逮栅,所以這里有個節(jié)點類型的判斷。
2窗宇、Spring MVC 的執(zhí)行過程措伐。
雖然源碼自己也看了很多遍,還是說的不利索担映。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 根據請求的 url 找 Handler废士,返回一個 HandlerExecutionChain。
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
// 沒找到蝇完,發(fā)出異常
noHandlerFound(processedRequest, response);
return;
}
// 為 handler 找 adapter 適配器 官硝。
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 循環(huán)執(zhí)行 pre 攔截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 真正調用 handler,返回 modelAndView短蜕,如果使用 @RestController 注解氢架,返回空.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
// 執(zhí)行 post 攔截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 渲染 view,返回結果
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
簡單的說:找 handler朋魔、找 adaptor岖研、執(zhí)行 pre 攔截器、調用 handler 返回 modelAndView、執(zhí)行 post 攔截器孙援、渲染 view害淤。
(1) 客戶端通過url發(fā)送請求
(2-3) DispatcherServlet 接收到請求,通過 HandlerMapping 到對應的handler拓售,并將 URI 映射的控制器 controller 組成處理器執(zhí)行鏈返回給 Dispatcher Servlet窥摄。
(4) 找 HandlerAdapter
(5-7)由找到的 HandlerAdapter 調用相應的 Handler 進行處理并返回 ModelAndView 給 DispatcherServlet
(8-9)DispatcherServlet 將獲取的 ModelAndView 傳遞給視圖解析器解析,返回具體 View
(10)DispatcherServlet 對 View 進行渲染視圖(即將模型數據填充至視圖中)
(11)將頁面響應給用戶
組件:
DispatcherServlet:前端控制器
用戶請求到達前端控制器础淤,它就相當于mvc模式中的c崭放,DispatcherServlet 是整個流程控制的中心,由它調用其它組件處理用戶的請求鸽凶,DispatcherServlet 的存在降低了組件之間的耦合性币砂。HandlerMapping:處理器映射器
HandlerMapping 負責根據用戶請求 URL 找到 Handler 即處理器,Spring MVC 提供了不同的映射器實現不同的映射方式玻侥,例如:配置文件方式决摧,實現接口方式,注解方式等使碾。Handler:處理器
Handler 是繼 DispatcherServlet 前端控制器的后端控制器蜜徽,在 DispatcherServlet的控制下Handler對具體的用戶請求進行處理。由于 Handler 涉及到具體的用戶業(yè)務請求票摇,所以一般情況需要程序員根據業(yè)務需求開發(fā) Handler拘鞋。HandlerAdapter:處理器適配器
通過 HandlerAdapter 對處理器進行執(zhí)行,這是適配器模式的應用矢门,通過擴展適配器可以對更多類型的處理器進行執(zhí)行盆色。ViewResolver:視圖解析器
View Resolver 負責將處理結果生成 View 視圖,View Resolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址祟剔,再生成 View 視圖對象隔躲,最后對 View 進行渲染將處理結果通過頁面展示給用戶。View:視圖
springmvc框架提供了很多的View視圖類型的支持物延,包括:jstlView宣旱、freemarkerView、pdfView等叛薯。我們最常用的視圖就是jsp浑吟。
一般情況下需要通過頁面標簽或頁面模版技術將模型數據通過頁面展示給用戶,需要由程序員根據業(yè)務需求開發(fā)具體的頁面耗溜。
3组力、HashMap 先擴容還是先轉紅黑樹。
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final int TREEIFY_THRESHOLD = 8;
static final int MIN_TREEIFY_CAPACITY = 64;
static final float DEFAULT_LOAD_FACTOR = 0.75f;
初始化桶大小16抖拴,轉紅黑樹有兩個條件燎字,一個是沖突的 key 超過8個,并且桶的數量大于等于64。0.75是負載因子候衍,當前有0.75的桶都有值了笼蛛,就會進行擴容,擴大為當前的2倍脱柱,再進行 reHash伐弹。
所以對于這道題,判斷沖突的key大于等于8個時榨为,嘗試轉紅黑樹,如果桶的數量小于64煌茴,則擴容随闺;否則轉紅黑樹。
for (int binCount = 0; ; ++binCount) {
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
binCount 從 0 開始的蔓腐,所以要減 1矩乐。
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
else if ((e = tab[index = (n - 1) & hash]) != null) {
TreeNode<K,V> hd = null, tl = null;
do {
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
} while ((e = e.next) != null);
if ((tab[index] = hd) != null)
hd.treeify(tab);
}
}
這是轉紅黑樹的方法,先判斷桶的大小回论,小于 64 就擴容散罕。
4、@Controller 與 @RestController
@RestController 注解相當于@ResponseBody + @Controller合在一起的作用傀蓉。
如果只是使用 @RestController 注解 Controller欧漱,則 Controller 中的方法無法返回 jsp 頁面,或者 html葬燎,在上面 Spring MVC 調用過程中 adapter 調用 handler 的時候 modelAndView 就會返回 null误甚,直接把 return 的內容放到 response 里了。
如果需要返回到指定頁面谱净,則需要用 @Controller配合視圖解析器InternalResourceViewResolver才行窑邦。
如果需要返回JSON,XML或自定義mediaType內容到頁面壕探,則需要在對應的方法上加上@ResponseBody注解冈钦。
5、Spring Cloud 有哪些組件李请?
Eureka
Ribbon
Feign
Config
Hystrix
Hystrix DashBoard
Bus
Data Stream
6瞧筛、JVM 內存模型
不要答成運行時數據區(qū)域了。
內存模型
下面這個是運行時數據區(qū)域:
虛擬機
7捻艳、ConcurrentHashMap
ConcurrentHashMap
ConcurrentHashMap 和 HashMap 實現上類似驾窟,最主要的差別是 ConcurrentHashMap 采用了分段鎖(Segment),每個分段鎖維護著幾個桶(HashEntry)认轨,多個線程可以同時訪問不同分段鎖上的桶绅络,從而使其并發(fā)度更高(并發(fā)度就是 Segment 的個數)。
1.8 采用 CAS。
8恩急、Mysql 查詢優(yōu)化
EXPLAIN
9杉畜、InnoDB 與 MyISAM 的區(qū)別。
對 MyISAM 不是很了解衷恭。
主要區(qū)別是 MyISAM 不支持事務此叠。
MyISAM
10、volatile 有什么作用随珠,是不是原子性灭袁?
保證可見性: 修飾變量使其修改之后能夠立馬被其他線程看見。原理是 volatile 修飾的變量讀取時必須從主存中獲取窗看,修改后要立馬寫入主存茸歧,從而保證了修改之后能夠立馬被其他線程看見。
保證有序性:防止編譯器重排显沈。會加入一個內存屏障软瞎,防止后面的執(zhí)行排到前面。
不保證原子性:單一的讀/寫是具備原子性的拉讯,復合操作涤浇,比如 i++ 不具備原子性。