互联网大厂 Java 面试:核心知识、框架与中间件的全方位考验
严肃的面试官坐在桌前,面前放着王铁牛的简历。王铁牛紧张地走进面试房间,坐下后,面试正式开始。
第一轮提问
- 面试官:首先问几个 Java 核心知识的问题。Java 中基本数据类型有哪些?
- 王铁牛:嗯,有 byte、short、int、long、float、double、char、boolean。
- 面试官:回答正确。那说说 Java 中多态的实现方式有哪些?
- 王铁牛:主要有继承和接口实现,通过方法重写和方法重载来体现多态。
- 面试官:不错。那 String、StringBuilder 和 StringBuffer 有什么区别?
- 王铁牛:String 是不可变的,每次操作都会产生新的对象;StringBuilder 是非线程安全的,效率高;StringBuffer 是线程安全的,效率相对低一些。
- 面试官:回答得很好,基础很扎实。
第二轮提问
- 面试官:接下来聊聊 JUC、多线程和线程池。什么是 JUC?
- 王铁牛:JUC 就是 java.util.concurrent 包,里面提供了很多并发编程的工具类。
- 面试官:那创建线程有哪几种方式?
- 王铁牛:有继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。
- 面试官:线程池的核心参数有哪些?
- 王铁牛:有核心线程数、最大线程数、空闲线程存活时间、时间单位、任务队列、线程工厂和拒绝策略。
- 面试官:那说说拒绝策略有哪些?
- 王铁牛:呃……有……好像有几种,具体我有点记不清了。
- 面试官:这个知识点比较重要,要回去好好巩固一下。
第三轮提问
- 面试官:现在问一些框架和中间件的问题。Spring 的核心特性有哪些?
- 王铁牛:有依赖注入和面向切面编程。
- 面试官:Spring Boot 如何实现自动配置的?
- 王铁牛:嗯……就是有一些配置类,然后根据条件自动配置,具体怎么弄的我不太清楚。
- 面试官:MyBatis 中 #{} 和 ${} 的区别是什么?
- 王铁牛:好像一个是预编译,一个不是,具体哪个是哪个我有点乱了。
- 面试官:Dubbo 的负载均衡策略有哪些?
- 王铁牛:我就知道有几种,具体名字我想不起来了。
面试结束,面试官合上手中的文件,说道:“今天的面试就到这里,你整体有一定的基础,但对于一些重要的知识点掌握得不够扎实,尤其是框架和中间件的部分。你先回家等通知吧,后续如果有结果会及时联系你。”
问题答案详解
- Java 中基本数据类型有哪些?
- Java 有 8 种基本数据类型,分为 4 类:
- 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
- 浮点类型:float(4 字节)、double(8 字节)。
- 字符类型:char(2 字节)。
- 布尔类型:boolean(理论上 1 位,实际实现因不同 JVM 而异)。
- Java 有 8 种基本数据类型,分为 4 类:
- Java 中多态的实现方式有哪些?
- 多态是指同一个行为具有多个不同表现形式或形态的能力。实现方式主要有:
- 继承:子类继承父类,重写父类的方法,通过父类引用指向子类对象,调用重写后的方法实现多态。
- 接口实现:类实现接口,重写接口中的抽象方法,通过接口引用指向实现类对象,调用重写后的方法实现多态。
- 方法重载:在一个类中,有多个方法名相同,但参数列表不同(参数个数、类型或顺序不同),根据传入的参数不同调用不同的方法。
- 多态是指同一个行为具有多个不同表现形式或形态的能力。实现方式主要有:
- String、StringBuilder 和 StringBuffer 有什么区别?
- String:是不可变类,一旦创建,其值不能被修改。每次对 String 进行操作,都会创建一个新的 String 对象,效率较低。适用于字符串不经常变化的场景。
- StringBuilder:是可变类,非线程安全的。在单线程环境下,对字符串进行频繁的增删改操作时,使用 StringBuilder 效率高。
- StringBuffer:是可变类,线程安全的。内部的方法大多使用 synchronized 关键字修饰,保证在多线程环境下操作字符串的安全性,但效率相对 StringBuilder 较低。
- 什么是 JUC?
- JUC 即 java.util.concurrent 包,是 Java 5 引入的用于处理并发编程的工具包。它提供了一系列高级的并发编程工具和类,如线程池、锁机制、原子类、并发集合等,大大简化了 Java 并发编程的开发。
- 创建线程有哪几种方式?
- 继承 Thread 类:创建一个类继承 Thread 类,重写 run() 方法,在 run() 方法中定义线程要执行的任务,然后创建该类的对象并调用 start() 方法启动线程。
- 实现 Runnable 接口:创建一个类实现 Runnable 接口,实现 run() 方法,然后将该类的对象作为参数传递给 Thread 类的构造函数,创建 Thread 对象并调用 start() 方法启动线程。
- 实现 Callable 接口:创建一个类实现 Callable 接口,实现 call() 方法,该方法有返回值。通过 FutureTask 类来包装 Callable 对象,再将 FutureTask 对象作为参数传递给 Thread 类的构造函数,创建 Thread 对象并调用 start() 方法启动线程。最后可以通过 FutureTask 的 get() 方法获取线程执行的结果。
- 使用线程池:通过 Executors 工具类创建不同类型的线程池,如固定大小线程池、单线程线程池等。将实现了 Runnable 或 Callable 接口的任务提交给线程池执行。
- 线程池的核心参数有哪些?
- 核心线程数(corePoolSize):线程池中的核心线程数量,当提交的任务数小于核心线程数时,线程池会创建新的线程来执行任务。
- 最大线程数(maximumPoolSize):线程池允许创建的最大线程数量。当任务队列已满,且线程数小于最大线程数时,线程池会创建新的线程来执行任务。
- 空闲线程存活时间(keepAliveTime):当线程池中的线程数量大于核心线程数时,多余的空闲线程在等待新任务的最长时间,超过这个时间,空闲线程将被销毁。
- 时间单位(unit):空闲线程存活时间的单位,如 TimeUnit.SECONDS 表示秒。
- 任务队列(workQueue):用于存储待执行任务的队列,当提交的任务数超过核心线程数时,任务会被放入任务队列中等待执行。常见的任务队列有 ArrayBlockingQueue、LinkedBlockingQueue 等。
- 线程工厂(threadFactory):用于创建线程的工厂,通过线程工厂可以自定义线程的名称、优先级等属性。
- 拒绝策略(handler):当任务队列已满,且线程数达到最大线程数时,新提交的任务将被拒绝执行,此时会调用拒绝策略来处理这些任务。常见的拒绝策略有 AbortPolicy(直接抛出异常)、CallerRunsPolicy(由调用线程执行任务)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。
- Spring 的核心特性有哪些?
- 依赖注入(Dependency Injection,DI):是一种设计模式,通过外部容器将对象所依赖的其他对象注入到该对象中,而不是由对象自己创建依赖对象。这样可以降低对象之间的耦合度,提高代码的可维护性和可测试性。
- 面向切面编程(Aspect-Oriented Programming,AOP):允许开发者在不修改原有业务逻辑的基础上,对程序进行增强,如日志记录、事务管理等。通过将这些通用的功能模块封装成切面,在需要的地方进行切入,实现代码的复用和分离。
- Spring Boot 如何实现自动配置的?
- Spring Boot 的自动配置是基于条件注解和类路径扫描实现的。当 Spring Boot 应用启动时,会加载 META-INF/spring.factories 文件,该文件中定义了一系列自动配置类。这些自动配置类使用 @Configuration 注解标记,并且使用 @Conditional 系列注解来判断是否满足自动配置的条件。例如,@ConditionalOnClass 表示只有当类路径中存在指定的类时,该自动配置类才会生效。同时,Spring Boot 还会根据 application.properties 或 application.yml 中的配置来进一步定制自动配置的行为。
- MyBatis 中 #{} 和 ${} 的区别是什么?
- #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 占位符,然后使用 PreparedStatement 进行预编译,将参数设置到占位符中。这样可以防止 SQL 注入攻击,因为参数会被自动进行转义处理。
- {} 时,会直接将 ${} 中的内容替换到 SQL 语句中,不会进行预编译。如果使用不当,可能会导致 SQL 注入攻击。通常用于动态传入表名、列名等场景。
- Dubbo 的负载均衡策略有哪些?
- RandomLoadBalance(随机负载均衡):随机选择一个服务提供者进行调用,是 Dubbo 默认的负载均衡策略。
- RoundRobinLoadBalance(轮询负载均衡):按照顺序依次选择服务提供者进行调用,适用于服务提供者性能相近的场景。
- LeastActiveLoadBalance(最少活跃调用数负载均衡):优先选择活跃调用数最少的服务提供者进行调用,活跃调用数是指当前正在处理的请求数。如果多个服务提供者的活跃调用数相同,则随机选择一个。
- ConsistentHashLoadBalance(一致性哈希负载均衡):根据请求的参数生成一个哈希值,通过哈希算法将请求映射到一个固定的服务提供者上,保证相同参数的请求总是发送到同一个服务提供者。适用于缓存等场景。