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

36 阅读2分钟

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

在互联网大厂的一间安静面试室内,严肃的面试官正对面坐着略显紧张的求职者王铁牛。一场关于 Java 核心知识的面试即将拉开帷幕。

第一轮面试开始 面试官:首先问你几个基础问题。Java 中基本数据类型有哪些? 王铁牛:Java 的基本数据类型有 byte、short、int、long、float、double、char、boolean。 面试官:回答得不错。那你说说面向对象的四大特性是什么? 王铁牛:面向对象的四大特性是封装、继承、多态和抽象。 面试官:很好。再问你,HashMap 和 Hashtable 有什么区别? 王铁牛:HashMap 是非线程安全的,允许 null 键和 null 值;Hashtable 是线程安全的,不允许 null 键和 null 值。

第二轮面试开始 面试官:进入并发相关的问题。JUC 包是什么,有什么作用? 王铁牛:JUC 就是 java.util.concurrent 包,它提供了并发编程的工具类,能方便我们处理多线程编程。 面试官:那你讲讲线程池有哪些重要的参数? 王铁牛:线程池重要参数有核心线程数、最大线程数、线程空闲时间、时间单位、任务队列、线程工厂和拒绝策略。 面试官:当线程池的任务队列满了,并且线程数达到最大线程数,再提交任务会怎样? 王铁牛:这时候会触发拒绝策略,不同的拒绝策略有不同的处理方式,比如抛出异常、直接丢弃等。

第三轮面试开始 面试官:现在来谈谈框架相关。Spring 的 IOC 是什么,它的实现原理是什么? 王铁牛:IOC 就是控制反转,把对象的创建和依赖关系的管理交给 Spring 容器。原理嘛……大概就是通过反射创建对象。 面试官:Spring Boot 是如何实现自动配置的? 王铁牛:这个……好像是根据 classpath 里的依赖和配置文件来自动配置的,但具体不太清楚。 面试官:MyBatis 中 #{} 和 ${} 的区别是什么? 王铁牛:嗯……感觉有点区别,但具体我也说不太明白。

面试接近尾声,面试官推了推眼镜,严肃地说:“今天的面试就到这里,你先回家等通知吧。我们会综合评估后尽快给你反馈。”

