《互联网大厂 Java 面试:核心知识、框架与中间件大考验》

64 阅读5分钟

互联网大厂 Java 面试:核心知识、框架与中间件大考验

在互联网大厂的一间安静的面试室内,严肃的面试官坐在桌前,对面是紧张又怀揣期待的求职者王铁牛。一场关于 Java 技术的严峻考验即将拉开帷幕。

第一轮提问 面试官:首先问你几个基础的 Java 核心知识问题。Java 中基本数据类型有哪些? 王铁牛:这个我知道,Java 基本数据类型有 byte、short、int、long、float、double、char、boolean。 面试官:回答得不错。那在 Java 里,重载和重写的区别是什么? 王铁牛:重载是在同一个类中,方法名相同但参数列表不同;重写是子类对父类中允许访问的方法的实现过程进行重新编写,返回值和形参都不能改变。 面试官:很好,理解得很清晰。那说说 String、StringBuilder 和 StringBuffer 的区别。 王铁牛:String 是不可变的,每次对 String 的操作都会生成新的 String 对象;StringBuilder 是可变的,线程不安全,效率高;StringBuffer 也是可变的,但是线程安全,效率相对低一些。 面试官:非常棒,基础很扎实。接下来我们进入多线程相关的问题。什么是线程安全? 王铁牛:线程安全就是在多线程环境下,对共享资源的访问不会导致数据的不一致或者其他异常问题。

第二轮提问 面试官:刚才你提到了线程安全,那在 Java 的 JUC(Java 并发工具包)里,有哪些类可以实现线程安全的操作? 王铁牛:嗯……有那个……好像有 ConcurrentHashMap 吧,其他的我有点想不起来了。 面试官:ConcurrentHashMap 是对的。那说说线程池的好处是什么? 王铁牛:线程池嘛,就是可以复用线程,减少创建和销毁线程的开销,大概就是这样。 面试官:没错。那如果线程池的任务队列满了,新提交的任务会怎样处理? 王铁牛:这个……我不太确定,可能会直接丢弃任务吧。 面试官:再问你一个,在 JVM 中,堆和栈的区别是什么? 王铁牛:堆和栈啊,堆好像是放对象的,栈是放变量的,具体的我也说不太清楚。

第三轮提问 面试官:接下来聊聊框架相关的问题。在 Spring 中,依赖注入有哪些方式? 王铁牛:我记得有构造器注入和属性注入,其他的不太记得了。 面试官:Spring Boot 相对于 Spring 有哪些优势? 王铁牛:Spring Boot 好像更方便,能快速搭建项目,减少配置,具体怎么实现的我不太懂。 面试官:MyBatis 中 #{} 和 ${} 的区别是什么? 王铁牛:这个……我只知道它们都能传参数,区别不太清楚。 面试官:Dubbo 是一个分布式服务框架,说说它的主要功能有哪些? 王铁牛:Dubbo 好像能远程调用服务,其他的我就不知道了。 面试官:RabbitMQ 是常用的消息队列,它的消息确认机制是怎样的? 王铁牛:消息确认机制……我不太明白这是啥。 面试官:xxl - job 是一个分布式任务调度平台,它的执行流程是怎样的? 王铁牛:这个我没了解过。 面试官:Redis 是常用的缓存数据库,说说 Redis 的持久化方式有哪些? 王铁牛:Redis 持久化……我好像听说过 RDB,其他的就不知道了。

面试官:今天的面试就到这里,你先回家等通知吧。我们会综合评估后尽快给你反馈。

