背景
外包就外包吧, 好歹也是一个面试机会, 还是认真对待吧.
前置说明
不一定是标准答案, 都是自己此时此刻的一些思考和理解. 请谅解~
八股文:
如何设计一个高并发对外的接口
下面是我自己的思考, 不一定是标准答案:
换一句复杂的话说, 也是自己对问题的理解: 也就是系统/接口设计的初期, 业务方/需求方已经可以预测这个接口可能会有大量的请求去访问, 如何保证接口的高可用?
更多可能是系统和架构方面的设计.
缓存: 首先分析这个接口返回的数据, 修改的频率时候会很高. 这些数据数据是否适合做缓存. 如果适合被缓存, DB的压力接回被缓解很多.
单一责任链: 设计这个接口应该让它处理的事情尽量简单. 一些不太重要的事情, 是否可以通过异步的方式去处理.
消息队列: 消息队列核心的用途大概就是, 解耦, 削峰, 限流. 上面异步的方式, 也可以接入消息队列, 达到解耦的目的.
架构方面: 用微服务架构, 有完善的熔断, 降级策略.
测试: 做好接口的压测.
Reidis数据类型和使用场景
数据类型: String, List, Hash, Sorted Set …
使用场景: 用户就是缓存和分布式锁(SetNX) set if not exit
MySQL索引区别
感觉知道索引是什么, 列举几个常用的索引就差不多了. 网上的分类有点乱.
主键索引: 就InnoDB而言, 主键索引, 叶子节点上包含了每一行的全部数据.
唯一索引: 就是约定某一个字段不能有重复值, 主键索引其实就是天然的唯一索引.
覆盖索引: 就是几个字段联合起来去建立一个索引. 如果用覆盖索引, 就尽量让查询的字段都是索引字段, 避免去回表查询.
项目用了哪种垃圾回收
看过项目的服务启动脚本, 用得是G1垃圾回收器.
G1垃圾回收器相比其他垃圾回收器最大的变化就是将堆分为了很多个Region,有一种分而治之的感觉. 但是核心的原理还是差不多. 也是分为: 年轻代, 老年代. 可以是任何一个Region扮演. 还分配了一个区域存储超大对象. Young Region经过几轮回收后, 如果依然存活, 就会晋升为老年代.
项目介绍
交代项目的背景: 乱说吧, 想到什么说什么吧.
之前有一个项目用到了这个模式: 状态模式
模板方法模式: 就是在抽象层定义一个顺序, 抽象层可以不用实现具体的方法. 让子类自己去实现.
策略模式:
// TODO 这里有空自己画一下, 研究一下.
垃圾回收器有哪些(有哪些区别)
Serile
Parillen
CMS(Corrent Mark Sweep)
G1
…
怎么判断是垃圾
通过GCRoot对象去做可达性分析.
垃圾清理的过程
之前看过一个jConsole的动态的垃圾回收的图.
Eden From To Old
内存模型
就是JMM. 为了解决多线程的程序, 操作共享变量的问题.
类加载机制
双亲委派, 向上委托. 用这个图记忆一下吧:
// TODO 有空自己画一下, 加深印象.
类加载的顺序:
// TODO 有空自己画
Mybatis底层原理
就是一个数据层的框架, 感觉会用就可以, 要自己底层原理干啥呢? 学习的意义真的不大.
为了面试, 找资料学习一下吧
我感觉就是对SQL的封装啊
// TODO 是一款优秀的框架, 可以读一读里面的源码. 有空再说吧.
synchronized ReentrantLock底层原理
synchronized 是Java内置的锁, 是Java的一个关键字, 通常用于修饰方法和类. 使用javap 命令可以查看对应的字节码文件信息, 底层是通过 monitorenter 和 monitorexit 实现的.
ReentrantLock 是并发包里面有一个Lock接口, JDK为我们提供的一种具体实现. 这个实现是基于AQS(AbstractQueuedSynchronizer)实现的.
synchronized使用起来更加方便一点, 并且不需要我们自己去解锁操作. 但是在高并发场景ReentrantLock性能会更好一些.
锁升级过程
无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
如何处理分布式事务
最好是能通过合理的拆分微服务, 去规避分布式事务的问题.
常见的解决方案: Seata, 2PC, 3PC, TCC
// TODO 对于成熟的解决方案, 其实也可以分析一下底层原理. 理论支撑 -> 具体代码实现.
设计模式:
手写一个单例模式
public class Singleton4 implements Serializable {
// 懒加载
private static volatile Singleton4 INSTANCE = null;
// 无参构造私有化
private Singleton4() {
if (INSTANCE != null) {
throw new RuntimeException("单例对象不能重复创建");
}
}
/**
* 对外提供获取该单例对象的方法
*
* @return 单例对象
*/
public static Singleton4 getInstance() {
if (INSTANCE == null) { // 第一次检查
synchronized (Singleton4.class) {
if (INSTANCE == null) { // 第二次检查
INSTANCE = new Singleton4();
}
}
}
return INSTANCE;
}
}
算法:
数组查找3个组相加为0的元素(3sum)
原理就是用两个指针去处理, 代码实现如下:
class Solution {
public static List<List<Integer>> threeSum(int[] nums) {
// 返回值
List<List<Integer>> res = new ArrayList<>();
// 参数校验: 为空或长度不足3个, 直接返回空数组
if (nums == null || nums.length < 3) {
return res;
}
int len = nums.length;
// 排序
Arrays.sort(nums);
// 遍历数组
for (int i = 0; i < len; i++) {
if (nums[i] > 0) { // 如果第一个数都大于0, 那么三数之和一定大于0
break;
}
if (i > 0 && nums[i] == nums[i-1]) {
continue;
}
int left = i + 1;
int right = len - 1;
while (left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum == 0) {
res.add(Arrays.asList(nums[i], nums[left], nums[right]));
// 重复元素的跳过
while (left < right && nums[left] == nums[left + 1]) {
left++;
}
while (left < right && nums[right] == nums[right - 1]) {
right--;
}
// 继续移动
left++;
right--;
}
// 左指针右移
if (sum < 0) {
left++;
}
// 右指针左移
if (sum > 0) {
right--;
}
}
}
return res;
}
}
二分查找算法
我也不知道是什么思维, 就是二分查找的算法, 学校感觉都学习过, 可以画一个简答的图:
public static int search(int[] nums, int target) {
// 参数校验
if (nums == null || nums.length == 0) {
return -1;
}
int len = nums.length; // 数组长度
int left = 0; // 左边界
int right = len - 1; // 右边界
while (left <= right) {
int mid = left + (right - left) / 2; // 防止溢出
if (nums[mid] == target) {
return mid;
}
if (nums[mid] > target) { // target在左边区间
right = mid - 1;
}
if (nums[mid] < target) { // target在右边区间
left = mid + 1;
}
}
return -1;
}
算法如何判断一个链表中是否有环
设置快慢指针, 示意图和代码实现如下:
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
LC2两数相加
有点躁动了, 还没看完
// TODO 后面再看吧
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = null, tail = null;
int carry = 0;
while (l1 != null || l2 != null) {
int n1 = l1 != null ? l1.val : 0;
int n2 = l2 != null ? l2.val : 0;
int sum = n1 + n2 + carry;
if (head == null) {
head = tail = new ListNode(sum % 10);
} else {
tail.next = new ListNode(sum % 10);
tail = tail.next;
}
carry = sum / 10;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
if (carry > 0) {
tail.next = new ListNode(carry);
}
return head;
}
}
输入一个string转化为数字,如果不是数字就输出0
先完成吧, 应该不是这个答案:
public static int stringToNumber(String str) {
try {
// 尝试将字符串转换为整数
return Integer.parseInt(str);
} catch (NumberFormatException e) {
// 如果转换失败,返回0
return 0;
}
}
字符串反转
感觉不可能直接让你调用API, 算了先这样吧:
public static String reverseString(String str) {
return new StringBuilder(str).reverse().toString();
}
有一个密文,解析出来是每一行的最后一个数字。
// TODO
大概是这样,比如有1,2,3,4,5,6,7,8,9,10,这10个数,第一排是1,第二排是2,3,4,第三排是5,6,7,8,9,第四排是10。那么密文就是1,4,9,10
让你实现并说出时间服杂度是多少要循环多少次
import java.util.ArrayList;
import java.util.List;
public class DecodeCipher {
public List<Integer> decode(int[] nums) {
List<Integer> result = new ArrayList<>();
if (nums == null || nums.length == 0) {
return result;
}
int row = 0; // 当前行号
int col = 0; // 当前列号
int n = nums.length;
for (int i = 0; i < n; i++) {
// 如果当前列等于行号,说明是当前行的最后一个元素
if (col == row) {
result.add(nums[i]);
}
col++;
// 准备进入下一行
if (col > row) {
row++;
col = 0;
}
}
return result;
}
// 测试方法
public static void main(String[] args) {
DecodeCipher decoder = new DecodeCipher();
int[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<Integer> result = decoder.decode(nums);
System.out.println(result); // 输出: [1, 4, 9, 10]
}
}
功能:输入阿拉伯数字,翻译成符合中文阅读习惯的中文输出
// TODO
输入:1000
输出:一千
输入范围:0~一亿正整数,不用考虑异常情况
例子:12341234,123005
import java.util.HashMap;
import java.util.Map;
public class NumberToChinese {
private static final String[] CHINESE_NUMS = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九"};
private static final String[] LEVELS = {"", "十", "百", "千", "万", "十万", "百万", "千万", "亿"};
public String numberToChinese(int num) {
if (num == 0) return "零";
return helper(num).replaceAll("亿万", "亿").replaceAll("一十", "十").replaceAll("(零)+", "零").replaceAll("零$", "");
}
private String helper(int num) {
if (num == 0) return "";
StringBuilder sb = new StringBuilder();
int level = 0;
while (num > 0) {
int remainder = num % 10;
if (remainder > 0 || sb.length() > 0) {
if (level > 0 && num % 10 == 0 && sb.length() == 0) {
sb.insert(0, CHINESE_NUMS[0]);
} else {
sb.insert(0, LEVELS[level]);
if (remainder > 0) {
sb.insert(0, CHINESE_NUMS[remainder]);
}
}
}
num /= 10;
level++;
if (level == 4) {
sb.insert(0, "亿");
level = 0;
} else if (level == 8) {
sb.insert(0, "万");
}
}
return sb.toString();
}
// 测试方法
public static void main(String[] args) {
NumberToChinese converter = new NumberToChinese();
System.out.println(converter.numberToChinese(12345)); // 输出: "一万二千三百四十五"
System.out.println(converter.numberToChinese(1000)); // 输出: "一千"
System.out.println(converter.numberToChinese(10010)); // 输出: "一万零一十"
System.out.println(converter.numberToChinese(10000000)); // 输出: "一百万"
System.out.println(converter.numberToChinese(100000000)); // 输出: "一亿"
System.out.println(converter.numberToChinese(10000010)); // 输出: "一十万零一十"
}
}
米家5,1,4,米粉2,3,5,8,然后求米家米粉最近的距离
// TODO 毅力用完了, 后面再看吧
import java.util.Arrays;
public class MinDistance {
public int findMinDistance(int[] miajia, int[] mifens) {
if (miajia == null || mifens == null || miajia.length == 0 || mifens.length == 0) {
throw new IllegalArgumentException("Input arrays cannot be null or empty.");
}
// 对两个数组进行排序
Arrays.sort(miajia);
Arrays.sort(mifens);
int minDist = Integer.MAX_VALUE;
int i = 0, j = 0;
// 使用双指针遍历两个数组
while (i < miajia.length && j < mifens.length) {
minDist = Math.min(minDist, Math.abs(miajia[i] - mifens[j]));
// 移动较小的指针以尝试缩小差距
if (miajia[i] < mifens[j]) {
i++;
} else {
j++;
}
// 如果已经找到最小可能的距离,则提前终止
if (minDist == 0) break;
}
return minDist;
}
// 测试方法
public static void main(String[] args) {
MinDistance solver = new MinDistance();
int[] miajia = {5, 1, 4};
int[] mifens = {2, 3, 5, 8};
System.out.println(solver.findMinDistance(miajia, mifens)); // 输出: 0
}
}
其他:
binlog有几种数据类型
1、问我怎么看待ai发展,然后平时工作做什么内容,异常怎么解决,遇到了那些问题,幂等,消息一致性,es基本原理,kafaka基本原理
9、比较宽泛的架构类型
6、项目中的难点,怎么解决的