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

27 阅读7分钟

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

在互联网大厂的一间严肃的面试室内,面试官正襟危坐,对面坐着紧张又期待的求职者王铁牛。一场对 Java 相关核心知识的严峻考验即将拉开帷幕。

第一轮面试 面试官:“我们先从 Java 核心知识开始。Java 中的基本数据类型有哪些?” 王铁牛:“Java 的基本数据类型有 byte、short、int、long、float、double、char 和 boolean。” 面试官:“回答得不错。那 String 是基本数据类型吗?” 王铁牛:“不是,String 是引用数据类型,它是一个类。” 面试官:“很好。那说说 String、StringBuilder 和 StringBuffer 的区别。” 王铁牛:“String 是不可变的,每次对 String 进行修改都会创建一个新的 String 对象。而 StringBuilder 和 StringBuffer 是可变的,StringBuffer 是线程安全的,因为它的方法都加了 synchronized 关键字,StringBuilder 是非线程安全的,但性能比 StringBuffer 高。” 面试官:“非常棒,基础很扎实。”

第二轮面试 面试官:“接下来谈谈 JUC 和多线程。什么是线程安全?” 王铁牛:“线程安全就是在多线程环境下,程序的执行结果和单线程环境下是一样的,不会出现数据不一致或者其他异常情况。” 面试官:“不错。那 Java 中实现多线程有几种方式?” 王铁牛:“有四种方式,继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。” 面试官:“很好。说说 Thread 类中的 start() 和 run() 方法的区别。” 王铁牛:“调用 start() 方法会启动一个新的线程,线程会进入就绪状态,当获取到 CPU 时间片后就会执行 run() 方法。而直接调用 run() 方法只是普通的方法调用,不会启动新的线程。” 面试官:“回答正确,对多线程的理解很清晰。”

第三轮面试 面试官:“现在聊聊框架相关的。Spring 的核心特性有哪些?” 王铁牛:“Spring 的核心特性有 IoC(控制反转)和 AOP(面向切面编程)。IoC 是将对象的创建和依赖关系的管理交给 Spring 容器,AOP 是在不修改原有代码的基础上,对程序进行增强。” 面试官:“不错。那 Spring Boot 和 Spring 有什么关系?” 王铁牛:“Spring Boot 是基于 Spring 构建的,它简化了 Spring 应用的开发过程,提供了自动配置等功能,让开发人员可以更快速地搭建项目。” 面试官:“很好。MyBatis 中 #{} 和 {} 的区别是什么?” **王铁牛**:“#{} 是预编译处理,它会将参数进行占位符替换,能防止 SQL 注入。{} 是字符串替换,会直接将参数插入到 SQL 语句中,有 SQL 注入的风险。” 面试官:“回答得很准确。”

面试接近尾声,面试官推了推眼镜,说道:“今天的面试就到这里,你的表现整体还不错,但我们还需要综合评估一下。你先回家等通知吧,我们会在一周内给你答复。”

