面试官:请简要介绍一下Java核心知识中面向对象的三大特性。
王铁牛:嗯,面向对象的三大特性嘛,有封装、继承、多态。封装就是把数据和操作数据的方法封装在一起,对外提供统一的接口;继承就是子类继承父类的属性和方法;多态就是同一个行为具有多个不同表现形式或形态。
面试官:回答得不错。那在多线程环境下,如何确保数据的一致性?
王铁牛:可以用锁呀,像synchronized关键字,能保证同一时间只有一个线程访问共享资源。还有Lock接口,比synchronized更灵活。
面试官:很好。再问一个,说说JVM的内存模型。
王铁牛:JVM内存模型包括堆、栈、方法区这些。堆是存放对象实例的地方,栈存局部变量等,方法区存类信息、常量等。
第一轮结束。
面试官:讲讲JUC包下常用的并发工具类。
王铁牛:有CountDownLatch、CyclicBarrier、Semaphore这些。CountDownLatch用于线程间同步,CyclicBarrier能让一组线程互相等待,Semaphore用于控制同时访问资源的线程数量。
面试官:那线程池的核心参数有哪些,分别有什么作用?
王铁牛:核心参数有corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。corePoolSize是核心线程数,maximumPoolSize是最大线程数,keepAliveTime是线程空闲时的存活时间,unit是时间单位,workQueue是任务队列,threadFactory创建线程,handler处理超出线程池范围的任务。
面试官:说说HashMap的底层实现原理。
王铁牛:HashMap底层是数组+链表+红黑树。当链表长度超过8且数组长度大于64时,链表会转换成红黑树。
第二轮结束。
面试官:简述Spring框架的核心特性。
王铁牛:Spring核心特性有依赖注入、面向切面编程、IoC容器等。依赖注入能把对象依赖关系注入到对象中,AOP可实现横切关注点,IoC容器管理对象的创建和生命周期。
面试官:那Spring Boot的自动配置原理是什么?
王铁牛:Spring Boot自动配置是通过@EnableAutoConfiguration注解,它会根据类路径下的依赖自动配置相关的Bean。
面试官:MyBatis的#{}和${}有什么区别?
王铁牛:#{}是预编译处理,能防止SQL注入,${}是字符串替换,可能存在SQL注入风险。
面试官:简单说下Dubbo的服务调用流程。
王铁牛:服务提供者暴露服务,注册到注册中心,服务消费者从注册中心获取服务地址,然后进行远程调用。
第三轮结束。
面试结束,面试官表示会让王铁牛回家等通知。此次面试,王铁牛对于一些简单问题回答得较为准确,得到了面试官的夸赞,但对于复杂问题回答得比较混乱,不太清晰。整体表现有亮点也有不足,后续能否通过面试还需看面试官综合评估。
答案:
- 面向对象三大特性:
- 封装:把数据和操作数据的方法封装在一起,对外提供统一接口。好处是提高代码安全性和可维护性,比如一个类中的属性可以通过private修饰,然后提供public的getter和setter方法来访问和修改。
- 继承:子类继承父类的属性和方法。实现了代码复用,比如一个父类有一些通用的方法,子类可以直接继承使用,不用重复编写。
- 多态:同一个行为具有多个不同表现形式或形态。分为编译时多态(方法重载)和运行时多态(方法重写)。方法重载是在同一个类中定义多个同名方法,但参数列表不同;方法重写是子类重写父类的方法,在运行时根据对象的实际类型调用相应的方法。
- 多线程环境确保数据一致性:
- synchronized关键字:可以修饰方法或代码块,保证同一时间只有一个线程能访问被修饰的资源。例如修饰一个方法,那么在该方法执行期间,其他线程无法进入该方法。
- Lock接口:比synchronized更灵活,提供了更多的功能。如可中断锁、定时锁等。可以通过创建ReentrantLock对象来使用,例如lock()方法获取锁,unlock()方法释放锁。
- JVM内存模型:
- 堆:存放对象实例,是垃圾回收的主要区域。分为新生代、老年代和永久代(Java 8后为元空间)。新生代又分为Eden区和两个Survivor区,新创建的对象一般存放在Eden区,经过几次垃圾回收后还存活的对象会被转移到老年代。
- 栈:存放局部变量、方法调用等。每个线程都有自己独立的栈空间,栈中的数据遵循先进后出的原则。
- 方法区:存储类信息、常量、静态变量等。在Java 8后,永久代被元空间取代,元空间使用本地内存,不受JVM堆大小限制。
- JUC包下常用并发工具类:
- CountDownLatch:用于线程间同步,它有一个计数器,通过调用countDown()方法减少计数器的值,当计数器的值为0时,等待在await()方法处的线程会被唤醒。例如多个线程需要等待某个操作完成后再继续执行,可以使用CountDownLatch。
- CyclicBarrier:能让一组线程互相等待,直到所有线程都到达某个屏障点。它可以循环使用,通过调用await()方法等待其他线程,当所有线程都调用await()方法后,这些线程会继续执行。
- Semaphore:用于控制同时访问资源的线程数量。通过构造函数设置许可数量,线程调用acquire()方法获取许可,调用release()方法释放许可。比如限制同时访问某个资源的线程数量为5个。
- 线程池核心参数及作用:
- corePoolSize:核心线程数,当提交的任务数小于corePoolSize时,线程池会创建新线程来执行任务。
- maximumPoolSize:最大线程数,当提交的任务数大于corePoolSize且任务队列已满时,会创建新线程执行任务,但线程数不能超过maximumPoolSize。
- keepAliveTime:线程空闲时的存活时间,当线程数大于corePoolSize且线程空闲时间超过keepAliveTime时,多余的线程会被销毁。
- unit:keepAliveTime的时间单位。
- workQueue:任务队列,用于存放提交的任务,当线程数达到corePoolSize时,新提交的任务会被放入任务队列中。
- threadFactory:创建线程的工厂,可以自定义线程的名称、优先级等属性。
- handler:处理超出线程池范围的任务,比如当任务队列已满且线程数达到maximumPoolSize时,会调用handler来处理新提交的任务。
- HashMap底层实现原理:
- HashMap底层是数组+链表+红黑树结构。初始时会创建一个长度为16的数组,当插入键值对时,会根据键的哈希值计算出在数组中的索引位置。
- 如果该位置为空,则直接插入新节点;如果不为空,则会形成链表或红黑树。当链表长度超过8且数组长度大于64时,链表会转换成红黑树,以提高查询效率。红黑树是一种自平衡二叉查找树,它在插入、删除和查找操作上具有较好的时间复杂度。
- Spring框架核心特性:
- 依赖注入:通过IoC容器将对象之间的依赖关系注入到对象中。比如一个类需要另一个类的实例,不需要在本类中手动创建,而是由IoC容器提供。可以通过构造函数注入、setter方法注入等方式实现。
- 面向切面编程(AOP):可实现横切关注点,如日志记录、事务管理等功能可以独立于业务逻辑之外实现。通过定义切面、切入点和通知来实现,通知分为前置通知、后置通知、环绕通知等多种类型。
- IoC容器:管理对象的创建、生命周期等。它负责创建对象、装配对象之间的依赖关系,并在对象不再需要时销毁对象。常见的IoC容器实现有XmlBeanFactory、ApplicationContext等。
- Spring Boot自动配置原理:
- Spring Boot自动配置是通过@EnableAutoConfiguration注解实现的。它会根据类路径下的依赖自动配置相关的Bean。
- 它会扫描classpath下的META-INF/spring.factories文件,根据其中定义的自动配置类来进行配置。例如当项目中引入了Spring Data JPA依赖时,会自动配置相关的数据源、JPA的EntityManager等Bean。
- MyBatis的#{}和${}区别:
- #{}:是预编译处理,会将传入的值作为参数进行预编译,能防止SQL注入。例如在SQL语句中使用#{}时,MyBatis会将其替换为?,然后通过PreparedStatement设置参数值。
- {}。
- Dubbo服务调用流程:
- 服务提供者:将服务接口实现类发布到Dubbo框架中,通过@Service注解暴露服务,并在配置文件中指定服务的相关信息,如接口名、版本号等。然后将服务注册到注册中心。
- 注册中心:存储服务提供者的服务信息,包括服务接口、实现类、地址等。服务提供者会定期向注册中心汇报自己的服务状态。
- 服务消费者:从注册中心获取服务提供者的地址信息。通过@Reference注解引用需要的服务接口,在调用服务方法时,Dubbo会根据从注册中心获取的地址信息进行远程调用。
- 远程调用:服务消费者通过网络向服务提供者发送请求,服务提供者接收到请求后执行相应的业务逻辑,并返回结果给服务消费者。