面试官:第一轮面试开始。首先问你,Java 中如何创建一个线程?
王铁牛:可以通过继承 Thread 类或者实现 Runnable 接口来创建线程。
面试官:不错,回答正确。那再问你,什么是线程池?它有什么作用?
王铁牛:线程池就是预先创建一定数量的线程,当有任务到来时,从线程池中获取线程来执行任务,执行完任务后线程不会销毁,而是继续留在线程池中等待下一个任务。它可以提高线程的复用性,减少线程创建和销毁的开销。
面试官:很好,回答得很清晰。最后一个问题,HashMap 在多线程环境下会出现什么问题?
王铁牛:嗯……会出现链表成环,然后导致死循环吧。
面试官:第二轮面试。说说 JVM 的内存结构主要有哪些?
王铁牛:有堆、栈、方法区这些。
面试官:那堆内存又分为哪几个区域?
王铁牛:好像有新生代、老年代、永久代。
面试官:永久代现在在 Java 8 中叫什么了?
王铁牛:呃……不太记得了,好像叫元空间。
面试官:第三轮面试。讲讲 Spring 的核心特性有哪些?
王铁牛:嗯……能依赖注入?
面试官:Spring Boot 相对于 Spring 有哪些优势?
王铁牛:这个,Spring Boot 更简单,更方便搭建项目吧。
面试官:MyBatis 的工作原理是什么?
王铁牛:就是通过 XML 配置文件或者注解来映射 SQL 语句。
面试结束,总体来看,你对一些简单的 Java 核心知识有一定了解,但对于复杂点的问题回答得不是很全面清晰。回去等通知吧。
答案:
- Java 创建线程的方式:
- 继承 Thread 类:定义一个类继承 Thread 类,重写 run 方法,然后创建该类的实例并调用 start 方法启动线程。例如:
class MyThread extends Thread { @Override public void run() { System.out.println("线程执行的任务"); } } MyThread thread = new MyThread(); thread.start();- 实现 Runnable 接口:定义一个类实现 Runnable 接口,实现 run 方法,然后创建 Thread 类的实例,将实现 Runnable 接口的类的实例作为参数传递给 Thread 类的构造函数,最后调用 start 方法启动线程。例如:
class MyRunnable implements Runnable { @Override public void run() { System.out.println("线程执行的任务"); } } MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start(); - 线程池:
- 线程池是一种多线程处理形式,它预先创建一定数量的线程,当有任务到来时,从线程池中获取线程来执行任务,执行完任务后线程不会销毁,而是继续留在线程池中等待下一个任务。
- 作用:
- 提高线程复用性:避免频繁创建和销毁线程,减少系统开销。
- 控制线程数量:可以根据系统资源情况合理设置线程池大小,防止线程过多导致系统资源耗尽。
- 便于管理:统一管理线程,方便监控和统计线程的使用情况。
- HashMap 在多线程环境下的问题:
- 在多线程环境下,HashMap 可能会出现链表成环导致死循环的问题。这是因为在扩容时,如果多个线程同时对 HashMap 进行操作,可能会导致链表节点的顺序错乱,形成环形链表。当后续进行查找等操作时,就可能陷入死循环。
- 例如在 JDK 1.7 及之前的版本中,HashMap 的扩容机制存在问题。当进行扩容时,会重新计算节点的位置并插入到新的数组中。在这个过程中,如果有多个线程同时操作,就可能出现链表成环的情况。
- JVM 的内存结构:
- 堆:是 JVM 中最大的一块内存区域,用于存储对象实例。堆又分为新生代、老年代、永久代(Java 8 中叫元空间)。
- 栈:每个线程都有自己独立的栈空间,用于存储局部变量、方法调用等信息。
- 方法区:存储类信息、常量、静态变量等。在 Java 8 中,永久代被元空间取代,元空间使用本地内存,而不是像永久代那样使用 JVM 堆内存。
- Spring 的核心特性:
- 依赖注入:通过 XML 配置文件、注解等方式将对象之间的依赖关系注入到对象中,降低对象之间的耦合度。例如:
或者使用注解:<bean id="userService" class="com.example.UserService"> <constructor-arg ref="userDao"/> </bean> <bean id="userDao" class="com.example.UserDao"/>@Component public class UserService { @Autowired private UserDao userDao; } @Repository public class UserDao { }- IoC(控制反转):将对象的创建和管理交给 Spring 容器,而不是在对象内部自行创建和管理依赖对象。
- 面向切面编程(AOP):可以在不修改原有代码的基础上,对业务逻辑进行增强,比如日志记录、事务管理等。例如:
@Aspect public class LogAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore() { System.out.println("方法执行前记录日志"); } } - Spring Boot 相对于 Spring 的优势:
- 快速搭建项目:Spring Boot 提供了大量的 Starter 依赖,通过自动配置可以快速搭建一个完整的项目,减少了繁琐的配置工作。
- 约定大于配置:遵循一定的约定,减少了不必要的 XML 配置或者注解配置,使项目结构更加清晰,开发效率更高。
- 内置 Web 容器:内置了 Tomcat 等 Web 容器,无需像传统 Spring 项目那样手动配置和部署 Web 容器。
- MyBatis 的工作原理:
- 配置文件:通过 XML 配置文件或者注解来映射 SQL 语句。在 XML 配置文件中,可以定义 SQL 语句、参数映射、结果集映射等。例如:
<select id="getUserById" parameterType="int" resultType="User"> SELECT * FROM user WHERE id = #{id} </select>- SQL 执行:MyBatis 通过 SqlSessionFactory 创建 SqlSession,SqlSession 负责执行 SQL 语句。当调用 SqlSession 的方法时,会根据配置文件中的映射信息来执行相应的 SQL 操作,并将结果进行处理返回给调用者。
- 缓存机制:MyBatis 有一级缓存和二级缓存。一级缓存是 SqlSession 级别的,在同一个 SqlSession 中执行相同的 SQL 语句时,会直接从缓存中获取结果。二级缓存是基于 namespace 的,多个 SqlSession 可以共享二级缓存。