互联网大厂面试:Java核心知识、框架与中间件大考验
在互联网大厂的一间明亮的面试室内,严肃的面试官坐在桌前,对面坐着略显紧张的求职者王铁牛。面试正式开始。
第一轮面试 面试官:首先问你几个基础的Java核心知识问题。Java 中的基本数据类型有哪些? 王铁牛:Java 的基本数据类型有 byte、short、int、long、float、double、char、boolean。 面试官:回答得不错。那说说 String 类为什么是不可变的? 王铁牛:因为 String 类被 final 修饰,它的底层是用 char 数组存储数据,这个数组也被 final 修饰,所以不可变。 面试官:很好,理解得挺到位。那在多线程环境下,使用 StringBuilder 会有什么问题? 王铁牛:StringBuilder 不是线程安全的,在多线程环境下多个线程同时对它进行操作,可能会出现数据不一致或者抛出异常的情况。 面试官:非常棒,看来基础很扎实。
第二轮面试 面试官:接下来聊聊 JUC 和多线程相关的。说说 CountDownLatch 的作用和使用场景。 王铁牛:CountDownLatch 可以让一个或多个线程等待其他线程完成操作后再继续执行。使用场景比如在一个大型任务中,有多个子任务并行执行,主线程需要等所有子任务都完成后才能继续往下执行,就可以用 CountDownLatch。 面试官:回答正确。那线程池的拒绝策略有哪些? 王铁牛:线程池的拒绝策略有 AbortPolicy、CallerRunsPolicy、DiscardPolicy、DiscardOldestPolicy。 面试官:不错。那在多线程环境下,如何保证 HashMap 的线程安全? 王铁牛:可以使用 Collections.synchronizedMap() 方法把 HashMap 包装成线程安全的 Map,或者直接使用 ConcurrentHashMap。 面试官:回答得很好,对多线程这块掌握得挺全面。
第三轮面试 面试官:现在来谈谈框架和中间件。Spring 的 AOP 是什么,有什么应用场景? 王铁牛:AOP 是面向切面编程,它可以在不修改原有代码的基础上,对程序进行增强。应用场景比如日志记录、事务管理等。 面试官:那 Spring Boot 的自动配置原理是什么? 王铁牛:呃……这个……好像是根据 classpath 下的依赖和配置文件,自动帮我们配置一些 Bean。具体怎么实现的我有点说不清楚。 面试官:那 MyBatis 的一级缓存和二级缓存是怎么回事? 王铁牛:一级缓存是会话级别的缓存,同一个 SqlSession 内查询相同的数据会从缓存中取。二级缓存是 mapper 级别的缓存,不同的 SqlSession 可以共享。但是具体的缓存机制和怎么使用我不太记得了。 面试官:看来你对框架的原理部分掌握得不够深入。Dubbo 是做什么的,它的负载均衡策略有哪些? 王铁牛:Dubbo 是一个分布式服务框架,负载均衡策略嘛……我只知道有随机和轮询,其他的不太清楚了。
面试接近尾声,面试官放下手中的笔,表情严肃地说:“王铁牛,通过这次面试,我能看出你对 Java 的一些基础知识和部分框架有一定的了解,在前面两轮的表现还是不错的,回答问题比较准确清晰。但在第三轮关于框架原理和中间件的深入问题上,你回答得不够理想,很多原理性的东西没有阐述清楚。我们需要综合评估一下你的整体情况,你先回家等通知吧。”
问题答案详解
- Java 中的基本数据类型有哪些?
- Java 有 8 种基本数据类型,分为 4 类:
- 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
- 浮点类型:float(4 字节)、double(8 字节)。
- 字符类型:char(2 字节)。
- 布尔类型:boolean(理论上 1 位,但 JVM 实现通常为 1 字节)。
- Java 有 8 种基本数据类型,分为 4 类:
- String 类为什么是不可变的?
- String 类被 final 修饰,意味着它不能被继承。
- 其底层使用 char 数组(Java 9 及以后是 byte 数组)存储数据,并且这个数组也被 final 修饰,一旦初始化就不能再指向其他数组。这保证了 String 对象创建后内容不会被修改,提高了安全性和性能,比如可以作为 HashMap 的键。
- 在多线程环境下,使用 StringBuilder 会有什么问题?
- StringBuilder 内部的方法没有使用 synchronized 关键字修饰,不是线程安全的。在多线程环境下,多个线程同时对 StringBuilder 进行 append、delete 等操作时,可能会出现数据不一致的情况,比如一个线程正在修改字符数组,另一个线程也来修改,就会导致最终结果不符合预期,甚至可能抛出 ArrayIndexOutOfBoundsException 等异常。
- CountDownLatch 的作用和使用场景。
- 作用:CountDownLatch 允许一个或多个线程等待其他线程完成操作。它通过一个计数器来实现,初始值为需要等待的线程数量,每个线程完成任务后计数器减 1,当计数器为 0 时,等待的线程可以继续执行。
- 使用场景:
- 并行任务的汇总:比如一个大型任务可以拆分成多个子任务并行执行,主线程需要等所有子任务完成后才能继续处理结果。
- 资源初始化:多个线程负责不同资源的初始化,主线程需要等所有资源初始化完成后才能继续执行后续业务。
- 线程池的拒绝策略有哪些?
- AbortPolicy:默认的拒绝策略,当线程池队列满且线程数达到最大线程数时,会直接抛出 RejectedExecutionException 异常。
- CallerRunsPolicy:由调用线程池的线程来执行被拒绝的任务,这样可以减缓新任务提交的速度。
- DiscardPolicy:直接丢弃被拒绝的任务,不做任何处理。
- DiscardOldestPolicy:丢弃队列中最老的任务,然后尝试将被拒绝的任务加入队列。
- 在多线程环境下,如何保证 HashMap 的线程安全?
- 使用 Collections.synchronizedMap() 方法:该方法会返回一个线程安全的 Map,它是通过在所有方法上加 synchronized 关键字来实现线程安全的,但性能较低。
- 使用 ConcurrentHashMap:它是 Java 并发包提供的线程安全的哈希表,采用分段锁(Java 7)或 CAS + synchronized(Java 8)的机制,并发性能比 Collections.synchronizedMap() 高很多。
- Spring 的 AOP 是什么,有什么应用场景?
- AOP 即面向切面编程,它将程序中的一些通用功能(如日志记录、事务管理等)从业务逻辑中分离出来,形成独立的模块(切面),在不修改原有代码的基础上,通过动态代理的方式将这些功能织入到目标对象的方法执行前后。
- 应用场景:
- 日志记录:记录方法的调用参数、返回值和执行时间等信息。
- 事务管理:在方法执行前后进行事务的开启、提交或回滚操作。
- 权限验证:在方法执行前验证用户是否有访问权限。
- Spring Boot 的自动配置原理是什么?
- Spring Boot 的自动配置基于 Spring 的条件注解和类路径扫描机制。当 Spring Boot 应用启动时,会根据 classpath 下的依赖和配置文件,自动配置一些 Bean。
- 具体流程如下:
- Spring Boot 应用启动时,会加载 META - INF/spring.factories 文件,该文件中配置了所有自动配置类的全限定名。
- Spring 会根据条件注解(如 @ConditionalOnClass、@ConditionalOnMissingBean 等)来判断是否需要加载某个自动配置类。如果条件满足,就会创建相应的 Bean 并注入到 Spring 容器中。
- MyBatis 的一级缓存和二级缓存是怎么回事?
- 一级缓存:是会话级别的缓存,同一个 SqlSession 内,多次执行相同的查询语句,第一次查询会将结果存入缓存,后续查询会直接从缓存中获取,减少数据库访问。当 SqlSession 关闭或执行了增删改操作,一级缓存会被清空。
- 二级缓存:是 mapper 级别的缓存,不同的 SqlSession 可以共享。需要在 mapper.xml 文件中配置 标签或者在 mapper 接口上使用 @CacheNamespace 注解开启。当一个 SqlSession 执行查询将结果存入二级缓存后,其他 SqlSession 也可以从该缓存中获取相同查询结果。同样,执行增删改操作会清空二级缓存。
- Dubbo 是做什么的,它的负载均衡策略有哪些?
- Dubbo 是一个分布式服务框架,用于实现服务的远程调用、服务注册与发现、负载均衡等功能,帮助开发者构建分布式系统。
- 负载均衡策略:
- RandomLoadBalance:随机策略,随机选择一个服务提供者。
- RoundRobinLoadBalance:轮询策略,按照顺序依次选择服务提供者。
- LeastActiveLoadBalance:最少活跃调用数策略,优先选择活跃调用数最少的服务提供者。
- ConsistentHashLoadBalance:一致性哈希策略,根据请求的某些参数计算哈希值,将请求路由到固定的服务提供者。