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

39 阅读4分钟

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

在互联网大厂的一间明亮的面试室内,严肃的面试官坐在桌前,对面坐着紧张又期待的求职者王铁牛。一场关于 Java 核心知识的面试即将展开。

第一轮面试开始 面试官:“首先,我们来聊聊 Java 核心知识。Java 中基本数据类型有哪些?” 王铁牛:“这个我知道,有 byte、short、int、long、float、double、char、boolean。” 面试官:“不错,回答得很准确。那 String 是基本数据类型吗?” 王铁牛:“不是,String 是引用数据类型,它是一个类。” 面试官:“很好。那说说 Java 中多态的实现方式有哪些?” 王铁牛:“主要有两种,一种是方法重载,就是在一个类中定义多个同名方法,但参数列表不同;另一种是方法重写,子类重写父类的方法。” 面试官:“回答得很清晰,看来你对 Java 核心知识掌握得不错。”

第二轮面试开始 面试官:“接下来谈谈 JUC 和多线程。什么是线程安全?” 王铁牛:“线程安全就是在多线程环境下,程序的运行结果和单线程环境下是一样的,不会出现数据不一致等问题。” 面试官:“可以,那 JUC 包中的 CountDownLatch 有什么作用?” 王铁牛:“嗯……好像是用来让一个或多个线程等待其他线程完成操作的,具体的我有点不太记得清楚了。” 面试官:“没关系,简单理解是对的。那线程池的好处有哪些?” 王铁牛:“线程池可以降低创建和销毁线程的开销,提高响应速度,还能控制线程的数量,避免资源过度消耗。” 面试官:“这个回答得很好。不过对于 CountDownLatch 你还需要再巩固一下。”

第三轮面试开始 面试官:“现在来问一些关于框架和中间件的问题。Spring 的 IOC 是什么?” 王铁牛:“IOC 是控制反转,就是把对象的创建和依赖关系的管理从代码中转移到 Spring 容器中,由容器来负责创建和管理对象。” 面试官:“很正确。那 Spring Boot 相对于 Spring 有什么优势?” 王铁牛:“Spring Boot 简化了 Spring 的配置,它有很多自动配置的功能,能让开发人员快速搭建项目,减少了很多繁琐的配置工作。” 面试官:“很好。MyBatis 中 #{} 和 {} 的区别是什么?” **王铁牛**:“呃……好像 #{} 是预编译的,能防止 SQL 注入,{} 是直接替换,可能会有 SQL 注入风险,但具体原理我有点说不清楚。” 面试官:“你知道基本的区别,不过原理还是要掌握的。最后问一下,Redis 的数据类型有哪些?” 王铁牛:“有 String、Hash、List、Set、ZSet。”

面试结束,面试官整理了一下手中的资料,看着王铁牛说:“今天的面试就到这里,你对一些基础问题回答得不错,说明有一定的知识储备,但对于部分复杂的知识点,像 CountDownLatch 的具体使用和 MyBatis 中 #{} 和 ${} 的原理,还需要进一步深入学习。你先回家等通知吧,我们后续会综合评估后给你答复。”

问题答案详解

  1. Java 中基本数据类型有哪些?
    • Java 有 8 种基本数据类型,分为 4 类:
      • 整数类型:byte(1 字节,-128 到 127)、short(2 字节,-32768 到 32767)、int(4 字节,-2147483648 到 2147483647)、long(8 字节,范围更大)。
      • 浮点类型:float(4 字节,单精度浮点数)、double(8 字节,双精度浮点数)。
      • 字符类型:char(2 字节,用于表示单个字符)。
      • 布尔类型:boolean(只有两个值,true 和 false)。
  2. String 是基本数据类型吗?
    • String 不是基本数据类型,而是引用数据类型。它是 Java 中的一个类,位于 java.lang 包下。基本数据类型是 Java 语言内置的最基础的数据类型,而引用数据类型是对对象的引用。
  3. Java 中多态的实现方式有哪些?
    • 方法重载(Overloading):在同一个类中,定义多个同名的方法,但它们的参数列表(参数的类型、个数、顺序)不同。调用时,编译器会根据传入的参数来决定调用哪个方法。例如:
public class OverloadExample {
    public int add(int a, int b) {
        return a + b;
    }
    public double add(double a, double b) {
        return a + b;
    }
}
- 方法重写(Overriding):子类继承父类后,重写父类中的方法。要求方法名、参数列表和返回值类型都相同,访问修饰符不能比父类的更严格。例如:
class Animal {
    public void sound() {
        System.out.println("Animal makes a sound");
    }
}
class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("Dog barks");
    }
}
  1. 什么是线程安全?
    • 在多线程环境下,当多个线程同时访问共享资源时,如果程序的运行结果和单线程环境下的运行结果一致,不会出现数据不一致、脏读、幻读等问题,就称该程序是线程安全的。例如,一个简单的计数器,如果多个线程同时对其进行自增操作,可能会出现结果不准确的情况,需要使用同步机制(如 synchronized 关键字)来保证线程安全。
  2. JUC 包中的 CountDownLatch 有什么作用?
    • CountDownLatch 是 JUC(Java Util Concurrency)包中的一个同步工具类,它可以让一个或多个线程等待其他线程完成操作。它通过一个计数器来实现,初始化时指定计数器的值,每当一个线程完成任务后,计数器的值减 1,当计数器的值为 0 时,等待的线程就会被唤醒继续执行。例如:
