《互联网大厂Java面试全流程大揭秘:核心知识深度考察》

49 阅读5分钟

面试官:请简要介绍一下 Java 核心知识中面向对象的三大特性。

王铁牛:嗯,面向对象三大特性就是封装、继承和多态。封装就是把数据和操作数据的方法封装在一起;继承就是子类继承父类的属性和方法;多态就是同一个行为具有多个不同表现形式或形态。

面试官:不错,回答得很清晰。那再问你,说说 JUC 包下常用的几个类及其作用。

王铁牛:JUC 包下的 CountDownLatch 可以用来实现一个线程等待其他多个线程完成任务;CyclicBarrier 能让一组线程相互等待,直到所有线程都到达某个屏障点;还有 Semaphore 用于控制同时访问某个资源的线程数量。

面试官:很好,第一轮表现不错。接下来第二轮,说说 JVM 的内存模型以及垃圾回收算法。

王铁牛:JVM 内存模型包括堆、栈、方法区等。垃圾回收算法嘛,有标记清除算法、标记整理算法、复制算法。

面试官:回答得比较笼统,能不能具体展开讲讲这些算法的优缺点?

王铁牛:呃……标记清除算法就是先标记再清除,优点是简单,缺点是会产生内存碎片;标记整理算法在标记清除基础上整理内存,不会产生碎片,但效率低些;复制算法是将内存分为两块,每次只使用一块,效率高但浪费一半内存。

面试官:第三轮,多线程中如何保证线程安全,举几个具体例子。

王铁牛:可以用 synchronized 关键字,比如在方法或代码块前加 synchronized,还有 Lock 接口,像 ReentrantLock。

面试官:那说说线程池的原理和几种常见的线程池类型。

王铁牛:线程池原理就是预先创建一定数量的线程,当有任务来时从线程池中获取线程执行任务。常见类型有 FixedThreadPool、CachedThreadPool、SingleThreadExecutor。

面试官:回答得不是很准确。今天的面试就到这里,回去等通知吧。

答案:

  1. Java 面向对象三大特性
    • 封装:把对象的属性和行为(方法)包装在一起,对外提供统一的访问接口。这样可以隐藏对象内部的实现细节,提高代码的安全性和可维护性。例如,在一个类中定义私有属性和公共的 get、set 方法来访问和修改属性。
    • 继承:子类继承父类的属性和方法,实现代码复用。子类可以扩展父类的功能,也可以重写父类的方法。比如一个 Animal 类有 eat 方法,Dog 类继承 Animal 类,就可以直接使用 eat 方法,还能根据自身特点重写。
    • 多态:同一个行为具有多个不同表现形式或形态。可以通过方法重写和接口实现来体现。比如有一个父类引用指向子类对象,调用同一个方法时会根据对象的实际类型执行不同的行为。
  2. JUC 包下常用类及其作用
    • CountDownLatch:用于一个线程等待其他多个线程完成任务。例如,主线程创建了多个子线程去执行不同任务,当所有子线程都完成后,主线程需要执行后续操作,这时就可以使用 CountDownLatch。主线程调用 await 方法等待,子线程完成任务后调用 countDown 方法,当计数器减为 0 时,主线程继续执行。
    • CyclicBarrier:让一组线程相互等待,直到所有线程都到达某个屏障点。比如有多个运动员参加接力比赛,每个运动员完成自己的赛程后在屏障点等待,直到所有运动员都到达,再一起继续后续流程。
    • Semaphore:控制同时访问某个资源的线程数量。例如,有一个资源同时只能被 3 个线程访问,就可以创建一个 Semaphore(3),线程在访问资源前调用 acquire 方法获取许可,访问完后调用 release 方法释放许可。
  3. JVM 内存模型及垃圾回收算法
    • JVM 内存模型
      • :是 JVM 中最大的一块内存区域,用于存放对象实例。
      • :存放局部变量、方法调用等信息。
      • 方法区:存储类信息、常量、静态变量等。
    • 垃圾回收算法
      • 标记清除算法
        • 优点:简单直观。
        • 缺点:会产生大量不连续的内存碎片,导致后续分配大对象时可能无法找到足够连续的内存空间。
      • 标记整理算法
        • 优点:解决了标记清除算法产生内存碎片的问题。
        • 缺点:需要移动存活对象,效率相对较低。
      • 复制算法
        • 优点:实现简单,运行效率高。
        • 缺点:浪费一半内存空间。适用于对象存活率较低的场景,如新生代。
  4. 多线程保证线程安全的方法及例子
    • synchronized 关键字
      • 可以修饰方法,被修饰的方法在同一时刻只能被一个线程访问。例如:
public class SynchronizedExample {
    public synchronized void method() {
        // 方法体
    }
}
    - 也可以修饰代码块,指定锁对象。如:
public class SynchronizedExample {
    public void method() {
        synchronized (this) {
            // 代码块
        }
    }
}
- **Lock 接口**:以 ReentrantLock 为例,它提供了比 synchronized 更灵活的锁控制。例如:
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private ReentrantLock lock = new ReentrantLock();

    public void method() {
        lock.lock();
        try {
            // 代码块
        } finally {
            lock.unlock();
        }
    }
}
  1. 线程池原理和常见类型
    • 原理:预先创建一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务。线程执行完任务后不会销毁,而是返回线程池等待下一个任务。这样可以避免频繁创建和销毁线程带来的开销,提高系统性能。
    • 常见类型
      • FixedThreadPool:创建一个固定大小的线程池,线程数量一旦创建就不会改变。适用于任务量比较稳定的场景。
      • CachedThreadPool:线程池大小无界,线程空闲时会被回收,适用于执行很多短期异步任务的场景。
      • SingleThreadExecutor:只有一个线程的线程池,保证任务按顺序执行。