问题答案详解

  1. Java 中的基本数据类型有哪些? Java 有 8 种基本数据类型,分为 4 类:

    • 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
    • 浮点类型:float(4 字节)、double(8 字节)。
    • 字符类型:char(2 字节)。
    • 布尔类型:boolean(1 位)。
  2. String 是基本数据类型吗? String 不是基本数据类型,而是引用数据类型。它是 Java 中的一个类,用于表示字符串。基本数据类型是 Java 内置的简单数据类型,而引用数据类型是指向对象的引用。

  3. String、StringBuilder 和 StringBuffer 的区别

    • String:是不可变的,一旦创建,其值不能被修改。每次对 String 进行修改(如拼接、替换等),都会创建一个新的 String 对象,因此频繁修改 String 会产生大量的临时对象,影响性能。
    • StringBuilder:是可变的,它提供了一系列的方法来修改字符串,如 append()、insert() 等。它是非线程安全的,适用于单线程环境,性能较高。
    • StringBuffer:也是可变的,与 StringBuilder 类似,但它是线程安全的,因为它的方法都加了 synchronized 关键字,保证在多线程环境下操作的安全性。由于加锁会带来一定的性能开销,所以在单线程环境下,性能不如 StringBuilder。
  4. 什么是线程安全? 线程安全是指在多线程环境下,一个类或方法能够正确地处理并发访问,不会出现数据不一致、脏读、幻读等问题。也就是说,无论多个线程如何并发地访问该类或方法,其执行结果都与单线程环境下的执行结果相同。例如,一个共享的计数器,如果在多线程环境下进行自增操作,可能会出现数据不一致的问题,而使用线程安全的计数器(如 AtomicInteger)就可以避免这种问题。

  5. Java 中实现多线程有几种方式?

    • 继承 Thread 类:创建一个类继承自 Thread 类,重写其 run() 方法,在 run() 方法中定义线程要执行的任务。然后创建该类的实例,调用 start() 方法启动线程。
    • 实现 Runnable 接口:创建一个类实现 Runnable 接口,实现其 run() 方法。然后创建该类的实例,并将其作为参数传递给 Thread 类的构造函数,最后调用 Thread 实例的 start() 方法启动线程。
    • 实现 Callable 接口:与 Runnable 接口类似,但 Callable 接口的 call() 方法可以有返回值,并且可以抛出异常。需要使用 FutureTask 来包装 Callable 实例,然后将 FutureTask 作为参数传递给 Thread 类的构造函数启动线程。可以通过 FutureTask 的 get() 方法获取线程的返回值。
    • 使用线程池:线程池可以管理和复用线程,提高线程的使用效率。可以使用 Executors 类提供的静态方法创建不同类型的线程池,如 newFixedThreadPool()、newCachedThreadPool() 等。将实现了 Runnable 或 Callable 接口的任务提交给线程池执行。
  6. Thread 类中的 start() 和 run() 方法的区别

    • start() 方法:是启动线程的方法,它会创建一个新的线程,并将该线程置于就绪状态。当该线程获取到 CPU 时间片后,会自动调用其 run() 方法。start() 方法只能调用一次,多次调用会抛出 IllegalThreadStateException 异常。
    • run() 方法:是线程要执行的任务的入口,它只是一个普通的方法。如果直接调用 run() 方法,不会启动新的线程,而是在当前线程中执行 run() 方法中的代码。
  7. Spring 的核心特性有哪些?

    • IoC(控制反转):也称为依赖注入(DI),是指将对象的创建和依赖关系的管理从代码中转移到 Spring 容器中。通过 Spring 容器来创建和管理对象,对象之间的依赖关系由容器来注入,从而降低了代码的耦合度。例如,一个 Service 类依赖于一个 DAO 类,传统方式需要在 Service 类中手动创建 DAO 类的实例,而使用 IoC 后,只需要在配置文件或使用注解声明依赖关系,Spring 容器会自动创建和注入 DAO 类的实例。
    • AOP(面向切面编程):是一种编程范式,它允许在不修改原有代码的基础上,对程序进行增强。AOP 将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,形成独立的切面。通过在特定的切入点(如方法调用、异常抛出等)织入切面代码,实现对程序的增强。Spring AOP 基于代理模式实现,有 JDK 动态代理和 CGLIB 代理两种方式。
  8. Spring Boot 和 Spring 有什么关系? Spring Boot 是基于 Spring 构建的,它的出现是为了简化 Spring 应用的开发过程。Spring 是一个强大的 Java 开发框架,提供了 IoC、AOP 等核心功能,但在开发 Spring 应用时,需要进行大量的配置工作,如配置数据源、事务管理器等。Spring Boot 提供了自动配置的功能,它会根据项目的依赖和配置自动配置 Spring 应用,减少了开发人员的配置工作量。同时,Spring Boot 还提供了嵌入式服务器(如 Tomcat、Jetty 等),可以将应用打包成可执行的 JAR 文件,直接运行,方便部署。

  9. MyBatis 中 #{} 和 ${} 的区别是什么?

    • #{}:是预编译处理,在 SQL 语句中使用 #{} 时,MyBatis 会将其替换为占位符(?),然后使用 PreparedStatement 来执行 SQL 语句。在执行时,会将参数值安全地传递给占位符,能有效防止 SQL 注入攻击。例如,在 SQL 语句中使用 “SELECT * FROM users WHERE username = #{username}”,MyBatis 会将 #{username} 替换为占位符,然后将参数值传递给 PreparedStatement。
    • :是字符串替换,MyBatis会直接将{}:是字符串替换,MyBatis 会直接将 {} 中的内容替换为参数值。由于是直接替换,可能会导致 SQL 注入问题。例如,在 SQL 语句中使用 “SELECT * FROM users WHERE username = 'username”,如果参数值被恶意注入SQL代码,可能会导致数据库安全问题。因此,在使用{username}'”,如果参数值被恶意注入 SQL 代码,可能会导致数据库安全问题。因此,在使用 {} 时需要谨慎,通常只在一些特殊情况下使用,如动态表名、动态列名等。