《互联网大厂 Java 求职者面试:从核心知识到分布式组件》

38 阅读6分钟

在互联网大厂的面试室里,面试官正坐在桌前,准备对 Java 求职者王铁牛进行面试。

第一轮: 面试官:首先,你能简单介绍一下 Java 的核心知识吗? 王铁牛:Java 的核心知识包括面向对象编程、封装、继承、多态等。 面试官:不错,那你说说 Java 中的基本数据类型有哪些? 王铁牛:有 byte、short、int、long、float、double、char、boolean 这几种。 面试官:很好,那你再说说这些基本数据类型的默认值分别是多少? 王铁牛:byte 是 0,short 是 0,int 是 0,long 是 0L,float 是 0.0f,double 是 0.0d,char 是 '\u0000',boolean 是 false。

第二轮: 面试官:接着,我们来谈谈 JUC 相关的知识吧。你知道 Java 中的线程池吗?它有什么作用? 王铁牛:知道,线程池可以提高线程的复用性,减少创建和销毁线程的开销,提高程序的性能。 面试官:那你能说一下线程池的几种创建方式吗? 王铁牛:有 newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor 等。 面试官:非常好,那你说说它们的区别是什么? 王铁牛:(开始有些模糊)这个……不太清楚了。

第三轮: 面试官:再来看 JVM 方面的知识,你了解 JVM 的内存结构吗? 王铁牛:嗯……大概知道有堆、栈、方法区等。 面试官:那你能详细说说堆和栈的区别吗? 王铁牛:(思考了一会儿)堆是用来存储对象的,栈是用来存储局部变量和方法调用的。 面试官:不错,那你说说垃圾回收机制是怎么工作的? 王铁牛:(一脸茫然)不太清楚具体怎么工作的。

面试官:好了,今天的面试就到这里,你可以先回去等通知。

答案:

  • Java 的核心知识:
    • 面向对象编程是一种编程范式,将数据和操作数据的方法封装在对象中,通过对象的交互来完成程序的功能。封装是将数据和方法封装在一个类中,对外提供公共的接口,隐藏内部实现细节。继承是子类继承父类的属性和方法,子类可以扩展或重写父类的方法。多态是指同一个方法在不同的对象上有不同的表现形式,通过父类引用指向子类对象来实现。
    • Java 的基本数据类型及其默认值:
      • byte:8 位有符号整数,默认值为 0。
      • short:16 位有符号整数,默认值为 0。
      • int:32 位有符号整数,默认值为 0。
      • long:64 位有符号整数,默认值为 0L。
      • float:32 位浮点数,默认值为 0.0f。
      • double:64 位浮点数,默认值为 0.0d。
      • char:16 位 Unicode 字符,默认值为 '\u0000'。
      • boolean:布尔值,默认值为 false。
  • Java 中的线程池:
    • 线程池是一种线程复用技术,它可以提前创建一定数量的线程,当有任务需要执行时,直接从线程池中获取线程来执行任务,任务执行完毕后,线程不会被销毁,而是返回到线程池中,可以继续被复用。
    • 线程池的几种创建方式:
      • newFixedThreadPool:创建一个固定大小的线程池,线程池中的线程数量始终保持不变,当有任务提交时,直接从线程池中获取线程执行任务,如果线程池中的线程都在执行任务,那么新提交的任务将被放入阻塞队列中等待执行。
      • newCachedThreadPool:创建一个可缓存的线程池,线程池中的线程数量可以根据需要动态调整,当有任务提交时,如果线程池中有空闲的线程,则直接使用空闲的线程执行任务,如果没有空闲的线程,则创建一个新的线程执行任务。线程池中的线程空闲时间超过一定时间后,将被回收。
      • newSingleThreadExecutor:创建一个单线程的线程池,线程池中的线程数量始终为 1,当有任务提交时,直接使用该线程执行任务,如果该线程正在执行任务,则新提交的任务将被放入阻塞队列中等待执行。
    • 它们的区别:
      • newFixedThreadPool:适用于处理固定大小的线程任务,线程数量固定,可以控制线程的并发数量,避免资源过度消耗。
      • newCachedThreadPool:适用于处理大量短期异步任务,线程数量可以根据需要动态调整,线程复用率高,适用于并发量较大的场景。
      • newSingleThreadExecutor:适用于需要保证顺序执行任务的场景,或者需要限制线程数量为 1 的场景。
  • JVM 的内存结构:
    • JVM 的内存结构主要包括堆、栈、方法区等。
    • 堆是 JVM 管理的最大的一块内存区域,用于存储对象实例和数组。堆分为新生代和老年代,新生代又分为 Eden 区、From Survivor 区和 To Survivor 区。对象首先在 Eden 区分配内存,当 Eden 区满了之后,进行 Minor GC(新生代垃圾回收),将存活的对象复制到 From Survivor 区,然后清空 Eden 区和 To Survivor 区,下次 GC 时,将 From Survivor 区的对象复制到 To Survivor 区,如此循环。老年代用于存储经过多次垃圾回收后仍然存活的对象。
    • 栈是用于存储局部变量、方法参数和方法调用帧的内存区域,每个线程都有自己的栈,栈的大小是固定的,栈的增长方向是向下的。
    • 方法区用于存储类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区是共享的内存区域,所有线程都可以访问。
  • 垃圾回收机制:
    • 垃圾回收机制是 JVM 自动管理内存的一种机制,它的主要作用是回收不再被引用的对象所占用的内存空间。
    • JVM 的垃圾回收机制采用分代回收的策略,将内存分为新生代和老年代,根据对象的存活时间将对象分配到不同的代中,然后针对不同的代采用不同的垃圾回收算法。
    • 在新生代中,采用复制算法进行垃圾回收,将存活的对象复制到另一个区域,然后清空当前区域。在老年代中,采用标记-清除算法或标记-整理算法进行垃圾回收,标记出需要回收的对象,然后清除或整理内存空间。
    • 垃圾回收器会在适当的时候自动触发垃圾回收,也可以通过调用 System.gc() 方法来手动触发垃圾回收,但手动触发不一定会立即进行垃圾回收,具体的回收时机由 JVM 决定。