《互联网大厂面试:Java核心、JUC、JVM等技术深度考察》
在互联网大厂宽敞明亮的面试间里,气氛紧张而严肃。面试官正襟危坐,面前摆放着求职者的简历,而王铁牛坐在对面,有些局促不安。
第一轮面试开始
- 面试官:首先问几个 Java 核心知识的问题。Java 中基本数据类型有哪些?
- 王铁牛:这个我知道,有 byte、short、int、long、float、double、char、boolean。
- 面试官:不错,回答得很准确。那 String 是基本数据类型吗?
- 王铁牛:不是,String 是引用数据类型。
- 面试官:很好。那说说 Java 中重写和重载的区别。
- 王铁牛:重写是子类对父类中已有的方法进行重新实现,方法名、参数列表、返回值类型都要一样;重载是在一个类中,方法名相同,但参数列表不同。
- 面试官:非常棒,看来你对 Java 核心基础掌握得很扎实。
第二轮面试开始
- 面试官:接下来聊聊 JUC 和多线程。什么是线程安全?
- 王铁牛:线程安全就是多个线程访问同一个对象时,不管运行时环境采用何种调度方式或者这些线程如何交替执行,对象都能表现出正确的行为。
- 面试官:回答得不错。那 Java 中如何创建线程?
- 王铁牛:有三种方式,继承 Thread 类、实现 Runnable 接口、实现 Callable 接口。
- 面试官:很好。那说说线程池的好处有哪些?
- 王铁牛:线程池可以降低资源消耗,提高响应速度,便于线程的管理。
- 面试官:非常好,对多线程和线程池的理解很到位。
第三轮面试开始
- 面试官:现在问一些框架相关的问题。Spring 中 IOC 和 AOP 是什么?
- 王铁牛:IOC 是控制反转,把对象的创建和依赖关系的管理交给 Spring 容器;AOP 是面向切面编程,用于在不修改源代码的情况下增强功能。
- 面试官:不错。那 Spring Boot 的自动配置原理是什么?
- 王铁牛:呃……这个……就是它能自动配置一些东西吧,具体原理我有点不太清楚。
- 面试官:那 MyBatis 中 #{} 和 ${} 的区别是什么?
- 王铁牛:这个……好像一个是预编译,一个不是,具体我也说不太明白。
- 面试官:看来你对框架的原理部分掌握得不够深入。那 Dubbo 的负载均衡策略有哪些?
- 王铁牛:我就知道有几种策略,具体是啥我记不太清了。
面试结束 面试官放下手中的笔,表情严肃地说:“王铁牛,通过这次面试,我能看出你对 Java 核心知识、多线程和线程池这些基础部分掌握得还不错,回答问题也比较准确。但是在框架的原理和一些复杂技术的细节方面,你还有所欠缺,像 Spring Boot 的自动配置原理、MyBatis 中 #{} 和 ${} 的区别以及 Dubbo 的负载均衡策略等问题,你都回答得不够清晰准确。我们后续会综合评估所有面试者的情况,你先回家等通知吧。”
问题答案
- Java 中基本数据类型有哪些?
- Java 中有 8 种基本数据类型,分别为:
- 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
- 浮点类型:float(4 字节)、double(8 字节)。
- 字符类型:char(2 字节)。
- 布尔类型:boolean(理论上 1 位,但在 Java 中没有明确指定大小)。
- Java 中有 8 种基本数据类型,分别为:
- String 是基本数据类型吗?
- String 不是基本数据类型,而是引用数据类型。基本数据类型是 Java 语言中最基础的数据类型,而 String 是一个类,用于表示字符串。
- Java 中重写和重载的区别
- 重写(Override):发生在子类和父类之间,子类对父类中已有的方法进行重新实现。要求方法名、参数列表、返回值类型必须相同,访问修饰符不能比父类的更严格。重写是实现多态的一种方式。
- 重载(Overload):发生在同一个类中,方法名相同,但参数列表不同(参数的类型、个数、顺序不同)。返回值类型和访问修饰符可以不同。重载主要是为了提供更多的调用方式。
- 什么是线程安全?
- 当多个线程访问同一个对象时,如果不管运行时环境采用何种调度方式或者这些线程如何交替执行,对象都能表现出正确的行为,就称这个对象是线程安全的。例如,一个线程安全的计数器,在多线程环境下,无论多个线程如何并发地对其进行递增或递减操作,最终的计数结果都是正确的。
- Java 中如何创建线程?
- 继承 Thread 类:创建一个类继承 Thread 类,重写 run() 方法,在 run() 方法中定义线程要执行的任务。然后创建该类的对象,调用 start() 方法启动线程。
- 实现 Runnable 接口:创建一个类实现 Runnable 接口,实现 run() 方法。然后创建该类的对象,将其作为参数传递给 Thread 类的构造函数,再调用 Thread 对象的 start() 方法启动线程。
- 实现 Callable 接口:创建一个类实现 Callable 接口,实现 call() 方法,该方法可以有返回值。通过 FutureTask 类来包装 Callable 对象,再将 FutureTask 对象作为参数传递给 Thread 类的构造函数,最后调用 start() 方法启动线程。可以通过 FutureTask 的 get() 方法获取线程执行的结果。
- 线程池的好处有哪些?
- 降低资源消耗:线程的创建和销毁需要消耗系统资源,使用线程池可以避免频繁地创建和销毁线程,而是复用已有的线程,从而降低资源消耗。
- 提高响应速度:当有任务提交到线程池时,线程池可以立即从空闲线程中取出一个线程来执行任务,不需要等待线程的创建,从而提高了响应速度。
- 便于线程的管理:线程池可以对线程进行统一的管理,例如可以设置线程的最大数量、最小数量、线程的存活时间等,方便对系统资源进行控制和监控。
- Spring 中 IOC 和 AOP 是什么?
- IOC(Inversion of Control,控制反转):是一种设计思想,将对象的创建和依赖关系的管理交给 Spring 容器,而不是由对象自己来创建和管理依赖对象。这样可以降低对象之间的耦合度,提高代码的可维护性和可测试性。例如,一个类 A 依赖于类 B,在传统的方式中,类 A 自己负责创建类 B 的对象;而在 IOC 模式下,Spring 容器会创建类 B 的对象,并将其注入到类 A 中。
- AOP(Aspect-Oriented Programming,面向切面编程):是一种编程范式,用于在不修改源代码的情况下增强功能。它将那些与业务逻辑无关,但多个模块都需要的功能(如日志记录、事务管理等)提取出来,形成一个独立的切面,然后在合适的时机将这些切面织入到业务逻辑中。
- Spring Boot 的自动配置原理是什么?
- Spring Boot 的自动配置基于 Spring 的条件化配置特性。当 Spring Boot 应用启动时,会扫描 classpath 下的配置类和依赖项。在 spring-boot-autoconfigure 模块中,有大量的自动配置类,这些类使用了 @Conditional 注解及其派生注解。例如,@ConditionalOnClass 表示只有当类路径中存在指定的类时,该配置类才会生效;@ConditionalOnMissingBean 表示只有当容器中不存在指定类型的 Bean 时,才会创建该 Bean。Spring Boot 会根据应用的依赖和配置情况,自动选择合适的配置类进行加载,从而实现自动配置。
- MyBatis 中 #{} 和 ${} 的区别是什么?
- #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 占位符,然后使用 PreparedStatement 进行预编译,这样可以防止 SQL 注入攻击。例如,在查询语句中使用 #{} 传入参数,MyBatis 会将参数以安全的方式插入到 SQL 语句中。
- **{} 时,会将 {} 主要用于动态传入表名、列名等。
- Dubbo 的负载均衡策略有哪些?
- RandomLoadBalance(随机策略):随机选择一个服务提供者。默认情况下,Dubbo 使用的就是随机负载均衡策略。这种策略简单高效,适用于各个服务提供者性能相近的场景。
- RoundRobinLoadBalance(轮询策略):按照顺序依次选择服务提供者。每个服务提供者会依次被调用,适用于服务提供者性能较为均衡的场景。
- LeastActiveLoadBalance(最少活跃调用数策略):选择当前活跃调用数最少的服务提供者。活跃调用数是指一个服务提供者正在处理的请求数量,这种策略可以使性能较好的服务提供者处理更多的请求。
- ConsistentHashLoadBalance(一致性哈希策略):根据请求的某些特征(如请求的参数)计算哈希值,将请求映射到一个哈希环上,然后选择离该哈希值最近的服务提供者。这种策略可以保证相同的请求总是被路由到同一个服务提供者,适用于缓存等场景。