答案详解

  1. Java 基本数据类型
    • byte:8 位,有符号,取值范围 -128 到 127。
    • short:16 位,有符号,取值范围 -32768 到 32767。
    • int:32 位,有符号,取值范围 -2147483648 到 2147483647。
    • long:64 位,有符号,取值范围 -9223372036854775808 到 9223372036854775807,定义时需在数字后面加 L。
    • float:32 位,单精度浮点数,定义时需在数字后面加 F。
    • double:64 位,双精度浮点数。
    • char:16 位,无符号,用于表示单个字符,用单引号括起来。
    • boolean:只有两个值,true 和 false。
  2. 重载和重写的区别
    • 重载:发生在同一个类中,方法名相同但参数列表不同(参数的类型、个数、顺序不同),与返回值类型无关。编译器根据调用方法时传递的参数来决定调用哪个重载方法。
    • 重写:发生在子类和父类之间,子类重写父类的方法。要求方法名、参数列表、返回值类型必须相同(子类返回值类型可以是父类返回值类型的子类),访问修饰符不能比父类更严格。重写是实现多态的重要手段。
  3. String、StringBuilder 和 StringBuffer 的区别
    • String:是不可变类,一旦创建,其值不能被改变。每次对 String 进行拼接等操作时,都会创建一个新的 String 对象,会产生大量的临时对象,效率较低。
    • StringBuilder:是可变类,线程不安全。它内部使用一个可变的字符数组来存储字符串,在进行字符串拼接等操作时,不会创建新的对象,而是直接在原数组上进行修改,效率较高。
    • StringBuffer:也是可变类,线程安全。它和 StringBuilder 的功能类似,但在方法上使用了 synchronized 关键字进行同步,保证了线程安全,但也因此在性能上比 StringBuilder 稍低。
  4. 线程安全:在多线程环境下,当多个线程同时访问共享资源时,如果对资源的访问不会导致数据的不一致、数据丢失、程序崩溃等异常问题,就称该资源是线程安全的。实现线程安全的方法有使用 synchronized 关键字、Lock 接口、原子类等。
  5. JUC 中实现线程安全操作的类
    • ConcurrentHashMap:是线程安全的哈希表,在多线程环境下可以高效地进行读写操作。它采用分段锁或 CAS(Compare - And - Swap)等技术来保证线程安全。
    • CopyOnWriteArrayList:是线程安全的列表,在进行写操作时会复制一份新的数组,然后在新数组上进行修改,最后将引用指向新数组,读操作则直接在原数组上进行,这样可以保证读操作的高效性。
    • BlockingQueue:是一个阻塞队列接口,有多种实现类,如 ArrayBlockingQueue、LinkedBlockingQueue 等。它可以在队列为空或已满时进行阻塞操作,常用于生产者 - 消费者模型中。
  6. 线程池的好处
    • 减少线程创建和销毁的开销:线程的创建和销毁需要消耗系统资源,使用线程池可以复用已有的线程,避免频繁创建和销毁线程带来的性能损耗。
    • 提高响应速度:当有新任务提交时,线程池可以立即从空闲线程中取出线程来执行任务,而不需要等待线程的创建。
    • 便于管理:可以对线程池中的线程进行统一的管理,如设置线程的数量、线程的优先级等。
  7. 线程池任务队列满时新任务的处理策略
    • AbortPolicy:默认的处理策略,直接抛出 RejectedExecutionException 异常。
    • CallerRunsPolicy:由调用线程池的线程来执行该任务。
    • DiscardPolicy:直接丢弃该任务,不做任何处理。
    • DiscardOldestPolicy:丢弃任务队列中最老的任务,然后将新任务加入队列。
  8. JVM 中堆和栈的区别
    • 存储内容
      • :主要用于存储对象实例和数组,是所有线程共享的内存区域。
      • :每个线程都有自己独立的栈,栈中存储局部变量、方法调用的上下文信息等。
    • 内存分配和回收方式
      • :内存分配和回收由垃圾回收器(GC)负责,开发人员一般不需要手动管理。
      • :栈的内存分配和回收是由系统自动完成的,方法调用时入栈,方法返回时出栈。
    • 内存空间大小
      • :一般比较大,用于存储大量的对象。
      • :相对较小,每个线程的栈空间大小是固定的。
  9. Spring 依赖注入的方式
    • 构造器注入:通过构造方法来注入依赖对象,保证对象在创建时就完成依赖的初始化。
    • 属性注入:通过 setter 方法来注入依赖对象,比较灵活,可以在对象创建后再注入依赖。
    • 接口注入:实现特定的接口来完成依赖注入,这种方式使用较少。
  10. Spring Boot 相对于 Spring 的优势
    • 快速搭建项目:Spring Boot 提供了大量的 Starter 依赖,只需要添加相应的依赖,就可以快速搭建一个完整的项目,减少了繁琐的配置。
    • 自动配置:Spring Boot 会根据项目中添加的依赖自动进行配置,开发人员可以根据需要进行定制化配置。
    • 嵌入式服务器:Spring Boot 内置了 Tomcat、Jetty 等嵌入式服务器,不需要手动部署服务器,直接运行项目即可。
    • 监控和管理:Spring Boot Actuator 提供了丰富的监控和管理功能,如健康检查、性能指标监控等。
  11. MyBatis 中 #{} 和 ${} 的区别
    • #{}:是预编译处理,MyBatis 会将 #{} 替换为占位符?,然后使用 PreparedStatement 来执行 SQL 语句,可以防止 SQL 注入攻击。
    • **:是字符串替换,MyBatis会直接将{}**:是字符串替换,MyBatis 会直接将 {} 替换为实际的值,可能会导致 SQL 注入攻击,一般用于动态表名、动态列名等场景。
  12. Dubbo 的主要功能
    • 远程服务调用:可以实现不同服务之间的远程调用,就像调用本地方法一样简单。
    • 服务注册与发现:Dubbo 支持多种注册中心,如 Zookeeper、Nacos 等,服务提供者可以将自己的服务注册到注册中心,服务消费者可以从注册中心获取服务提供者的地址信息。
    • 负载均衡:Dubbo 提供了多种负载均衡策略,如随机、轮询、最少活跃调用数等,可以根据不同的场景选择合适的负载均衡策略。
    • 集群容错:当服务提供者出现故障时,Dubbo 可以通过集群容错机制来保证服务的可用性,如失败重试、快速失败等。
  13. RabbitMQ 的消息确认机制
    • 生产者确认机制:生产者将消息发送到 RabbitMQ 后,RabbitMQ 会返回一个确认消息给生产者,告知生产者消息是否已经成功接收。有两种模式:
      • 异步确认:生产者发送消息后继续执行其他任务,RabbitMQ 通过回调函数告知生产者消息的确认情况。
      • 同步确认:生产者发送消息后会阻塞,直到收到 RabbitMQ 的确认消息。
    • 消费者确认机制:消费者从 RabbitMQ 接收消息后,需要向 RabbitMQ 发送确认消息,告知 RabbitMQ 消息已经被成功处理。有三种模式:
      • 自动确认:消费者接收到消息后,RabbitMQ 会自动认为消息已经被处理。
      • 手动确认:消费者需要手动调用确认方法来告知 RabbitMQ 消息已经被处理。
      • 批量确认:消费者可以批量确认多条消息。
  14. xxl - job 的执行流程
    • 任务配置:在 xxl - job 管理平台上配置任务信息,包括任务的调度策略、执行器信息、任务代码等。
    • 任务注册:执行器启动时,会向 xxl - job 调度中心注册自己的信息。
    • 任务调度:调度中心根据任务的调度策略,在合适的时间触发任务。
    • 任务分发:调度中心将任务请求发送给对应的执行器。
    • 任务执行:执行器接收到任务请求后,执行任务代码。
    • 结果反馈:执行器将任务的执行结果反馈给调度中心。
  15. Redis 的持久化方式
    • RDB(Redis Database):是一种快照持久化方式,将 Redis 在某一时刻的内存数据以二进制文件的形式保存到磁盘上。RDB 持久化可以通过手动执行 SAVE 或 BGSAVE 命令触发,也可以配置定期自动触发。RDB 的优点是文件紧凑,恢复速度快;缺点是可能会丢失最后一次快照到故障发生之间的数据。
    • AOF(Append Only File):是一种日志持久化方式,将 Redis 执行的写命令以日志的形式追加到文件末尾。AOF 持久化可以保证数据的完整性,最多只会丢失 1 秒的数据。AOF 的优点是数据安全性高;缺点是文件较大,恢复速度相对较慢。