互联网大厂 Java 面试:核心知识、框架与中间件大考验
王铁牛怀揣着紧张与期待,坐在了互联网大厂的面试会议室里。严肃的面试官早已端坐在对面,一场考验技术实力的面试即将拉开帷幕。
第一轮提问 面试官:首先问几个 Java 核心知识的问题。Java 中基本数据类型有哪些? 王铁牛:这个我知道,有 byte、short、int、long、float、double、char、boolean。 面试官:回答正确。那你说说 Java 中重载和重写的区别是什么? 王铁牛:重载是在一个类中,方法名相同但参数列表不同;重写是子类对父类中方法的实现进行重新编写,方法名、参数列表和返回值类型都一样。 面试官:非常好。再问一个,Java 中 String、StringBuffer 和 StringBuilder 的区别是什么? 王铁牛:String 是不可变的,每次对 String 的操作都会产生一个新的 String 对象;StringBuffer 是可变的,而且是线程安全的;StringBuilder 也是可变的,但它是非线程安全的。 面试官:回答得很清晰,基础很扎实。
第二轮提问 面试官:接下来考考你 JUC 和多线程相关的知识。什么是线程池?为什么要使用线程池? 王铁牛:线程池就是预先创建好一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务。使用线程池可以减少线程创建和销毁的开销,提高性能,还能控制并发线程的数量。 面试官:不错。那线程池有哪些核心参数? 王铁牛:这个……有核心线程数、最大线程数、线程存活时间、阻塞队列、拒绝策略。 面试官:回答正确。再说说 CountDownLatch 和 CyclicBarrier 的区别。 王铁牛:嗯……它们好像都是用于线程同步的,具体区别我有点不太清楚。 面试官:这个问题相对复杂一些,你对前面的回答还是不错的。
第三轮提问 面试官:现在来聊聊框架和中间件。Spring 框架的核心特性有哪些? 王铁牛:Spring 的核心特性有 IoC(控制反转)和 AOP(面向切面编程)。IoC 是把对象的创建和依赖关系的管理交给 Spring 容器,AOP 是在不修改原有代码的基础上对程序进行增强。 面试官:很好。那 Spring Boot 相对于 Spring 有哪些优势? 王铁牛:Spring Boot 可以快速搭建项目,它有自动配置的功能,减少了大量的配置文件,还提供了嵌入式服务器,方便开发和部署。 面试官:回答得很准确。那 MyBatis 中 #{} 和 ${} 的区别是什么? 王铁牛:这个……我记得一个是预编译,一个不是,但具体哪个对应哪个我有点乱了。 面试官:这个细节你还需要再巩固一下。
面试接近尾声,面试官摘下眼镜,看着王铁牛说:“今天的面试就到这里。整体来看,你对一些基础的 Java 核心知识掌握得比较扎实,像基本数据类型、重载重写、线程池的基本概念等都回答得很好,这说明你有一定的知识储备。在 JUC 和框架方面,你也能说出一些关键的点,不过对于一些复杂的概念和细节,比如 CountDownLatch 和 CyclicBarrier 的区别、MyBatis 中 #{} 和 ${} 的区别,还存在理解不深入的情况。我们需要综合评估一下你的面试表现,你先回家等通知吧,后续我们会通过邮件或者电话和你沟通结果。”
答案详细解析
- Java 基本数据类型:
- byte:8 位,有符号,范围是 -128 到 127。
- short:16 位,有符号,范围是 -32768 到 32767。
- int:32 位,有符号,范围是 -2147483648 到 2147483647。
- long:64 位,有符号,范围非常大。定义 long 类型常量时,后面要加 L 或 l。
- float:32 位,单精度浮点数。定义 float 类型常量时,后面要加 F 或 f。
- double:64 位,双精度浮点数,是 Java 中默认的浮点类型。
- char:16 位,无符号,用于表示单个字符,使用单引号括起来。
- boolean:只有两个值,true 和 false,用于逻辑判断。
- 重载和重写的区别:
- 重载:发生在同一个类中,方法名相同,但参数列表不同(参数的类型、个数、顺序不同)。重载与方法的返回值类型、访问修饰符无关。重载的目的是为了提供更多的调用方式,方便使用。
- 重写:发生在子类和父类之间,子类重写父类的方法。方法名、参数列表和返回值类型必须相同(返回值类型在 Java 5 及以后可以是协变返回类型,即子类方法的返回值类型可以是父类方法返回值类型的子类)。重写的方法不能比被重写的方法有更严格的访问权限,不能抛出比被重写方法更多的异常。重写的目的是为了实现多态,让子类可以根据自身的需求来实现父类的方法。
- String、StringBuffer 和 StringBuilder 的区别:
- String:是不可变的对象,一旦创建,其值不能被改变。每次对 String 进行操作(如拼接、替换等),都会创建一个新的 String 对象,这会导致大量的内存开销。因此,在需要频繁修改字符串的场景下,不建议使用 String。
- StringBuffer:是可变的对象,它的方法是线程安全的,因为它的很多方法都使用了 synchronized 关键字进行同步。适用于多线程环境下需要频繁修改字符串的场景,但由于同步机制会带来一定的性能开销。
- StringBuilder:也是可变的对象,和 StringBuffer 类似,但它是非线程安全的。在单线程环境下,由于不需要进行同步操作,它的性能比 StringBuffer 高,因此在单线程环境下需要频繁修改字符串时,建议使用 StringBuilder。
- 线程池:
- 定义:线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池预先创建一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务,任务执行完后线程不会销毁,而是返回到线程池中等待下一个任务。
- 使用原因:
- 减少线程创建和销毁的开销:线程的创建和销毁需要消耗系统资源,频繁地创建和销毁线程会影响系统性能。使用线程池可以复用线程,减少了线程创建和销毁的次数,从而提高了系统的性能。
- 提高响应速度:当有任务提交时,线程池中如果有空闲线程,任务可以立即被执行,不需要等待线程的创建,从而提高了响应速度。
- 控制并发线程的数量:可以通过线程池的参数来控制并发线程的数量,避免过多的线程同时运行导致系统资源耗尽,从而提高系统的稳定性。
- 线程池的核心参数:
- corePoolSize:核心线程数,线程池在初始化时会创建的线程数量。当有任务提交时,首先会使用核心线程来执行任务。
- maximumPoolSize:最大线程数,线程池允许创建的最大线程数量。当核心线程都在执行任务,且任务队列已满时,线程池会创建新的线程,直到达到最大线程数。
- keepAliveTime:线程存活时间,当线程池中的线程数量超过核心线程数时,多余的线程在空闲一段时间后会被销毁,这个空闲时间就是 keepAliveTime。
- workQueue:阻塞队列,用于存储等待执行的任务。当核心线程都在执行任务时,新提交的任务会被放入阻塞队列中。常见的阻塞队列有 ArrayBlockingQueue、LinkedBlockingQueue 等。
- rejectedExecutionHandler:拒绝策略,当线程池中的线程数量达到最大线程数,且阻塞队列也已满时,新提交的任务会被拒绝执行,这时会调用拒绝策略来处理这些任务。常见的拒绝策略有 AbortPolicy(直接抛出异常)、CallerRunsPolicy(让提交任务的线程来执行任务)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。
- CountDownLatch 和 CyclicBarrier 的区别:
- CountDownLatch:是一个计数器,它的构造函数接收一个整数作为计数器的初始值。当一个线程完成任务后,会调用 countDown() 方法将计数器减 1,其他线程可以调用 await() 方法等待计数器的值变为 0。一旦计数器的值变为 0,所有等待的线程会继续执行。CountDownLatch 只能使用一次,计数器的值不能重置。常用于一个或多个线程等待其他线程完成任务的场景。
- CyclicBarrier:是一个屏障,它的构造函数接收一个整数作为参与线程的数量。当一个线程到达屏障时,会调用 await() 方法等待其他线程,当所有线程都到达屏障时,所有线程会同时继续执行。CyclicBarrier 可以重复使用,当所有线程通过屏障后,它会自动重置。常用于多个线程相互等待,直到所有线程都准备好后再一起执行后续任务的场景。
- Spring 框架的核心特性:
- IoC(控制反转):也称为依赖注入(DI),是一种设计模式,它将对象的创建和依赖关系的管理从代码中转移到 Spring 容器中。在传统的编程中,对象的创建和依赖关系是在代码中硬编码的,而在 Spring 中,对象的创建和依赖关系由 Spring 容器负责管理。通过 IoC,代码的耦合度降低,可维护性和可测试性提高。
- AOP(面向切面编程):是一种编程范式,它允许在不修改原有代码的基础上对程序进行增强。AOP 将程序中的一些通用的功能(如日志记录、事务管理等)从业务逻辑中分离出来,形成独立的切面。在程序运行时,Spring 会将这些切面动态地织入到目标对象的方法中,从而实现对程序的增强。AOP 可以提高代码的复用性和可维护性。
- Spring Boot 相对于 Spring 的优势:
- 快速搭建项目:Spring Boot 提供了一系列的 Starter 依赖,通过引入这些依赖,就可以快速搭建一个 Spring 项目,无需手动配置大量的依赖。
- 自动配置:Spring Boot 会根据项目中引入的依赖自动进行配置,减少了大量的配置文件。例如,当引入 Spring Data JPA 和数据库驱动时,Spring Boot 会自动配置数据源和 JPA 相关的配置。
- 嵌入式服务器:Spring Boot 内置了嵌入式服务器(如 Tomcat、Jetty 等),可以直接将项目打包成可执行的 JAR 文件,通过 java -jar 命令即可启动项目,方便开发和部署。
- 生产级特性:Spring Boot 提供了一系列的生产级特性,如健康检查、指标监控、外部化配置等,方便对项目进行监控和管理。
- MyBatis 中 #{} 和 ${} 的区别:
- #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 占位符,然后使用 PreparedStatement 来执行 SQL 语句。这样可以防止 SQL 注入攻击,因为 PreparedStatement 会自动对传入的参数进行转义处理。
- **{} 时,会直接将 {} 时需要特别小心,一般只用于传入表名、列名等不会被用户输入影响的参数。