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

101 阅读3分钟

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

严肃的面试官端坐在桌前,面前放着求职者王铁牛的简历。王铁牛有些紧张地走进面试房间,坐下后,面试正式开始。

第一轮提问 面试官:我们先从 Java 核心知识开始。Java 中基本数据类型有哪些? 王铁牛:有 byte、short、int、long、float、double、char、boolean。 面试官:不错,回答得很准确。那说说重载和重写的区别。 王铁牛:重载是在同一个类中,方法名相同但参数列表不同;重写是子类重写父类的方法,方法名、参数列表和返回值类型都要相同。 面试官:非常好。那 Java 中异常处理机制是怎样的? 王铁牛:Java 异常分为检查型异常和非检查型异常。可以用 try-catch 语句捕获异常,也可以用 throws 关键字声明抛出异常。

第二轮提问 面试官:接下来聊聊 JUC 和多线程。什么是线程安全? 王铁牛:就是多个线程访问同一个资源时,不会出现数据不一致等问题。 面试官:很好。那说一下 synchronized 关键字的作用和用法。 王铁牛:synchronized 可以保证在同一时刻只有一个线程能访问被它修饰的代码块或方法。可以修饰实例方法、静态方法和代码块。 面试官:那线程池的作用是什么,有哪些常见的线程池? 王铁牛:线程池可以复用线程,减少线程创建和销毁的开销。常见的有 FixedThreadPool、CachedThreadPool、ScheduledThreadPool。 面试官:那线程池的核心参数有哪些? 王铁牛:这个……好像有最大线程数,还有……其他的我有点记不清了。

第三轮提问 面试官:我们再看看一些框架和中间件。Spring 的核心特性有哪些? 王铁牛:有依赖注入和面向切面编程。 面试官:不错。那 Spring Boot 的自动配置原理是什么? 王铁牛:这个……大概是根据一些配置文件自动配置吧,具体我不太清楚。 面试官:MyBatis 中 #{} 和 ${} 的区别是什么? 王铁牛:我记得一个是预编译,一个不是,但具体哪个是哪个我有点乱了。 面试官:Dubbo 的集群容错策略有哪些? 王铁牛:这个我就不太了解了。

面试官扶了扶眼镜,说道:“今天的面试就到这里,你先回家等通知吧。我们会综合评估后给你反馈。”

