互联网大厂 Java 面试:核心知识、框架与中间件大考验
王铁牛怀揣着紧张与期待,走进了这家互联网大厂的面试间。严肃的面试官早已坐在那里,一场对 Java 技术深度与广度的考验即将拉开帷幕。
第一轮提问
- 面试官:首先问几个 Java 核心知识的问题。Java 中基本数据类型有哪些?
- 王铁牛:这个我知道,Java 基本数据类型有 byte、short、int、long、float、double、char、boolean。
- 面试官:不错,回答得很准确。那说说面向对象的四大特性。
- 王铁牛:面向对象的四大特性是封装、继承、多态和抽象。封装就是把数据和操作数据的方法绑定起来,隐藏内部实现细节;继承是子类继承父类的属性和方法;多态允许不同类的对象对同一消息做出响应;抽象是把现实世界中的事物抽象成类。
- 面试官:回答得很好。那 String、StringBuilder 和 StringBuffer 有什么区别?
- 王铁牛:String 是不可变的,每次对 String 进行操作都会创建新的对象;StringBuilder 是可变的,线程不安全,性能较高;StringBuffer 也是可变的,但是线程安全,性能相对较低。
第二轮提问
- 面试官:接下来谈谈 JUC 和多线程相关的。什么是线程池,为什么要使用线程池?
- 王铁牛:线程池就是预先创建好一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务。使用线程池可以减少线程创建和销毁的开销,提高系统的性能,还能更好地管理线程。
- 面试官:很好,那线程池有哪些核心参数?
- 王铁牛:线程池的核心参数有 corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(线程空闲存活时间)、TimeUnit(时间单位)、workQueue(任务队列)、threadFactory(线程工厂)和 RejectedExecutionHandler(拒绝策略)。
- 面试官:说说常见的线程池有哪些,它们的特点是什么?
- 王铁牛:常见的线程池有 FixedThreadPool,它的核心线程数和最大线程数相等,适用于执行长期的任务;CachedThreadPool,核心线程数为 0,最大线程数为 Integer.MAX_VALUE,会根据任务数量动态创建和销毁线程,适用于执行大量短时间的任务;SingleThreadExecutor,只有一个核心线程,保证任务按顺序执行。
第三轮提问
- 面试官:现在问一些框架和中间件的问题。Spring 的核心特性有哪些?
- 王铁牛:Spring 的核心特性有 IoC(控制反转)和 AOP(面向切面编程)。IoC 是把对象的创建和依赖关系的管理交给 Spring 容器;AOP 是在不修改原有代码的基础上,对程序进行增强。
- 面试官:那 Spring Boot 相对于 Spring 有什么优势?
- 王铁牛:Spring Boot 简化了 Spring 的配置,通过自动配置和 Starter 依赖,能快速搭建项目,还内置了服务器,方便开发和部署。
- 面试官:MyBatis 中 #{} 和 ${} 的区别是什么?
- 王铁牛:#{} 是预编译处理,会把参数当成一个占位符,能防止 SQL 注入;${} 是字符串替换,会直接把参数替换到 SQL 语句中,可能存在 SQL 注入风险。
面试总结
面试官扶了扶眼镜,看着王铁牛说道:“在这三轮面试中,你对一些基础且重要的 Java 知识回答得比较准确,像 Java 基本数据类型、面向对象特性、线程池的基本概念和常见线程池类型,以及 Spring 的核心特性和 Spring Boot 的优势等方面,展现出了你对这些知识点有一定的掌握。然而,当涉及到一些细节和更深入的内容时,表现就不太理想了。
比如在回答一些原理性和实际应用场景的问题时,你的回答不够完整和清晰。对于复杂问题,你没有很好地展现出对知识的深度理解和灵活运用能力。在技术面试中,不仅要知道基本概念,更要理解其背后的原理和在实际业务中的应用。例如在解释一些框架和中间件的特性时,应该结合具体的业务场景来说明它们的优势和作用。
综合来看,你的基础知识有一定的积累,但在知识的深度和实际应用能力上还有待提高。接下来我们会综合评估你的整体表现,你先回家等通知吧。”
问题答案详细解析
第一轮问题答案
- Java 中基本数据类型有哪些?
- Java 有 8 种基本数据类型,分为 4 类:
- 整数类型:byte(1 字节,-128 到 127)、short(2 字节,-32768 到 32767)、int(4 字节,-2147483648 到 2147483647)、long(8 字节,-9223372036854775808 到 9223372036854775807)。
- 浮点类型:float(4 字节,单精度浮点数)、double(8 字节,双精度浮点数)。
- 字符类型:char(2 字节,用于表示单个字符,采用 Unicode 编码)。
- 布尔类型:boolean(只有两个值,true 和 false)。
- Java 有 8 种基本数据类型,分为 4 类:
- 说说面向对象的四大特性。
- 封装:将数据(属性)和操作数据的方法绑定在一起,隐藏对象的内部实现细节,只对外提供公共的访问接口。这样可以提高代码的安全性和可维护性,例如一个类的属性可以通过 private 修饰,然后提供 public 的 getter 和 setter 方法来访问和修改属性。
- 继承:子类可以继承父类的属性和方法,从而实现代码的复用。子类还可以对父类的方法进行重写,以实现自己的特定功能。通过继承可以构建出类的层次结构,例如动物类是父类,猫类和狗类可以继承动物类。
- 多态:允许不同类的对象对同一消息做出不同的响应。多态的实现方式有方法重载和方法重写。方法重载是在一个类中定义多个同名但参数不同的方法;方法重写是子类重写父类的方法。多态可以提高代码的灵活性和可扩展性。
- 抽象:把现实世界中的事物抽象成类,提取出共同的属性和行为。抽象类和接口是实现抽象的重要方式。抽象类不能实例化,它可以包含抽象方法,子类必须实现抽象方法;接口是一种特殊的抽象类型,只包含抽象方法和常量。
- String、StringBuilder 和 StringBuffer 有什么区别?
- String:是不可变的,一旦创建,其值不能被修改。每次对 String 进行操作,如拼接、替换等,都会创建一个新的 String 对象。这是因为 String 类内部使用 final 修饰的字符数组来存储字符串。由于其不可变性,String 适用于字符串不经常变化的场景,如常量字符串。
- StringBuilder:是可变的,它内部使用动态数组来存储字符序列。线程不安全,性能较高,因为它没有进行线程同步操作。适用于单线程环境下对字符串进行频繁修改的场景。
- StringBuffer:也是可变的,同样使用动态数组存储字符序列。但它是线程安全的,因为它的方法都使用了 synchronized 关键字进行同步。由于线程同步会带来一定的性能开销,所以 StringBuffer 的性能相对较低,适用于多线程环境下对字符串进行频繁修改的场景。
第二轮问题答案
- 什么是线程池,为什么要使用线程池?
- 线程池:是一种线程管理机制,预先创建一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务。任务执行完后,线程不会销毁,而是返回线程池等待下一个任务。
- 使用线程池的原因:
- 减少线程创建和销毁的开销:线程的创建和销毁需要消耗系统资源,使用线程池可以避免频繁创建和销毁线程,提高系统的性能。
- 提高响应速度:当有任务提交时,线程池中已经有空闲的线程可以立即执行任务,不需要等待线程的创建。
- 更好地管理线程:可以控制线程的数量,避免创建过多的线程导致系统资源耗尽。还可以对线程进行监控和管理,例如设置线程的优先级、生命周期等。
- 线程池有哪些核心参数?
- corePoolSize:核心线程数,线程池初始化时创建的线程数量。当有任务提交时,首先会使用核心线程来执行任务。
- maximumPoolSize:最大线程数,线程池允许创建的最大线程数量。当任务队列已满,且核心线程都在执行任务时,会创建新的线程,直到达到最大线程数。
- keepAliveTime:线程空闲存活时间,当线程空闲时间超过这个时间,且线程数量超过核心线程数时,多余的线程会被销毁。
- TimeUnit:时间单位,用于指定 keepAliveTime 的时间单位,如 TimeUnit.SECONDS 表示秒。
- workQueue:任务队列,用于存储提交的任务。当核心线程都在执行任务时,新提交的任务会被放入任务队列中等待执行。常见的任务队列有 ArrayBlockingQueue、LinkedBlockingQueue 等。
- threadFactory:线程工厂,用于创建线程。可以通过自定义线程工厂来设置线程的名称、优先级等属性。
- RejectedExecutionHandler:拒绝策略,当任务队列已满,且线程数量达到最大线程数时,新提交的任务会被拒绝。常见的拒绝策略有 AbortPolicy(直接抛出异常)、CallerRunsPolicy(由调用线程来执行任务)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃任务队列中最老的任务)。
- 说说常见的线程池有哪些,它们的特点是什么?
- FixedThreadPool:通过 Executors.newFixedThreadPool(int nThreads) 方法创建,核心线程数和最大线程数相等,都是传入的参数 nThreads。任务队列使用 LinkedBlockingQueue,该队列是无界队列。适用于执行长期的任务,因为线程数量固定,可以避免创建过多的线程导致系统资源耗尽。
- CachedThreadPool:通过 Executors.newCachedThreadPool() 方法创建,核心线程数为 0,最大线程数为 Integer.MAX_VALUE。线程空闲存活时间为 60 秒,任务队列使用 SynchronousQueue,该队列不存储任务,只负责线程之间的同步。当有任务提交时,会创建新的线程来执行任务,如果线程空闲时间超过 60 秒,线程会被销毁。适用于执行大量短时间的任务,因为可以根据任务数量动态创建和销毁线程。
- SingleThreadExecutor:通过 Executors.newSingleThreadExecutor() 方法创建,只有一个核心线程,最大线程数也为 1。任务队列使用 LinkedBlockingQueue,保证任务按顺序执行。适用于需要保证任务顺序执行的场景,例如数据库操作。
第三轮问题答案
- Spring 的核心特性有哪些?
- IoC(控制反转):也称为依赖注入(DI),是指把对象的创建和依赖关系的管理交给 Spring 容器。在传统的编程中,对象的创建和依赖关系是由代码自己控制的,而在 Spring 中,通过配置文件或注解的方式,让 Spring 容器来负责对象的创建和依赖关系的注入。这样可以降低代码的耦合度,提高代码的可维护性和可测试性。
- AOP(面向切面编程):是在不修改原有代码的基础上,对程序进行增强。AOP 可以将一些通用的功能,如日志记录、事务管理等,从业务逻辑中分离出来,形成独立的切面。在程序运行时,通过动态代理的方式,将切面的功能织入到目标对象的方法中。AOP 可以提高代码的复用性和可维护性。
- Spring Boot 相对于 Spring 有什么优势?
- 简化配置:Spring Boot 提供了自动配置机制,通过分析项目的依赖和配置,自动为项目生成合适的配置。开发者只需要添加相应的 Starter 依赖,Spring Boot 就会自动完成大部分的配置工作,减少了繁琐的 XML 配置文件。
- 快速搭建项目:Spring Boot 提供了 Spring Initializr 工具,可以快速生成项目的骨架,包含了项目的基本结构和依赖。开发者可以根据自己的需求选择不同的 Starter 依赖,快速搭建出一个可用的项目。
- 内置服务器:Spring Boot 内置了 Tomcat、Jetty 等服务器,不需要单独部署服务器,直接运行项目的主类就可以启动服务器。这样方便了开发和测试,也简化了项目的部署过程。
- 生产级特性:Spring Boot 提供了一系列生产级特性,如健康检查、监控、配置管理等。这些特性可以帮助开发者更好地管理和监控项目的运行状态。
- MyBatis 中 #{} 和 ${} 的区别是什么?
- #{}:是预编译处理,在 SQL 语句中,#{} 会被当成一个占位符(?),MyBatis 会使用 PreparedStatement 来执行 SQL 语句。在执行 SQL 语句之前,会将参数进行预编译,防止 SQL 注入。例如,在 SQL 语句中使用 #{} 传递参数时,会将参数的值进行转义,避免恶意的 SQL 注入攻击。
- **{} 会直接把参数的值替换到 SQL 语句中。由于是直接替换,不会进行预编译,所以可能存在 SQL 注入风险。例如,如果用户输入的参数包含恶意的 SQL 语句,使用 {} 时需要谨慎,一般只用于表名、列名等静态参数的替换。