import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 3;
        CountDownLatch latch = new CountDownLatch(threadCount);

        for (int i = 0; i < threadCount; i++) {
            new Thread(() -> {
                try {
                    // 模拟线程执行任务
                    Thread.sleep(1000);
                    System.out.println(Thread.currentThread().getName() + " completed");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    latch.countDown();
                }
            }).start();
        }

        // 主线程等待所有线程完成
        latch.await();
        System.out.println("All threads completed, main thread continues");
    }
}
  1. 线程池的好处有哪些?
    • 降低资源开销:创建和销毁线程需要消耗系统资源,线程池可以复用已经创建的线程,减少了创建和销毁线程的开销。
    • 提高响应速度:当有任务提交时,线程池中如果有空闲线程,就可以立即执行任务,不需要等待线程的创建。
    • 控制并发数量:可以通过设置线程池的参数,控制线程的数量,避免过多的线程导致系统资源耗尽。
    • 便于管理:线程池提供了一系列的方法来管理线程,如监控线程的状态、定时执行任务等。
  2. Spring 的 IOC 是什么?
    • IOC 即控制反转(Inversion of Control),是 Spring 框架的核心特性之一。传统的程序中,对象的创建和依赖关系的管理是由代码来完成的,而在 Spring 中,将这些工作交给了 Spring 容器。容器负责创建对象、管理对象的生命周期以及对象之间的依赖关系。通过 IOC,程序的耦合度降低,提高了代码的可维护性和可测试性。例如,在 Spring 中可以通过 XML 配置或注解来定义对象和它们之间的依赖关系:
// 定义一个服务类
public class UserService {
    private UserDao userDao;

    public UserService(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser() {
        userDao.saveUser();
    }
}

// 定义一个数据访问类
public class UserDao {
    public void saveUser() {
        System.out.println("Save user to database");
    }
}

// 使用 Spring 注解配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean
    public UserDao userDao() {
        return new UserDao();
    }

    @Bean
    public UserService userService() {
        return new UserService(userDao());
    }
}
  1. Spring Boot 相对于 Spring 有什么优势?
    • 简化配置:Spring Boot 提供了大量的自动配置功能,它会根据项目中引入的依赖自动配置 Spring 应用,减少了繁琐的 XML 配置文件。例如,引入 Spring Boot 的 Web 依赖后,它会自动配置嵌入式的 Tomcat 服务器和 Spring MVC。
    • 快速搭建项目:可以使用 Spring Initializr 快速创建 Spring Boot 项目,只需要选择需要的依赖,就可以生成一个基本的项目结构。
    • 内置服务器:Spring Boot 内置了多种嵌入式服务器,如 Tomcat、Jetty 等,不需要单独部署服务器,直接运行项目即可。
    • 监控和管理:Spring Boot Actuator 提供了对应用的监控和管理功能,如查看应用的健康状态、内存使用情况等。
  2. MyBatis 中 #{} 和 ${} 的区别是什么?
    • #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 占位符,然后使用 PreparedStatement 进行预编译,能有效防止 SQL 注入攻击。例如:
<select id="getUserById" parameterType="int" resultType="User">
    SELECT * FROM users WHERE id = #{id}
</select>
- **${}**:是直接替换,MyBatis 在处理 ${} 时,会将 ${} 直接替换为传入的值。这种方式可能会导致 SQL 注入攻击,因为如果传入的值包含恶意的 SQL 代码,会直接拼接到 SQL 语句中。例如:
<select id="getUserByColumnName" parameterType="String" resultType="User">
    SELECT * FROM users WHERE ${columnName} = 'value'
</select>
- 一般情况下,建议优先使用 #{},只有在需要动态传入表名、列名等无法使用预编译的情况下,才使用 ${},并且要对传入的值进行严格的过滤和验证。

10. Redis 的数据类型有哪些? - String:最基本的数据类型,一个键对应一个值,可以是字符串、整数或浮点数。常用于缓存、计数器等场景。 - Hash:是一个键值对的集合,适合存储对象信息。例如,可以将用户信息存储在一个 Hash 中,每个字段对应用户的一个属性。 - List:是一个有序的字符串列表,可以从两端进行插入和删除操作。常用于消息队列、任务队列等场景。 - Set:是一个无序且唯一的字符串集合,支持集合的交、并、差等操作。常用于去重、共同好友等场景。 - ZSet(Sorted Set):是一个有序的集合,每个成员都有一个分数,根据分数进行排序。常用于排行榜、热门列表等场景。