深圳市專業(yè)制作網(wǎng)站公司嗎互聯(lián)網(wǎng)廣告代理
工作經(jīng)驗一年以上程序員必問問題
面試題概述
- 問題為在負責項目時遇到的棘手問題及解決方法,主要考察開發(fā)經(jīng)驗與技術(shù)水平,回答不佳會影響面試印象。
- 提供四個回答方向,準備其中一個方向即可。
1、設(shè)計模式應用方向
- 以登錄為例,未用設(shè)計模式時登錄邏輯在一個業(yè)務(wù)類中,需求變更(如登錄方式增減或更換)需頻繁修改業(yè)務(wù)層代碼。
- 采用工廠設(shè)計模式和策略模式后,解決了頻繁修改業(yè)務(wù)層代碼的問題,具體實現(xiàn)可參考相關(guān)案例,其他設(shè)計模式介紹方式類似。
2、線上 bug 方向
- 項目上線后可能出現(xiàn)測試環(huán)境未暴露的問題,如運行一段時間后 CPU 飆高、內(nèi)存泄露、線程死鎖等,線上調(diào)試困難。
- 介紹時需按問題、解決過程、最終解決方案的邏輯進行,可參考 DVM 和多線程課程案例。
3、調(diào)優(yōu)方向
- 調(diào)優(yōu)時最好給出指標數(shù)據(jù),如接口調(diào)優(yōu)前后的訪問耗時。
- 重點講述調(diào)優(yōu)中間過程,可涉及 SQL 優(yōu)化(加索引)、添加緩存、采用集群或高可用方案等。
4、組件封裝方向
- 分布式鎖和接口冪等可封裝為小型工具或組件供多項目使用,支付和事務(wù)可封裝為通用服務(wù),但難度較高且需考慮高可用和通用性。
- 有經(jīng)驗可詳細講述,無經(jīng)驗可參考網(wǎng)上文章,實在想不到可從其他三個方向入手。
總結(jié)強調(diào)
- 此面試題高頻出現(xiàn),需提前準備,選擇一個方向深入準備能體現(xiàn)技術(shù)水平。
具體問題
設(shè)計模式應用案例
在一個電商項目的用戶登錄功能開發(fā)中,最初的設(shè)計非常簡單直接。登錄邏輯全部集中在一個業(yè)務(wù)類 UserLoginService
中,代碼如下:
public class UserLoginService {public boolean login(String username, String password) {// 直接在該方法中進行數(shù)據(jù)庫查詢驗證if ("admin".equals(username) && "123456".equals(password)) {return true;}return false;}
}
隨著業(yè)務(wù)發(fā)展,需要支持多種登錄方式,如手機號驗證碼登錄、第三方平臺登錄(微信、支付寶)等。每次增加新的登錄方式,都需要在 login
方法中添加大量的條件判斷邏輯,導致代碼越來越臃腫,維護成本急劇上升。例如添加手機號驗證碼登錄:
public class UserLoginService {public boolean login(String identifier, String credential) {if (identifier.matches("^1[3 - 9]\\d{9}$")) {// 手機號驗證碼登錄邏輯,查詢數(shù)據(jù)庫驗證驗證碼if ("validCode".equals(credential)) {return true;}} else if ("admin".equals(identifier) && "123456".equals(credential)) {// 用戶名密碼登錄邏輯return true;}return false;}
}
為解決這個問題,引入了工廠設(shè)計模式與策略模式。首先定義一個登錄策略接口 LoginStrategy
:
public interface LoginStrategy {boolean login(String identifier, String credential);
}
然后分別實現(xiàn)用戶名密碼登錄策略類 UsernamePasswordLoginStrategy
和手機號驗證碼登錄策略類 PhoneCodeLoginStrategy
:
public class UsernamePasswordLoginStrategy implements LoginStrategy {@Overridepublic boolean login(String username, String password) {// 實際數(shù)據(jù)庫查詢驗證邏輯if ("admin".equals(username) && "123456".equals(password)) {return true;}return false;}
}
public class PhoneCodeLoginStrategy implements LoginStrategy {@Overridepublic boolean login(String phone, String code) {// 實際數(shù)據(jù)庫查詢驗證邏輯if ("13800138000".equals(phone) && "validCode".equals(code)) {return true;}return false;}
}
接著創(chuàng)建一個登錄策略工廠類 LoginStrategyFactory
:
public class LoginStrategyFactory {public static LoginStrategy getLoginStrategy(String loginType) {if ("usernamePassword".equals(loginType)) {return new UsernamePasswordLoginStrategy();} else if ("phoneCode".equals(loginType)) {return new PhoneCodeLoginStrategy();}return null;}
}
最后,修改業(yè)務(wù)類 UserLoginService
,通過工廠獲取相應的登錄策略:
public class UserLoginService {public boolean login(String loginType, String identifier, String credential) {LoginStrategy strategy = LoginStrategyFactory.getLoginStrategy(loginType);if (strategy!= null) {return strategy.login(identifier, credential);}return false;}
}
通過這種方式,當需要添加新的登錄方式時,只需要創(chuàng)建新的策略類并在工廠類中添加相應的獲取邏輯,無需修改業(yè)務(wù)層的核心代碼,大大提高了代碼的擴展性和維護性。
線上 bug 處理案例
在一個大型的在線教育平臺項目上線一段時間后,運維人員反饋系統(tǒng)出現(xiàn) CPU 使用率持續(xù)飆高的情況,導致部分課程直播卡頓,嚴重影響用戶體驗。由于線上環(huán)境復雜,難以在測試環(huán)境復現(xiàn)相同問題,增加了調(diào)試難度。
首先,利用 jstack
命令獲取當前 Java 進程的線程堆棧信息,通過分析堆棧信息,發(fā)現(xiàn)有一個線程一直在執(zhí)行某個方法 calculateCourseScore
,該方法用于計算課程的綜合得分,代碼如下:
public class CourseScoreCalculator {public double calculateCourseScore(List<StudentAnswer> answers) {double totalScore = 0;for (StudentAnswer answer : answers) {// 復雜的計算邏輯,包含多層嵌套循環(huán)for (int i = 0; i < answer.getOptions().size(); i++) {for (int j = 0; j < answer.getOptions().get(i).getSubOptions().size(); j++) {totalScore += answer.getOptions().get(i).getSubOptions().get(j).getScore();}}}return totalScore;}
}
從代碼邏輯上看,多層嵌套循環(huán)導致計算量過大,在高并發(fā)情況下容易使 CPU 使用率飆升。針對這個問題,對計算邏輯進行了優(yōu)化。通過減少不必要的循環(huán)嵌套,將部分重復計算的邏輯提取出來,優(yōu)化后的代碼如下:
public class CourseScoreCalculator {public double calculateCourseScore(List<StudentAnswer> answers) {double totalScore = 0;for (StudentAnswer answer : answers) {List<Option> options = answer.getOptions();for (Option option : options) {List<SubOption> subOptions = option.getSubOptions();for (SubOption subOption : subOptions) {totalScore += subOption.getScore();}}}return totalScore;}
}
同時,監(jiān)控系統(tǒng)資源,發(fā)現(xiàn)數(shù)據(jù)庫查詢操作也占用了較多資源。通過對數(shù)據(jù)庫查詢語句進行分析,發(fā)現(xiàn)部分查詢沒有使用合適的索引。例如,在查詢學生課程學習記錄時,原 SQL 語句為:
SELECT * FROM student_course_record WHERE student_id = 12345;
表 student_course_record
數(shù)據(jù)量較大,而 student_id
字段沒有添加索引,導致查詢效率低下。為 student_id
字段添加索引:
CREATE INDEX idx_student_id ON student_course_record(student_id);
經(jīng)過這些優(yōu)化措施,再次監(jiān)控系統(tǒng),CPU 使用率恢復到正常水平,線上直播卡頓問題得到解決。
系統(tǒng)調(diào)優(yōu)案例
在一個電商訂單系統(tǒng)中,有一個查詢訂單詳情的接口 getOrderDetail
,調(diào)優(yōu)前該接口的平均響應時間為 2 秒,嚴重影響用戶體驗。為了提升系統(tǒng)性能,對該接口進行了優(yōu)化。
首先,對接口涉及的 SQL 查詢語句進行分析。原 SQL 語句為:
SELECT * FROM orders
JOIN order_items ON orders.order_id = order_items.order_id
JOIN products ON order_items.product_id = products.product_id
WHERE orders.order_id = 12345;
通過 EXPLAIN
關(guān)鍵字分析查詢執(zhí)行計劃,發(fā)現(xiàn) JOIN
操作效率較低,因為相關(guān)表沒有合適的索引。為 orders
表的 order_id
字段、order_items
表的 order_id
和 product_id
字段、products
表的 product_id
字段添加索引:
CREATE INDEX idx_order_id_orders ON orders(order_id);
CREATE INDEX idx_order_id_order_items ON order_items(order_id);
CREATE INDEX idx_product_id_order_items ON order_items(product_id);
CREATE INDEX idx_product_id_products ON products(product_id);
經(jīng)過索引優(yōu)化后,SQL 查詢效率得到顯著提升。同時,考慮到訂單數(shù)據(jù)相對穩(wěn)定,添加緩存機制。使用 Redis 作為緩存,在查詢訂單詳情時,首先從 Redis 中查詢是否有緩存數(shù)據(jù),如果有則直接返回,避免了數(shù)據(jù)庫查詢。代碼實現(xiàn)如下:
public OrderDetail getOrderDetail(long orderId) {OrderDetail orderDetail = (OrderDetail) redisTemplate.opsForValue().get("order:" + orderId);if (orderDetail!= null) {return orderDetail;}// 如果緩存中沒有,從數(shù)據(jù)庫查詢orderDetail = orderDao.getOrderDetail(orderId);if (orderDetail!= null) {redisTemplate.opsForValue().set("order:" + orderId, orderDetail);}return orderDetail;
}
經(jīng)過上述優(yōu)化,該接口的平均響應時間從 2 秒縮短到了 500ms,系統(tǒng)性能得到了大幅提升。
組件封裝案例
- 分布式鎖封裝
在一個分布式電商庫存扣減系統(tǒng)中,為了保證庫存扣減的原子性,避免超賣現(xiàn)象,需要使用分布式鎖。封裝了一個簡單的分布式鎖工具類DistributedLockUtil
,使用 Redis 實現(xiàn)分布式鎖。代碼如下:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;@Component
public class DistributedLockUtil {@Autowiredprivate RedisTemplate<String, String> redisTemplate;public boolean tryLock(String lockKey, String requestId, int expireTime) {return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS);}public void unlock(String lockKey, String requestId) {if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) {redisTemplate.delete(lockKey);}}
}
在庫存扣減業(yè)務(wù)代碼中使用該分布式鎖:
public class StockService {@Autowiredprivate DistributedLockUtil distributedLockUtil;public boolean deductStock(long productId, int quantity) {String lockKey = "stock:" + productId;String requestId = UUID.randomUUID().toString();if (distributedLockUtil.tryLock(lockKey, requestId, 10)) {try {// 檢查庫存并扣減int stock = stockDao.getStock(productId);if (stock >= quantity) {stockDao.deductStock(productId, quantity);return true;}} finally {distributedLockUtil.unlock(lockKey, requestId);}}return false;}
}
這個分布式鎖工具類可以在多個電商相關(guān)項目中復用,保證了分布式環(huán)境下關(guān)鍵業(yè)務(wù)的一致性。
- 支付通用服務(wù)封裝
在一個綜合性電商平臺項目中,涉及多種支付方式,如微信支付、支付寶支付、銀行卡支付等。為了提高代碼的復用性和系統(tǒng)的可維護性,封裝了一個支付通用服務(wù)PaymentService
。
首先定義一個支付接口 PaymentProcessor
:
public interface PaymentProcessor {String processPayment(PaymentRequest request);
}
然后分別實現(xiàn)微信支付處理器 WeChatPaymentProcessor
、支付寶支付處理器 AlipayPaymentProcessor
和銀行卡支付處理器 BankCardPaymentProcessor
:
public class WeChatPaymentProcessor implements PaymentProcessor {@Overridepublic String processPayment(PaymentRequest request) {// 調(diào)用微信支付 API 進行支付處理// 返回支付結(jié)果return "微信支付成功";}
}
public class AlipayPaymentProcessor implements PaymentProcessor {@Overridepublic String processPayment(PaymentRequest request) {// 調(diào)用支付寶支付 API 進行支付處理// 返回支付結(jié)果return "支付寶支付成功";}
}
public class BankCardPaymentProcessor implements PaymentProcessor {@Overridepublic String processPayment(PaymentRequest request) {// 調(diào)用銀行卡支付 API 進行支付處理// 返回支付結(jié)果return "銀行卡支付成功";}
}
接著創(chuàng)建支付服務(wù)類 PaymentService
,通過策略模式選擇具體的支付處理器:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;@Service
public class PaymentService {private Map<String, PaymentProcessor> paymentProcessorMap = new HashMap<>();@Autowiredpublic PaymentService(WeChatPaymentProcessor weChatPaymentProcessor,AlipayPaymentProcessor alipayPaymentProcessor,BankCardPaymentProcessor bankCardPaymentProcessor) {paymentProcessorMap.put("wechat", weChatPaymentProcessor);paymentProcessorMap.put("alipay", alipayPaymentProcessor);paymentProcessorMap.put("bankCard", bankCardPaymentProcessor);}public String processPayment(PaymentRequest request) {String paymentType = request.getPaymentType();PaymentProcessor processor = paymentProcessorMap.get(paymentType);if (processor!= null) {return processor.processPayment(request);}return "不支持的支付方式";}
}
在訂單支付業(yè)務(wù)中使用該支付服務(wù):
public class OrderService {@Autowiredprivate PaymentService paymentService;public String payOrder(Order order) {PaymentRequest request = new PaymentRequest();request.setPaymentType(order.getPaymentType());request.setAmount(order.getTotalAmount());return paymentService.processPayment(request);}
}
通過這種方式,將支付相關(guān)的復雜邏輯封裝在 PaymentService
中,其他業(yè)務(wù)模塊只需調(diào)用該服務(wù)即可,提高了系統(tǒng)的通用性和可維護性,同時也方便擴展新的支付方式。
總結(jié)
設(shè)計模式應用
- 主要內(nèi)容:電商項目用戶登錄功能,從初始邏輯集中導致維護困難,到引入工廠與策略模式解決問題。
- 核心概念:工廠設(shè)計模式用于創(chuàng)建對象,策略模式將算法邏輯封裝。
- 關(guān)鍵知識點:理解兩種設(shè)計模式作用,明白如何結(jié)合使用提高代碼擴展性與維護性。
總結(jié):
- 設(shè)計模式可優(yōu)化代碼結(jié)構(gòu),解決業(yè)務(wù)變更時代碼頻繁修改問題。
- 工廠模式負責對象創(chuàng)建,策略模式處理不同業(yè)務(wù)邏輯實現(xiàn),二者結(jié)合使代碼更靈活。
線上 bug 處理
- 主要內(nèi)容:在線教育平臺上線后 CPU 飆高,通過分析線程堆棧和 SQL 語句定位并解決問題。
- 核心概念:利用工具獲取線程堆棧信息輔助定位問題,關(guān)注數(shù)據(jù)庫索引對查詢性能影響。
- 關(guān)鍵知識點:掌握獲取和分析線程堆棧方法,了解索引優(yōu)化 SQL 查詢原理。
總結(jié):
- 線上 bug 因環(huán)境復雜難調(diào)試,需借助工具精準定位。
- 優(yōu)化代碼邏輯和數(shù)據(jù)庫查詢,可有效解決性能問題。
系統(tǒng)調(diào)優(yōu)
- 主要內(nèi)容:電商訂單系統(tǒng)查詢訂單詳情接口,從 2 秒響應優(yōu)化到 500ms,通過 SQL 優(yōu)化和緩存實現(xiàn)。
- 核心概念:使用
EXPLAIN
分析查詢執(zhí)行計劃,運用緩存減少數(shù)據(jù)庫查詢壓力。 - 關(guān)鍵知識點:學會分析查詢執(zhí)行計劃,掌握緩存機制及應用場景。
總結(jié):
- 系統(tǒng)調(diào)優(yōu)需關(guān)注數(shù)據(jù)庫查詢和緩存策略,以提升接口響應速度。
- 索引優(yōu)化可提高 SQL 查詢效率,緩存能快速返回常用數(shù)據(jù)。
組件封裝
-
分布式鎖封裝
- 主要內(nèi)容:電商庫存扣減系統(tǒng)使用分布式鎖保證原子性,封裝基于 Redis 的分布式鎖工具。
- 核心概念:分布式鎖確保分布式環(huán)境下操作一致性,Redis 提供實現(xiàn)方式。
- 關(guān)鍵知識點:理解分布式鎖原理,掌握基于 Redis 實現(xiàn)分布式鎖方法。
-
總結(jié):
- 分布式鎖用于解決分布式場景下數(shù)據(jù)一致性問題。
- Redis 的
setIfAbsent
等操作可實現(xiàn)分布式鎖基本功能。
-
支付通用服務(wù)封裝
- 主要內(nèi)容:電商平臺多種支付方式,通過封裝支付通用服務(wù)提高復用性與可維護性。
- 核心概念:利用策略模式實現(xiàn)不同支付方式處理,封裝復雜支付邏輯。
- 關(guān)鍵知識點:理解策略模式應用,學會封裝通用服務(wù)。
-
總結(jié):
- 策略模式可根據(jù)不同條件選擇不同支付處理器。
- 封裝通用服務(wù)能提升代碼復用,便于維護和擴展新支付方式。