《互联网大厂Java求职者面试:核心知识大考验》

24 阅读9分钟

面试官:请简要介绍一下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设置参数值。
    • :是字符串替换,会直接将传入的值替换到SQL语句中。这种方式可能存在SQL注入风险,比如传入的值是恶意的SQL语句片段时,会导致数据库安全问题。一般不建议在需要防止SQL注入的地方使用{}:是字符串替换,会直接将传入的值替换到SQL语句中。这种方式可能存在SQL注入风险,比如传入的值是恶意的SQL语句片段时,会导致数据库安全问题。一般不建议在需要防止SQL注入的地方使用{}。
  • Dubbo服务调用流程:
    • 服务提供者:将服务接口实现类发布到Dubbo框架中,通过@Service注解暴露服务,并在配置文件中指定服务的相关信息,如接口名、版本号等。然后将服务注册到注册中心。
    • 注册中心:存储服务提供者的服务信息,包括服务接口、实现类、地址等。服务提供者会定期向注册中心汇报自己的服务状态。
    • 服务消费者:从注册中心获取服务提供者的地址信息。通过@Reference注解引用需要的服务接口,在调用服务方法时,Dubbo会根据从注册中心获取的地址信息进行远程调用。
    • 远程调用:服务消费者通过网络向服务提供者发送请求,服务提供者接收到请求后执行相应的业务逻辑,并返回结果给服务消费者。