面试官:请简要介绍一下Java核心知识中的面向对象三大特性。
王铁牛:这个简单,就是封装、继承和多态嘛。
面试官:不错,回答正确。那再说说JUC中常用的并发工具类有哪些。
王铁牛:像CountDownLatch、CyclicBarrier、Semaphore这些。
面试官:很好。接下来谈谈JVM的内存模型。
王铁牛:呃……这个嘛,就是堆、栈、方法区这些吧。
面试官:回答得不是很清晰,再仔细讲讲。
第一轮结束。
面试官:多线程中如何实现线程安全?
王铁牛:可以用synchronized关键字或者Lock接口。
面试官:那线程池的核心参数都有哪些?
王铁牛:有corePoolSize、maximumPoolSize、keepAliveTime这些。
面试官:说说HashMap的底层数据结构。
王铁牛:好像是数组加链表,哦不,还有红黑树。
面试官:回答得不太准确,再好好想想。
第二轮结束。
面试官:ArrayList的扩容机制是怎样的?
王铁牛:每次扩容是原来的1.5倍。
面试官:Spring框架中依赖注入的方式有哪些?
王铁牛:构造器注入、setter方法注入。
面试官:Spring Boot的启动流程了解吗?
王铁牛:这个不太清楚。
面试官:那说说MyBatis的缓存机制。
王铁牛:有一级缓存和二级缓存。
第三轮结束。
面试结束,面试官表示会让王铁牛回家等通知。
答案:
- 面向对象三大特性:
- 封装:将对象的属性和行为包装在一起,对外提供统一的接口,隐藏内部实现细节,提高代码的安全性和可维护性。
- 继承:子类继承父类的属性和方法,实现代码复用,提高开发效率。
- 多态:同一个行为具有多种不同表现形式,通过父类引用指向子类对象,在运行时根据对象的实际类型调用相应的方法,增强代码的灵活性和扩展性。
- JUC常用并发工具类:
- CountDownLatch:用于线程间的同步,它允许一个或多个线程等待其他一组线程完成操作。通过构造函数传入一个计数值,调用await方法的线程会阻塞,直到计数值为0。其他线程通过调用countDown方法来减少计数值。
- CyclicBarrier:也用于线程同步,它允许一组线程互相等待,直到到达某个公共屏障点。与CountDownLatch不同的是,CyclicBarrier可以重用,当所有线程到达屏障点后,屏障会重置,可继续下一轮的同步。
- Semaphore:信号量,用于控制同时访问某个资源的线程数量。通过构造函数传入许可数量,线程调用acquire方法获取许可,调用release方法释放许可。
- JVM内存模型:
- 堆:是Java虚拟机所管理的内存中最大的一块,被所有线程共享,用于存放对象实例。
- 栈:每个线程都有自己独立的栈空间,用于存储局部变量、方法调用等。
- 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量等数据。
- 多线程实现线程安全:
- synchronized关键字:可以修饰方法或代码块,当一个线程访问被synchronized修饰的方法或代码块时,会先获取对象的锁,其他线程必须等待该锁被释放后才能访问。
- Lock接口:提供了比synchronized更灵活的锁控制,如可中断锁、公平锁等。通过Lock接口的实现类如ReentrantLock来实现线程安全。
- 线程池核心参数:
- corePoolSize:线程池的核心线程数,当提交的任务数小于corePoolSize时,线程池会创建新线程来执行任务。
- maximumPoolSize:线程池允许的最大线程数,当提交的任务数大于corePoolSize时,会将任务放入队列中,如果队列已满且线程数小于maximumPoolSize,则会创建新线程来执行任务。
- keepAliveTime:线程池中非核心线程的存活时间,当线程空闲时间超过keepAliveTime时,非核心线程会被销毁。
- HashMap底层数据结构:
- 在JDK 1.8之前,HashMap底层是数组加链表结构。当插入元素时,通过计算key的哈希值得到数组下标,如果该下标对应的位置为空,则直接插入;如果不为空,则采用链表头插法插入新元素。
- 在JDK 1.8之后,HashMap底层数据结构变为数组 + 链表 + 红黑树。当链表长度大于8且数组长度大于64时,链表会转换为红黑树,以提高查询效率。
- ArrayList扩容机制:
- ArrayList默认初始容量是10。当添加元素导致容量不足时,会进行扩容。每次扩容是原来容量的1.5倍。扩容时会创建一个新的更大的数组,将原数组中的元素复制到新数组中。
- Spring框架依赖注入方式:
- 构造器注入:通过构造函数来注入依赖对象,这种方式可以保证依赖对象在对象创建时就被注入,并且一旦注入完成,对象就处于完整可用状态。
- setter方法注入:通过setter方法来注入依赖对象,这种方式更加灵活,对象创建时可以不注入依赖,后续再通过setter方法进行注入。
- Spring Boot启动流程:
- Spring Boot应用启动时,首先会创建SpringApplication实例。
- SpringApplication会根据应用的类型(如Web应用等)进行相应的初始化配置。
- 然后通过调用run方法启动应用,在run方法中会进行一系列操作,包括创建Spring容器(如AnnotationConfigApplicationContext或AnnotationConfigServletWebServerApplicationContext)。
- Spring容器会扫描应用中的组件,通过注解(如@Component、@Service等)将这些组件注册到容器中,并根据依赖关系进行自动装配。
- 最后,Spring Boot会启动内置的Web服务器(如Tomcat),监听指定端口,处理客户端请求。
- MyBatis缓存机制:
- 一级缓存:是SqlSession级别的缓存,在同一个SqlSession中,对相同的SQL语句进行查询时,会直接从缓存中获取数据,不会再次查询数据库。当SqlSession关闭时,一级缓存会被清空。
- 二级缓存:是基于namespace级别的缓存,多个SqlSession可以共享二级缓存。当一个SqlSession查询数据时,会先从一级缓存中查找,如果没有则从二级缓存中查找,若二级缓存也没有则查询数据库,并将查询结果放入一级缓存和二级缓存中。二级缓存需要在MyBatis配置文件中进行配置启用。