答案解析

  1. Java 基本数据类型
    • byte:8 位,有符号,范围 -128 到 127。
    • short:16 位,有符号,范围 -32768 到 32767。
    • int:32 位,有符号,范围 -2147483648 到 2147483647。
    • long:64 位,有符号,范围 -9223372036854775808 到 9223372036854775807,定义时需在值后面加 L。
    • float:32 位,单精度浮点数,定义时需在值后面加 F。
    • double:64 位,双精度浮点数。
    • char:16 位,无符号,用于表示单个字符,用单引号括起来。
    • boolean:只有两个值 true 和 false。
  2. 重载和重写的区别
    • 重载(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 Parent {
    public void print() {
        System.out.println("Parent");
    }
}
class Child extends Parent {
    @Override
    public void print() {
        System.out.println("Child");
    }
}
  1. Java 异常处理机制
    • 异常分为检查型异常(Checked Exception)和非检查型异常(Unchecked Exception)。检查型异常必须在代码中进行处理,要么用 try-catch 捕获,要么用 throws 声明抛出;非检查型异常(如 RuntimeException 及其子类)可以不处理。
    • try-catch 语句用于捕获和处理异常,格式如下:
try {
    // 可能抛出异常的代码
} catch (ExceptionType1 e1) {
    // 处理 ExceptionType1 类型的异常
} catch (ExceptionType2 e2) {
    // 处理 ExceptionType2 类型的异常
} finally {
    // 无论是否发生异常都会执行的代码
}
- throws 关键字用于在方法声明中声明该方法可能抛出的异常,让调用者处理。例如:
public void readFile() throws FileNotFoundException {
    FileInputStream fis = new FileInputStream("file.txt");
}
  1. 线程安全:当多个线程访问同一个资源时,如果不采取任何同步措施,可能会出现数据不一致、脏读等问题。线程安全的类或方法在多线程环境下能保证数据的一致性和正确性。例如,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的。
  2. synchronized 关键字
    • 作用:保证在同一时刻只有一个线程能访问被它修饰的代码块或方法,从而实现线程同步。
    • 用法:
      • 修饰实例方法:锁的是当前对象实例,例如:
public synchronized void method() {
    // 线程安全的代码
}
    - 修饰静态方法:锁的是当前类的 Class 对象,例如:
public static synchronized void staticMethod() {
    // 线程安全的代码
}
    - 修饰代码块:可以指定锁的对象,例如:
public void method() {
    synchronized (this) {
        // 线程安全的代码
    }
}
  1. 线程池的作用和常见线程池
    • 作用:复用线程,减少线程创建和销毁的开销,提高系统性能;可以控制线程的并发数量,避免过多线程导致系统资源耗尽。
    • 常见线程池:
      • FixedThreadPool:固定大小的线程池,核心线程数和最大线程数相等,当有新任务提交时,如果线程池中有空闲线程则立即执行,否则将任务放入队列等待。
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
    - CachedThreadPool:可缓存的线程池,核心线程数为 0,最大线程数为 Integer.MAX_VALUE,当有新任务提交时,如果线程池中有空闲线程则立即执行,否则创建新线程执行任务,空闲线程会在 60 秒后被回收。
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    - ScheduledThreadPool:定时任务线程池,可用于执行定时任务和周期性任务。
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
  1. 线程池的核心参数
    • corePoolSize:核心线程数,线程池创建后默认不会立即创建线程,当有任务提交时才会创建线程,直到线程数达到核心线程数。
    • maximumPoolSize:最大线程数,当队列满了且线程数小于最大线程数时,会创建新线程执行任务。
    • keepAliveTime:线程空闲时间,当线程数大于核心线程数时,空闲线程在超过该时间后会被回收。
    • unit:空闲时间的单位,如 TimeUnit.SECONDS。
    • workQueue:任务队列,用于存储等待执行的任务,常见的有 ArrayBlockingQueue、LinkedBlockingQueue 等。
    • threadFactory:线程工厂,用于创建线程。
    • handler:拒绝策略,当任务队列满了且线程数达到最大线程数时,新任务会触发拒绝策略,常见的有 AbortPolicy(直接抛出异常)、CallerRunsPolicy(由调用线程执行任务)等。
  2. Spring 的核心特性
    • 依赖注入(Dependency Injection,DI):将对象的依赖关系通过外部注入的方式进行管理,降低了对象之间的耦合度。例如,通过 XML 配置或注解(如 @Autowired)将一个对象注入到另一个对象中。
    • 面向切面编程(Aspect-Oriented Programming,AOP):将横切关注点(如日志、事务管理等)从业务逻辑中分离出来,提高了代码的可维护性和复用性。通过定义切面、切入点和通知来实现。
  3. Spring Boot 的自动配置原理
    • Spring Boot 启动时会扫描 classpath 下的 META-INF/spring.factories 文件,该文件中定义了一系列自动配置类。
    • 自动配置类会根据 classpath 中的依赖和配置文件中的属性来判断是否需要进行自动配置。例如,如果 classpath 中存在 Spring Data JPA 的依赖,且配置文件中配置了数据库相关信息,那么 Spring Boot 会自动配置数据源、JPA 实体管理器等。
    • 自动配置类使用 @Conditional 系列注解来进行条件判断,只有满足条件才会生效。
  4. MyBatis 中 #{} 和 ${} 的区别
    • #{} 是预编译处理,MyBatis 会将 #{} 替换为占位符 ?,然后使用 PreparedStatement 进行 SQL 执行,能防止 SQL 注入。例如:
<select id="getUserById" parameterType="int" resultType="User">
    SELECT * FROM users WHERE id = #{id}
</select>
- ${} 是字符串替换,MyBatis 会直接将 ${} 替换为传入的值,可能会导致 SQL 注入。例如:
<select id="getUserByTableName" parameterType="String" resultType="User">
    SELECT * FROM ${tableName}
</select>
  1. Dubbo 的集群容错策略
    • Failover Cluster:失败自动切换,当调用失败时,会自动切换到其他服务提供者进行重试,默认重试次数为 2 次。
    • Failfast Cluster:快速失败,当调用失败时,立即抛出异常,不进行重试。
    • Failsafe Cluster:失败安全,当调用失败时,会忽略异常,返回一个空结果。
    • Failback Cluster:失败自动恢复,当调用失败时,会将失败的请求记录下来,在后台定时重试。
    • Forking Cluster:并行调用多个服务提供者,只要有一个成功就返回结果。
    • Broadcast Cluster:广播调用所有服务提供者,所有提供者都执行请求,只要有一个失败就抛出异常。