[1]. 说说int和Integer的区别
int是Java中的基本数据类型,用于存储整数值。它直接在栈内存中存储数值,默认值是0,并且不能为null,本身不带方法。
Integer是int的包装类,属于引用类型。它在堆内存中存储一个对象,该对象包含一个int值。Integer可以为null,并且提供了很多方法,例如parseInt、compareTo和toString等。
[2]. 如何保证Kafka只消费一次?
Kafka默认提供‘至少一次’的语义,这意味着消息可能会被重复消费。
在分布式系统中,实现精确的‘只消费一次’比较困难。要解决这个问题,通常会结合以下方案:
首先是幂等消费,确保消息被多次消费与只消费一次的效果是一样的,例如可以通过数据库的唯一索引或者状态检查实现。
其次是手动提交偏移量,关闭自动提交,在成功处理消息后才提交偏移量,通常和幂等性一起使用。
最后,Kafka本身也提供了事务功能,可以在Producer和Consumer端保证消息操作的原子性。
[3]. JVM内存如何分配?什么时候新生代GC,什么时候老年代GC?
JVM的内存主要分为堆、方法区、虚拟机栈、本地方法栈和程序计数器。其中,堆是对象实例的主要存储区域,它被划分为新生代和老年代。
新生代又细分为Eden区和From Survivor和To Survivor两个Survivor区。
当使用new创建对象时,JVM会首先尝试在Eden区分配内存。当Eden区满时,会触发新生代GC,也叫Minor GC。Minor GC会把Eden区和From Survivor区中存活的对象复制到To Survivor区,同时存活对象的年龄会加1。之后清空Eden区和From Survivor区,然后From Survivor和To Survivor的角色会互换。
当老年代空间不足、或者新生代GC后存活的对象需要晋升到老年代但老年代空间不足,或者方法区空间不足时,会触发老年代GC(也叫Major GC或者Full GC)。
[4]. 手撕:合并两个有序数组。
public class MergeSortedArray {public static void merge(int[] nums1, int m, int[] nums2, int n) {int i = m - 1; // 指向 nums1 的末尾int j = n - 1; // 指向 nums2 的末尾int k = m + n - 1; // 指向 nums1 合并后的末尾while (i >= 0 && j >= 0) {if (nums1[i] > nums2[j]) {nums1[k] = nums1[i];i--;} else {nums1[k] = nums2[j];j--;}k--;}// 如果 nums2 还有剩余元素,则直接复制到 nums1 前面while (j >= 0) {nums1[k] = nums2[j];j--;k--;}}}
[5]. 手撕:二叉树后序遍历(非递归)
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;public class PostorderTraversal {static class TreeNode {int val;TreeNode left;TreeNode right;TreeNode(int val) {this.val = val;this.left = null;this.right = null;}}public static List<Integer> postorderTraversal(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null) {return result;}Stack<TreeNode> stack = new Stack<>();TreeNode prev = null; // 记录上次访问的节点TreeNode current = root;while (current != null || !stack.isEmpty()) {// 1. 遍历左子树while (current != null) {stack.push(current);current = current.left;}// 2. 访问右子树current = stack.peek(); // 取栈顶元素if (current.right == null || current.right == prev) {// 2.1 没有右子树或者右子树已经访问过,访问当前节点result.add(current.val);stack.pop();prev = current;current = null; // 避免重复访问左子树} else {// 2.2 右子树还没有访问过current = current.right;}}return result;}}