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

158 阅读7分钟

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

在互联网大厂宽敞明亮的面试室里,气氛紧张而严肃。面试官端坐在桌前,面前摆放着求职者的简历,而王铁牛则坐在对面,略显紧张地搓着手。面试正式开始。

第一轮提问 面试官:首先问几个 Java 核心知识的问题。Java 中基本数据类型有哪些? 王铁牛:嗯,有 byte、short、int、long、float、double、char、boolean。 面试官:回答正确,基础很扎实。那 String 类为什么是不可变的? 王铁牛:因为 String 类被 final 修饰,它的成员变量也是 final 的,这样保证了它的不可变性。 面试官:非常好。那在 Java 里,重写和重载的区别是什么? 王铁牛:重写是子类重写父类的方法,方法名、参数列表、返回值类型都要一样,访问修饰符不能比父类的更严格。重载是在同一个类中,方法名相同但参数列表不同。

第二轮提问 面试官:接下来聊聊 JUC 和多线程。说说 Java 中创建线程的几种方式。 王铁牛:有三种,继承 Thread 类,实现 Runnable 接口,实现 Callable 接口。 面试官:不错。那线程池有哪些创建方式,各自有什么特点? 王铁牛:可以用 Executors 工具类创建,像 newFixedThreadPool 是固定大小线程池,newCachedThreadPool 是可缓存线程池。不过阿里巴巴开发手册不建议用 Executors 创建,最好用 ThreadPoolExecutor 手动创建。 面试官:看来你对规范也有了解。那在多线程环境下,如何保证数据的一致性? 王铁牛:可以用 synchronized 关键字或者 Lock 接口来实现同步。

第三轮提问 面试官:现在问一些框架和中间件的问题。Spring 的核心特性有哪些? 王铁牛:有依赖注入和面向切面编程。 面试官:很好。那 Spring Boot 是如何实现自动配置的? 王铁牛:这个……嗯……好像是根据类路径下的依赖和配置文件来自动配置。 面试官:不太清晰啊。那 MyBatis 中 #{} 和 {} 的区别是什么? **王铁牛**:#{} 是预编译处理,能防止 SQL 注入,{} 是直接替换,有 SQL 注入风险。 面试官:这个回答还可以。那 Dubbo 的集群容错策略有哪些? 王铁牛:这个……我不太清楚。

总结:面试官推了推眼镜,说道:“王铁牛,通过这次面试,能看出你有一定的 Java 基础,对于一些基础的 Java 核心知识、多线程和框架的简单概念掌握得还可以,在回答 Java 基本数据类型、创建线程的方式、Spring 核心特性等问题时表现不错,说明你对这些基础内容有较为扎实的学习。但在一些复杂的知识点上,比如 Spring Boot 的自动配置原理、Dubbo 的集群容错策略等,你的回答不够清晰甚至存在知识盲区。我们公司对于技术人才的要求是既要具备扎实的基础,也要对深入的技术点有一定的理解和掌握。你先回家等通知吧,后续如果有结果会及时联系你。”

