面试官:请简要介绍一下Java核心知识,比如面向对象的三大特性。
王铁牛:这个我知道,面向对象的三大特性是封装、继承和多态。封装就是把对象的属性和方法封装起来,对外提供统一的接口;继承可以让子类继承父类的属性和方法;多态则是同一个方法可以根据对象的不同类型表现出不同的行为。
面试官:不错,回答得很准确。那再说说JUC包下的常用类有哪些。
王铁牛:JUC包下有很多常用类啊,比如CountDownLatch、CyclicBarrier、Semaphore这些。
面试官:很好。接下来问几个关于JVM的问题,类加载的过程分为哪几个阶段?
王铁牛:类加载过程分为加载、验证、准备、解析、初始化这几个阶段。
第一轮结束。
面试官:多线程中,如何创建线程?
王铁牛:可以通过继承Thread类或者实现Runnable接口来创建线程。
面试官:线程池有哪些参数,分别有什么作用?
王铁牛:线程池有corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler这些参数。corePoolSize是核心线程数,maximumPoolSize是最大线程数,keepAliveTime是线程池中的线程在超出corePoolSize后,多余的空闲线程的存活时间,unit是keepAliveTime的时间单位,workQueue是任务队列,threadFactory是线程工厂,handler是拒绝策略。
面试官:HashMap的底层数据结构是什么?
王铁牛:HashMap底层是数组+链表+红黑树的数据结构。
第二轮结束。
面试官:Spring的核心特性有哪些?
王铁牛:Spring的核心特性有依赖注入、面向切面编程、IoC容器等。
面试官:Spring Boot的自动配置原理是什么?
王铁牛:Spring Boot的自动配置原理就是通过@EnableAutoConfiguration注解,利用SpringFactoriesLoader加载所有的自动配置类,根据条件判断来决定是否启用某个配置。
面试官:MyBatis的#{}和${}有什么区别?
王铁牛:#{}会进行预编译,防止SQL注入,${}不会进行预编译,可能会有SQL注入风险。
第三轮结束。
面试结束,面试官表示会让王铁牛回家等通知。
答案:
- 面向对象的三大特性:
- 封装:将对象的属性和方法包装起来,对外提供统一的访问接口,提高了代码的安全性和可维护性。例如,一个类中的属性可以用private修饰,通过public的getter和setter方法来访问和修改。
- 继承:子类继承父类的属性和方法,实现代码的复用。比如,一个父类有一些通用的方法,子类可以继承这些方法并根据自身需求进行扩展。
- 多态:同一个方法根据对象的不同类型表现出不同的行为。比如,一个父类引用可以指向子类对象,调用同一个方法时会执行子类的具体实现。
- JUC包下的常用类:
- CountDownLatch:用于协调多个线程之间的同步,它允许一个或多个线程等待其他线程完成一组操作。例如,主线程可以等待多个子线程都执行完任务后再继续执行。
- CyclicBarrier:它可以让一组线程互相等待,直到所有线程都到达某个屏障点。之后,这些线程可以继续执行。比如,在一个多线程协作的场景中,所有线程都需要完成各自的一部分任务后,才能一起进行下一步操作。
- Semaphore:信号量,用于控制同时访问某个资源的线程数量。例如,限制数据库连接池的最大连接数。
- 类加载的过程:
- 加载:将类的字节码文件加载到内存中。
- 验证:检查字节码文件的格式、语义等是否正确。
- 准备:为类的静态变量分配内存并设置初始值。
- 解析:将符号引用转换为直接引用。
- 初始化:执行类的静态代码块,为静态变量赋值等操作。
- 创建线程的方式:
- 继承Thread类:通过继承Thread类并重写run方法来创建线程。
- 实现Runnable接口:实现Runnable接口的run方法,然后将其作为参数传递给Thread类的构造函数来创建线程。
- 线程池的参数及作用:
- corePoolSize:核心线程数,线程池在创建后,首先会创建corePoolSize个线程来处理任务。
- maximumPoolSize:最大线程数,当任务队列满了且线程数小于maximumPoolSize时,会创建新的线程来处理任务,直到线程数达到maximumPoolSize。
- keepAliveTime:线程池中的线程在超出corePoolSize后,多余的空闲线程的存活时间。
- unit:keepAliveTime的时间单位。
- workQueue:任务队列,用于存放提交到线程池但尚未被处理的任务。
- threadFactory:线程工厂,用于创建线程,可自定义线程的名称、优先级等。
- handler:拒绝策略,当线程池的线程数达到maximumPoolSize且任务队列已满时,会调用handler来处理新提交的任务,常见的拒绝策略有AbortPolicy(直接抛出异常)、CallerRunsPolicy(调用者线程处理任务)、DiscardPolicy(丢弃新提交的任务)、DiscardOldestPolicy(丢弃任务队列中最旧的任务)。
- HashMap的底层数据结构:
- HashMap底层是数组+链表+红黑树的数据结构。初始时,HashMap创建一个长度为16的数组。当向HashMap中插入键值对时,首先通过key的hashCode计算出哈希值,然后通过哈希值与数组长度取模得到数组的索引位置。如果该位置为空,则直接插入新的键值对。如果该位置不为空,就会发生哈希冲突,此时会将新的键值对以链表的形式添加到该位置的链表中。当链表长度达到8时(JDK 1.8及以后),链表会转换为红黑树,以提高查找效率。
- Spring的核心特性:
- 依赖注入:通过容器将依赖对象注入到需要的组件中,降低组件之间的耦合度。例如,一个类需要使用另一个类的功能,通过依赖注入可以方便地获取到该依赖类的实例。
- 面向切面编程(AOP):通过预编译方式和运行期动态代理实现程序功能的统一维护。比如,在不修改业务逻辑代码的情况下,实现日志记录、事务管理等功能。
- IoC容器:控制反转容器,负责创建、配置和管理对象之间的依赖关系。容器负责对象的创建和生命周期管理,对象只需要声明依赖,而不需要自己去创建依赖对象。
- Spring Boot的自动配置原理: Spring Boot的自动配置是通过@EnableAutoConfiguration注解来实现的。它利用SpringFactoriesLoader加载所有的自动配置类,这些自动配置类存放在META - INF/spring.factories文件中。自动配置类会根据当前应用的环境信息(如是否引入了某个依赖、配置文件中的属性等),通过条件判断来决定是否启用某个配置。例如,如果应用中引入了数据库相关的依赖,自动配置类会根据数据库的类型等信息,自动配置数据源、事务管理器等相关组件,极大地简化了开发过程。
- MyBatis的#{}和${}的区别:
- #{}:会进行预编译,将参数的值作为一个占位符,在SQL语句中用?代替,这样可以防止SQL注入。例如,当传入一个参数name时,SQL语句会变为select * from user where name =?,然后通过PreparedStatement设置参数值,保证了安全性。
- {tableName},如果参数值是用户可控的,就可能会导致SQL注入风险。