面试官:请简要介绍一下Java核心知识在实际项目中的应用场景。
王铁牛:嗯,Java核心知识嘛,像面向对象编程在项目里可以让代码结构更清晰,便于维护和扩展。
面试官:很不错,回答得很简洁明了。那说说JUC在多线程并发场景下的作用。
王铁牛:JUC能提供一些好用的工具类,比如并发容器啥的,让多线程编程更方便。
面试官:回答正确。再问你,JVM的内存模型是怎样的?
王铁牛:这个嘛……呃,就是有堆、栈、方法区这些吧。
第一轮结束。
面试官:接下来问多线程相关问题。如何创建一个线程?
王铁牛:可以继承Thread类或者实现Runnable接口。
面试官:很好。那线程池有哪些参数,它们的作用是什么?
王铁牛:这个……好像有核心线程数、最大线程数啥的,具体作用不太清楚了。
面试官:HashMap的底层实现原理是什么?
王铁牛:呃,就是数组加链表吧,大概是这样。
第二轮结束。
面试官:谈谈Spring的核心特性。
王铁牛:Spring可以实现依赖注入和面向切面编程。
面试官:Spring Boot有什么优势?
王铁牛:它很方便,能快速搭建项目。
面试官:MyBatis的映射文件中如何配置动态SQL?
王铁牛:这个……不太记得了,好像有if、foreach啥的。
第三轮结束。
面试结束后,面试官表示会让王铁牛回家等通知。此次面试,王铁牛对于一些简单问题回答得不错,展现了一定的基础知识,但对于复杂问题回答得不够清晰准确,有待进一步提升对知识的深入理解和掌握程度。
答案:
- Java核心知识在实际项目中的应用场景:
- 面向对象编程:提高代码的可维护性和可扩展性。通过封装、继承和多态,将项目中的不同功能模块进行合理划分和组织。例如,一个电商项目中,商品类、订单类等可以通过类的封装来管理各自的属性和行为,不同类型的商品可以继承商品类并扩展特定的属性和方法,同时通过多态来实现不同商品在购物车、下单等流程中的统一处理。
- 异常处理:保障程序的稳定性。在处理用户输入、文件操作、网络请求等可能出现错误的地方,使用try - catch - finally块来捕获和处理异常,避免程序因异常而崩溃。比如在读取配置文件时,如果文件不存在或格式错误,通过异常处理可以给出友好的提示信息并进行相应的处理。
- JUC在多线程并发场景下的作用:
- JUC提供了一系列并发工具类,如CountDownLatch、CyclicBarrier、Semaphore等。
- CountDownLatch可以用于控制线程等待,比如多个线程需要等待某个条件满足后再一起执行。例如,在一个多线程任务中,主线程需要等待所有子线程完成各自的初始化工作后再继续执行后续逻辑,就可以使用CountDownLatch。
- CyclicBarrier能让一组线程相互等待,直到所有线程都到达某个屏障点,然后再一起继续执行。比如在进行团队协作的任务时,所有成员都准备好后再共同开始执行下一步操作。
- Semaphore用于控制同时访问某个资源的线程数量。比如在一个系统中,限制同时只能有5个线程访问某个数据库连接池,就可以使用Semaphore来实现。
- JVM的内存模型:
- 堆:是JVM中最大的一块内存区域,用于存储对象实例。所有new出来的对象都存放在堆中。堆又可以分为新生代、老年代和永久代(Java 8后为元空间)。新生代主要存放新创建的对象,当对象经历一定次数的垃圾回收后仍存活,会被晋升到老年代。永久代(元空间)用于存储类信息、常量池等。
- 栈:每个线程都有自己独立的栈,用于存储局部变量、方法调用等。方法调用时,会在栈中创建栈帧,栈帧中包含局部变量表、操作数栈、动态链接等信息。
- 方法区:存储类的元数据信息,如类的结构、字段、方法等。在Java 8后,方法区的实现被改为元空间,元空间使用本地内存,而不是像永久代那样使用JVM的堆内存。
- 创建线程的方式:
- 继承Thread类:通过继承Thread类并重写其run方法来定义线程的执行逻辑。例如:
class MyThread extends Thread { @Override public void run() { System.out.println("This is a thread created by extending Thread class."); } } // 使用时 MyThread thread = new MyThread(); thread.start();- 实现Runnable接口:实现Runnable接口的run方法,然后将实现类的实例作为参数传递给Thread类的构造函数来创建线程。例如:
class MyRunnable implements Runnable { @Override public void run() { System.out.println("This is a thread created by implementing Runnable interface."); } } // 使用时 Thread thread = new Thread(new MyRunnable()); thread.start(); - 线程池的参数及其作用:
- corePoolSize(核心线程数):线程池创建时初始化的线程数量。当提交的任务数小于corePoolSize时,线程池会创建新线程来执行任务。
- maximumPoolSize(最大线程数):线程池允许的最大线程数量。当提交的任务数大于corePoolSize且任务队列已满时,会创建新线程执行任务,但线程数量不会超过maximumPoolSize。
- keepAliveTime(线程存活时间):当线程数量大于corePoolSize时,多余的线程在空闲时会存活的时间。
- unit(时间单位):keepAliveTime的时间单位。
- workQueue(任务队列):用于存放提交的任务,当线程池忙碌时,新提交的任务会放入任务队列中等待执行。
- threadFactory(线程工厂):用于创建线程,可自定义线程的名称、优先级等属性。
- handler(拒绝策略):当线程池的线程数量达到maximumPoolSize且任务队列已满时,如何处理新提交的任务。常见的拒绝策略有AbortPolicy(抛出异常)、CallerRunsPolicy(调用者线程执行任务)、DiscardPolicy(丢弃任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。
- HashMap的底层实现原理:
- HashMap底层是基于数组和链表(JDK 1.8后引入红黑树)实现的。
- 初始化时,会创建一个默认大小为16的数组。
- 当往HashMap中插入键值对时,首先会通过键的hashCode方法计算出一个哈希值,然后通过哈希值与数组长度进行按位与运算得到数组的下标位置。
- 如果该位置为空,则直接插入新的键值对。
- 如果该位置不为空,则会遍历链表(或红黑树),如果找到相同的键,则更新对应的值;如果未找到相同的键且链表长度小于8(JDK 1.8后),则将新的键值对插入链表尾部;如果链表长度大于等于8且数组长度小于64,则会将链表转换为红黑树以提高查询效率;如果链表长度大于等于8且数组长度大于等于64,则会进行扩容操作。
- 扩容时,会创建一个新的更大的数组(通常是原数组大小的2倍),然后将原数组中的键值对重新计算哈希值并插入到新数组中。
- Spring的核心特性:
- 依赖注入(Dependency Injection):通过容器将依赖对象注入到目标对象中,降低对象之间的耦合度。例如,在一个用户服务类中,需要使用数据库连接对象,通过依赖注入可以在配置文件中指定数据库连接对象的实现类,由Spring容器在创建用户服务类时自动注入。
- 面向切面编程(Aspect - Oriented Programming,AOP):可以将一些横切关注点(如日志记录、事务管理等)与业务逻辑分离。通过定义切面,在不修改业务逻辑代码的情况下,为业务方法添加额外的功能。比如在所有业务方法执行前后记录日志,或者对业务方法进行事务管理。
- Spring Boot的优势:
- 快速搭建项目:提供了大量的起步依赖(starter),可以快速集成各种常用的技术栈,如Web开发、数据库访问等。例如,添加一个Spring Boot的Web起步依赖,就可以快速搭建一个简单的Web应用,无需手动配置大量的依赖和相关配置文件。
- 自动配置:Spring Boot会根据项目中引入的依赖自动进行合理的默认配置,大大减少了开发人员的配置工作量。比如引入Spring Data JPA依赖后,Spring Boot会自动配置好数据库连接、实体扫描等相关配置。
- 嵌入式服务器:内置了Tomcat、Jetty等嵌入式服务器,无需再单独安装和配置外部服务器,方便进行开发和部署。
- MyBatis映射文件中配置动态SQL:
- 使用if标签:根据条件判断来决定是否包含某些SQL片段。例如:
<select id="getUserById" parameterType="int" resultType="User"> SELECT * FROM user <where> <if test="id!= null"> AND id = #{id} </if> </where> </select>- 使用foreach标签:用于循环遍历集合,生成对应的SQL片段。比如批量删除用户:
还可以使用choose、when、otherwise等标签来进行更复杂的条件选择。<delete id="deleteUsers" parameterType="list"> DELETE FROM user <where> <foreach collection="list" item="item" open="id in (" separator="," close=")"> #{item} </foreach> </where> </delete>