一、Java 基础 (核心重点)
-
面向对象的三大特性是什么?请详细解释,并举例说明。
- 封装:将对象的属性和行为封装起来,不需要让外界知道具体实现细节,通过提供的方法来访问和操作对象。例如:一个
Person类的age属性,通过setAge()方法进行设置,可以在方法内加入逻辑判断防止年龄被设置为负数。 - 继承:子类继承父类的特征和行为,使子类具有父类的属性和方法,从而达到代码重用的目的。例如:
Dog类继承Animal类,拥有Animal的eat()方法。 - 多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。主要包括重写(Override) 和重载(Overload)。例如:父类
Animal有makeSound()方法,子类Dog和Cat分别重写该方法,调用时表现出不同的叫声。
- 封装:将对象的属性和行为封装起来,不需要让外界知道具体实现细节,通过提供的方法来访问和操作对象。例如:一个
-
== 和 equals() 的区别?
- ==:是运算符。对于基本数据类型,比较的是值是否相等;对于引用数据类型,比较的是对象在内存中的地址(即是否是同一个对象)。
- equals():是
Object类的方法,默认行为也是比较内存地址。但很多类(如String,Integer)重写了equals()方法,使其比较的是对象的内容是否相等。
-
String, StringBuffer, StringBuilder 的区别?
- String:不可变字符序列。任何修改操作都会生成新的String对象。线程安全。
- StringBuffer:可变字符序列。修改操作在原有对象上进行。方法用
synchronized修饰,线程安全,但效率相对较低。 - StringBuilder:可变字符序列。与StringBuffer API兼容,但线程不安全,效率最高。
- 使用场景:操作少量数据用String;单线程下操作大量数据用StringBuilder;多线程下操作大量数据用StringBuffer。
-
接口和抽象类的区别?
特性 抽象类 (Abstract Class) 接口 (Interface) 方法 可以有抽象方法和具体方法 Java 8前只能是抽象方法,之后可以有 default和static方法变量 可以有成员变量 只能是 public static final常量继承 单继承(一个类只能继承一个抽象类) 多实现(一个类可以实现多个接口) 构造器 有 无 设计目的 is-a 关系,表示类的本质 has-a 关系,表示类的功能 -
简述Java的异常体系?Error和Exception有什么区别?
-
Throwable 是所有错误和异常的超类。
-
Error:是程序无法处理的严重错误,表示运行应用程序中较严重问题。通常与代码编写者无关,而是JVM出现的问题。例如:
OutOfMemoryError,StackOverflowError。不可捕获处理。 -
Exception:是程序本身可以处理的异常。分为运行时异常(RuntimeException) 和编译时异常(Checked Exception)。
- 运行时异常:如
NullPointerException,IndexOutOfBoundsException,编译器不要求强制处理。 - 编译时异常:如
IOException,SQLException,编译器要求必须用try-catch捕获或throws声明。
- 运行时异常:如
-
-
二、集合框架 (Collections Framework)
-
HashMap 的原理是什么?如何解决哈希冲突?
-
原理:基于哈希表(数组+链表/红黑树)实现。存储时,根据key的
hashCode()计算数组下标。获取时,同样根据hashCode找到数组下标,再遍历链表或红黑树比较key是否相等。 -
解决哈希冲突:
- 链表法(JDK 1.7):相同下标的元素组成一个单向链表。
- 链表+红黑树(JDK 1.8+):当链表长度超过阈值(默认为8)且数组长度大于64时,链表会转换为红黑树,以提高查询效率(O(n) -> O(log n))。当树节点数小于6时,会退化为链表。
-
-
HashMap 和 Hashtable 的区别?
- 线程安全:Hashtable是线程安全的(方法用
synchronized修饰),HashMap是线程不安全的。 - Null键/值:HashMap允许key和value为null;Hashtable不允许。
- 性能:由于Hashtable的同步开销,HashMap的性能通常优于Hashtable。
- 推荐:基本淘汰Hashtable,多线程环境下常用
ConcurrentHashMap。
- 线程安全:Hashtable是线程安全的(方法用
-
List 接口下,ArrayList 和 LinkedList 的区别?
-
底层结构:
ArrayList:基于动态数组。LinkedList:基于双向链表。
-
操作效率:
-
随机访问 (get/set):
ArrayList效率高(O(1)),LinkedList效率低(O(n))。 -
增删操作 (add/remove):
- 在尾部操作,两者效率相近。
- 在中间指定位置插入/删除,
LinkedList效率更高(O(1),但需先遍历到位置O(n)),ArrayList需要移动后续所有元素(O(n))。
-
-
三、多线程与并发
-
创建线程有哪几种方式?
- 继承
Thread类,重写run()方法。 - 实现
Runnable接口,实现run()方法(推荐,避免单继承局限,利于资源共享)。 - 实现
Callable接口,结合FutureTask使用,可以获取返回值。 - 使用线程池创建(如
ExecutorService)。
- 继承
-
Runnable 和 Callable 有什么区别?
Runnable的run()方法没有返回值,也不能抛出受检异常。Callable的call()方法可以有返回值,并且可以抛出异常。
-
sleep() 和 wait() 有什么区别?
- 所属类:
sleep()是Thread的静态方法;wait()是Object的实例方法。 - 锁释放:
sleep()不会释放当前线程持有的锁;wait()会释放锁,并让线程进入等待池。 - 使用场景:
sleep()用于暂停执行;wait()用于线程间通信。
- 所属类:
-
synchronized 和 ReentrantLock 有什么区别?
-
本质:
synchronized是JVM层面的关键字;ReentrantLock是JDK提供的API类。 -
功能:
ReentrantLock更灵活,提供更多功能,如:- 公平锁:可以选择公平锁或非公平锁(synchronized是非公平锁)。
- 可中断:
lockInterruptibly()可以响应中断。 - 尝试获取锁:
tryLock()可以尝试获取锁,获取不到直接返回。 - 多条件绑定:可以绑定多个
Condition对象,实现更精细的线程唤醒。
-
-
什么是线程池?为什么使用它?核心参数有哪些?
-
是什么:管理一组工作线程的池子。它是一种池化技术,避免频繁创建和销毁线程的开销。
-
为什么:降低资源消耗(复用已创建的线程)、提高响应速度(任务到来时无需等待线程创建)、提高线程的可管理性(统一分配、调优和监控)。
-
核心参数(以
ThreadPoolExecutor为例):corePoolSize:核心线程数。maximumPoolSize:最大线程数。keepAliveTime:非核心线程空闲存活时间。workQueue:任务队列(如ArrayBlockingQueue,LinkedBlockingQueue)。threadFactory:线程工厂,用于创建线程。handler:拒绝策略(当线程池和队列都满了时如何处理新任务)。
-
四、JVM 内存与GC
-
说一下 JVM 的主要组成部分及其作用?
- 类加载器(ClassLoader):加载.class文件到内存。
- 运行时数据区(Runtime Data Area):JVM管理的内存区域。
- 执行引擎(Execution Engine):解释/编译字节码,将其转换为机器指令执行。
- 本地方法接口(Native Interface):调用本地(C/C++)方法库。
-
描述一下 JVM 的内存模型(运行时数据区)?
-
线程私有:
- 程序计数器:当前线程所执行的字节码的行号指示器。
- 虚拟机栈:存储Java方法执行时的栈帧(局部变量表、操作数栈、动态链接、方法出口等)。
- 本地方法栈:为Native方法服务。
-
线程共享:
- 堆:存放对象实例和数组,是GC管理的主要区域。
- 方法区:存储已被加载的类信息、常量、静态变量、即时编译器编译后的代码等。JDK 8后称为元空间(Metaspace),使用本地内存。
-
-
如何判断对象是否可以回收?
- 引用计数法(Java未采用):存在循环引用问题。
- 可达性分析算法(Java采用):从一系列“GC Roots”对象作为起点向下搜索,走过的路径称为“引用链”。如果一个对象到GC Roots没有任何引用链相连,则证明此对象不可用,可以被回收。
-
说一下你知道的垃圾回收算法?
- 标记-清除:标记所有需要回收的对象,然后统一清除。问题:产生内存碎片。
- 复制:将内存分为两块,每次只使用一块。当这一块用完了,就将还存活的对象复制到另一块上,然后一次性清理已使用的内存。优点:无碎片。缺点:内存利用率低。
- 标记-整理:标记过程与“标记-清除”一样,但后续不是直接清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。
- 分代收集(现代商用VM常用策略):根据对象存活周期的不同将堆内存划分为新生代(Young Generation)和老年代(Old Generation)。新生代采用复制算法,老年代采用标记-清除或标记-整理算法。
五、数据库 (MySQL)
-
事务的四大特性(ACID)是什么?
- 原子性(Atomicity):事务是一个不可分割的工作单位,要么全部成功,要么全部失败回滚。
- 一致性(Consistency):事务执行前后,数据库的完整性约束没有被破坏。
- 隔离性(Isolation):多个并发事务之间相互隔离,互不干扰。
- 持久性(Durability):事务一旦提交,其对数据库的修改就是永久性的。
-
MySQL 的索引是什么?有什么作用?
- 是什么:索引是帮助MySQL高效获取数据的排好序的数据结构。
- 作用:大大加快数据的检索速度(查询优化)。
- 常见数据结构:B+Tree(最常用)、Hash。
-
什么是内连接、左连接、右连接?
- 内连接(INNER JOIN):返回两个表中连接字段匹配的行。
- 左连接(LEFT JOIN):返回左表的所有行,即使右表中没有匹配的行。右表无匹配则为NULL。
- 右连接(RIGHT JOIN):返回右表的所有行,即使左表中没有匹配的行。左表无匹配则为NULL。
六、Spring 框架
-
Spring 中 Bean 的作用域有哪些?
- singleton(默认):单例,一个Spring容器中只有一个Bean实例。
- prototype:原型,每次请求(获取)都会创建一个新的Bean实例。
- request:一次HTTP请求一个实例。
- session:一个HTTP Session一个实例。
- application:一个ServletContext生命周期一个实例。
-
Spring 事务的实现原理是什么?事务传播行为有哪些?
-
原理:基于AOP(面向切面编程),通过动态代理和事务管理器(如
DataSourceTransactionManager)来实现。在方法调用前开启事务,方法执行后提交或回滚事务。 -
传播行为(常用):
- REQUIRED(默认):如果当前没有事务,就新建一个事务;如果已存在一个事务,就加入到这个事务中。
- REQUIRES_NEW:新建事务,如果当前存在事务,则把当前事务挂起。
- SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
-
-
Spring MVC 的工作流程是什么?
- 用户发送请求至前端控制器(DispatcherServlet)。
- DispatcherServlet收到请求调用处理器映射器(HandlerMapping)。
- HandlerMapping找到具体的处理器,返回给DispatcherServlet。
- DispatcherServlet调用处理器适配器(HandlerAdapter)。
- HandlerAdapter经过适配调用具体的控制器(Controller)。
- Controller执行完成返回一个ModelAndView。
- HandlerAdapter将ModelAndView返回给DispatcherServlet。
- DispatcherServlet将ModelAndView传给视图解析器(ViewResolver)。
- ViewResolver解析后返回具体的View。
- DispatcherServlet根据View进行渲染视图,并返回给客户端。
七、综合与场景题
-
你在项目中遇到过什么印象最深的技术难题?你是怎么解决的?
- 考察点:解决问题的能力、经验总结、技术深度。
- 回答建议:准备一个真实案例,使用STAR原则(Situation, Task, Action, Result)来描述。例如:遇到了一个线上CPU飙高的问题,通过
top,jstack等命令排查,发现是死循环导致的,最终修复了代码逻辑。
-
如果线上接口突然变慢,你会如何排查?
- 检查应用服务器监控(CPU、内存、磁盘IO、网络IO)。
- 查看应用日志,是否有大量报错(如数据库连接超时、第三方接口超时)。
- 排查数据库(慢查询SQL、是否锁表)。
- 排查中间件(Redis连接池、MQ堆积)。
- 使用链路追踪工具(如Skywalking)分析调用链耗时。
-
谈谈你对微服务的理解,它有什么优缺点?
- 优点:服务解耦、独立开发部署、技术选型灵活、容错性高。
- 缺点:架构复杂、运维要求高、部署复杂、分布式事务问题、测试难度大、通信成本高。