问题答案

  1. Java 中基本数据类型有哪些? Java 的基本数据类型分为四类八种:
  • 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
  • 浮点类型:float(4 字节)、double(8 字节)。
  • 字符类型:char(2 字节)。
  • 布尔类型:boolean(理论上 1 位,但在 Java 里通常按 1 字节处理)。
  1. 面向对象的四大特性是什么?
  • 封装:将数据和操作数据的方法绑定在一起,隐藏对象的内部实现细节,只对外提供必要的接口。这样可以提高代码的安全性和可维护性。
  • 继承:子类可以继承父类的属性和方法,并且可以扩展新的属性和方法。继承可以实现代码的复用,同时体现了类之间的层次关系。
  • 多态:同一个行为具有多个不同表现形式或形态的能力。多态通过继承、接口实现和方法重写来实现,提高了代码的灵活性和可扩展性。
  • 抽象:抽象是将一类对象的共同特征总结出来构造类的过程。抽象类和接口是实现抽象的重要手段,它们定义了一组抽象的方法,具体的实现由子类完成。
  1. HashMap 和 Hashtable 有什么区别?
  • 线程安全性:HashMap 是非线程安全的,在多线程环境下可能会出现数据不一致的问题;Hashtable 是线程安全的,它的方法都使用了 synchronized 关键字进行同步,但这也导致了它的性能相对较低。
  • 空值处理:HashMap 允许键和值为 null;Hashtable 不允许键和值为 null,如果尝试插入 null 会抛出 NullPointerException。
  • 继承和历史:HashMap 继承自 AbstractMap 类;Hashtable 继承自 Dictionary 类,是 Java 早期的类。
  1. JUC 包是什么,有什么作用? JUC 即 java.util.concurrent 包,是 Java 5 引入的用于并发编程的工具包。它提供了一系列的类和接口,用于简化多线程编程,提高并发性能和安全性。例如:
  • 线程池相关类:如 ExecutorService、ThreadPoolExecutor 等,方便管理和复用线程。
  • 并发集合类:如 ConcurrentHashMap、CopyOnWriteArrayList 等,在多线程环境下可以安全地进行操作。
  • 锁机制:如 ReentrantLock、ReadWriteLock 等,提供了比 synchronized 更灵活的锁控制。
  • 原子类:如 AtomicInteger、AtomicLong 等,用于实现原子操作,避免多线程环境下的数据竞争。
  1. 线程池有哪些重要的参数? 线程池的重要参数有:
  • corePoolSize(核心线程数):线程池中的核心线程数量,当提交的任务数小于核心线程数时,线程池会创建新的线程来执行任务。
  • maximumPoolSize(最大线程数):线程池允许创建的最大线程数量。
  • keepAliveTime(线程空闲时间):当线程池中的线程数量超过核心线程数时,多余的空闲线程在等待新任务的最长时间,超过这个时间线程会被销毁。
  • unit(时间单位):keepAliveTime 的时间单位,如 TimeUnit.SECONDS、TimeUnit.MILLISECONDS 等。
  • workQueue(任务队列):用于存储提交但尚未执行的任务的队列,常见的有 ArrayBlockingQueue、LinkedBlockingQueue 等。
  • threadFactory(线程工厂):用于创建线程的工厂类,可以自定义线程的名称、优先级等属性。
  • handler(拒绝策略):当任务队列已满且线程数达到最大线程数时,新提交的任务会触发拒绝策略。常见的拒绝策略有 AbortPolicy(抛出异常)、CallerRunsPolicy(由调用线程处理任务)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。
  1. 当线程池的任务队列满了,并且线程数达到最大线程数,再提交任务会怎样? 此时会触发线程池的拒绝策略,不同的拒绝策略处理方式不同:
  • AbortPolicy:直接抛出 RejectedExecutionException 异常,阻止系统正常工作。
  • CallerRunsPolicy:将任务返回给调用者线程来执行,这样可以减缓新任务的提交速度。
  • DiscardPolicy:直接丢弃新提交的任务,不做任何处理。
  • DiscardOldestPolicy:丢弃任务队列中最老的任务,然后尝试将新任务加入队列。
  1. Spring 的 IOC 是什么,它的实现原理是什么? IOC(Inversion of Control,控制反转)是 Spring 框架的核心特性之一。传统的对象创建和依赖关系管理是由对象本身负责,而在 IOC 模式下,对象的创建和依赖关系的管理由 Spring 容器负责,对象只需要被动地接受依赖注入。 实现原理主要基于反射和配置文件(如 XML 配置或注解):
  • 配置阶段:通过 XML 文件或注解定义 Bean 的信息,包括 Bean 的类名、属性和依赖关系等。
  • 容器初始化阶段:Spring 容器读取配置文件,解析其中的 Bean 定义,使用反射机制创建 Bean 对象,并根据配置的依赖关系进行注入。
  • 使用阶段:应用程序从 Spring 容器中获取 Bean 对象并使用。
  1. Spring Boot 是如何实现自动配置的? Spring Boot 的自动配置是基于 Spring 的条件注解和类路径扫描实现的:
  • 自动配置类:Spring Boot 提供了一系列的自动配置类,这些类位于 spring-boot-autoconfigure 模块中,它们使用 @Configuration 注解进行标记。
  • 条件注解:自动配置类中使用了各种条件注解,如 @ConditionalOnClass、@ConditionalOnMissingBean 等,根据类路径中是否存在特定的类、是否已经定义了特定的 Bean 等条件来决定是否进行自动配置。
  • META - INF/spring.factories 文件:该文件中列出了所有的自动配置类,Spring Boot 在启动时会读取这个文件,加载其中的自动配置类。
  • 应用程序启动:当 Spring Boot 应用程序启动时,Spring Boot 会根据类路径中的依赖和配置文件,结合条件注解来判断哪些自动配置类需要生效,从而实现自动配置。
  1. MyBatis 中 #{} 和 ${} 的区别是什么?
  • 语法和处理方式:
    • #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 占位符,然后使用 PreparedStatement 进行预编译,再将参数设置到占位符中。这样可以防止 SQL 注入攻击。
    • :是字符串替换,MyBatis在处理{}:是字符串替换,MyBatis 在处理 {} 时,会直接将 ${} 替换为参数的值,相当于直接拼接 SQL 语句。
  • 使用场景:
    • #{}:适用于参数传递,如查询条件、插入值等。
    • :主要用于动态表名、动态列名等,因为预编译无法处理表名和列名的替换。但使用{}:主要用于动态表名、动态列名等,因为预编译无法处理表名和列名的替换。但使用 {} 时需要注意 SQL 注入的风险,要确保参数的安全性。