答案详细解释

  1. Java 基本数据类型:Java 有 8 种基本数据类型,分为 4 类。整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节);浮点类型:float(4 字节)、double(8 字节);字符类型:char(2 字节);布尔类型:boolean(true 或 false)。基本数据类型直接存储值,而不是引用。
  2. String 类不可变的原因:String 类被 final 修饰,意味着它不能被继承。其内部的字符数组 value 也是 final 的,一旦初始化就不能再指向其他数组。这样做的好处是可以保证安全性,避免被恶意修改,同时在字符串常量池的实现中,不可变的特性可以提高效率,多个引用可以指向同一个字符串常量。
  3. 重写和重载的区别
    • 重写:发生在子类和父类之间,子类重写父类的方法。方法名、参数列表、返回值类型必须相同(返回值类型在协变返回类型的情况下可以是子类类型),访问修饰符不能比父类更严格。重写是实现多态的一种方式。
    • 重载:发生在同一个类中,方法名相同但参数列表不同(参数个数、类型、顺序不同),与返回值类型和访问修饰符无关。重载是为了提供多个同名但功能略有不同的方法,方便调用。
  4. Java 创建线程的方式
    • 继承 Thread 类:创建一个类继承 Thread 类,重写 run 方法,然后创建该类的实例并调用 start 方法启动线程。
    • 实现 Runnable 接口:创建一个类实现 Runnable 接口,实现 run 方法,将该类的实例作为参数传递给 Thread 类的构造函数,再调用 start 方法。
    • 实现 Callable 接口:创建一个类实现 Callable 接口,实现 call 方法,该方法有返回值。使用 FutureTask 包装 Callable 实例,再将 FutureTask 作为参数传递给 Thread 类的构造函数,调用 start 方法,最后可以通过 FutureTask 的 get 方法获取返回值。
  5. 线程池的创建方式及特点
    • Executors 工具类创建
      • newFixedThreadPool:创建一个固定大小的线程池,线程数量固定,当有新任务提交时,如果线程池中有空闲线程则执行,否则任务会在队列中等待。
      • newCachedThreadPool:创建一个可缓存的线程池,线程数量不固定,当有新任务提交时,如果有空闲线程则使用,没有则创建新线程。如果线程空闲时间过长会被回收。
    • ThreadPoolExecutor 手动创建:可以根据实际需求精确控制线程池的核心线程数、最大线程数、线程空闲时间、任务队列等参数,避免 Executors 创建线程池可能带来的资源耗尽风险。
  6. 多线程环境下保证数据一致性的方法
    • synchronized 关键字:可以修饰方法或代码块。修饰方法时,该方法在同一时刻只能被一个线程访问;修饰代码块时,需要指定一个对象作为锁,同一时刻只有获得该锁的线程才能执行代码块。
    • Lock 接口:常用的实现类有 ReentrantLock。通过 lock() 方法获取锁,unlock() 方法释放锁,相比 synchronized 更加灵活,可以实现公平锁、可中断锁等特性。
  7. Spring 的核心特性
    • 依赖注入(DI):将对象之间的依赖关系由容器来管理,而不是在对象内部手动创建依赖对象。通过构造函数、setter 方法等方式将依赖对象注入到目标对象中,降低了代码的耦合度。
    • 面向切面编程(AOP):将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,以切面的形式进行统一管理。通过代理模式在目标方法执行前后插入额外的逻辑,提高了代码的可维护性和复用性。
  8. Spring Boot 自动配置原理:Spring Boot 通过 @EnableAutoConfiguration 注解开启自动配置功能。它会根据类路径下的依赖和配置文件中的属性,使用 SpringFactoriesLoader 从 META - INF/spring.factories 文件中加载所有的自动配置类。这些自动配置类会根据条件注解(如 @ConditionalOnClass、@ConditionalOnMissingBean 等)判断是否生效,如果满足条件则会自动配置相应的 Bean 到 Spring 容器中。
  9. MyBatis 中 #{} 和 ${} 的区别
    • #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为占位符?,然后使用 PreparedStatement 进行预编译,能有效防止 SQL 注入。
    • **:是直接替换,MyBatis会将{}**:是直接替换,MyBatis 会将 {} 直接替换为变量的值,可能会导致 SQL 注入问题,通常用于动态表名、列名等情况。
  10. Dubbo 的集群容错策略
    • Failover Cluster:失败自动切换,当调用失败时,会自动切换到其他可用的服务提供者进行重试,默认重试次数为 2 次。
    • Failfast Cluster:快速失败,只调用一次,失败后立即抛出异常,适用于非幂等性操作。
    • Failsafe Cluster:失败安全,调用失败时直接忽略异常,返回一个空结果,适用于一些对结果要求不高的操作。
    • Failback Cluster:失败自动恢复,调用失败后,会将请求记录下来,在后台定时重试,适用于消息通知等操作。
    • Forking Cluster:并行调用多个服务提供者,只要有一个成功就返回结果,适用于对实时性要求较高的操作。