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

78 阅读3分钟

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

在互联网大厂的一间严肃的面试室内,一位严肃的面试官正对面坐着求职者王铁牛。面试即将开始,空气中弥漫着紧张的气息。

第一轮面试开始 面试官:首先问几个 Java 核心知识的问题。Java 中基本数据类型有哪些? 王铁牛:嗯,有 byte、short、int、long、float、double、char、boolean。 面试官:回答正确,很不错。那 Java 中 String 类为什么是不可变的? 王铁牛:因为 String 类被 final 修饰,它的成员变量也是 final 的,所以不可变。 面试官:很好,理解得很准确。那说说 Java 中多态的实现方式有哪些? 王铁牛:有继承和接口实现,通过方法重写和方法重载来实现多态。 面试官:非常棒,基础很扎实。接下来问个 JVM 相关的,JVM 内存区域是如何划分的? 王铁牛:主要分为堆、栈、方法区、本地方法栈和程序计数器。堆是存放对象实例的,栈是存放局部变量等的。

第二轮面试开始 面试官:看来你基础掌握得不错。进入多线程部分,说说线程的几种状态。 王铁牛:有新建、就绪、运行、阻塞和死亡这几种状态。 面试官:回答得很清晰。那如何实现一个线程,有几种方式? 王铁牛:有继承 Thread 类和实现 Runnable 接口,还有实现 Callable 接口这几种方式。 面试官:不错。那说说线程池的好处有哪些? 王铁牛:可以复用线程,减少创建和销毁线程的开销,还能更好地管理线程数量。 面试官:很好。那在实际业务场景中,如何合理配置线程池的参数? 王铁牛:呃……这个嘛,我觉得就随便根据服务器性能设置一下就行吧。(回答不清晰)

第三轮面试开始 面试官:接下来问一些框架和中间件的问题。Spring 的核心特性有哪些? 王铁牛:有依赖注入和面向切面编程。 面试官:很好。那 Spring Boot 是如何实现自动配置的? 王铁牛:这个……好像是有一些配置文件吧,具体我也不太清楚。(回答不清晰) 面试官:那 MyBatis 中 #{} 和 ${} 的区别是什么? 王铁牛:呃,我记得一个是预编译,一个不是,具体哪个是哪个我有点混了。(回答不清晰) 面试官:再问一个 Dubbo 的问题,Dubbo 的负载均衡策略有哪些? 王铁牛:我好像听说过有几种,但是具体名字我想不起来了。(回答不清晰)

面试官:今天的面试就到这里,你先回家等通知吧。我们会综合评估后尽快给你反馈。

问题答案

  1. Java 中基本数据类型有哪些? Java 中的基本数据类型分为四类八种。

    • 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
    • 浮点类型:float(4 字节)、double(8 字节)。
    • 字符类型:char(2 字节)。
    • 布尔类型:boolean(理论上 1 位,但实际实现中不同 JVM 可能不同)。
  2. Java 中 String 类为什么是不可变的? String 类被声明为 final,意味着它不能被继承。其内部用于存储字符序列的 char 数组 value 也是 final 的,一旦初始化后,该数组的引用不能再指向其他数组,并且 String 类没有提供修改字符数组内容的公共方法,所以 String 对象一旦创建,其内容不可变。这种设计有很多好处,比如安全性(在多线程环境中可以安全使用)、缓存哈希码(提高性能)等。

  3. Java 中多态的实现方式有哪些?

    • 继承:通过子类继承父类,子类可以重写父类的方法,在运行时根据实际对象的类型调用相应的方法。
    • 接口实现:类实现接口,实现接口中的抽象方法,同样可以在运行时根据对象类型调用具体实现。
    • 方法重写:子类重写父类的方法,方法名、参数列表和返回值类型相同,但方法体不同。
    • 方法重载:在同一个类中,方法名相同,但参数列表不同(参数个数、类型或顺序不同)。
  4. JVM 内存区域是如何划分的?

    • 堆(Heap):是 JVM 中最大的一块内存区域,所有对象实例和数组都在此分配内存,是垃圾回收的主要区域。
    • 栈(Stack):主要分为虚拟机栈和本地方法栈。虚拟机栈为 Java 方法服务,每个方法执行时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。本地方法栈为本地方法服务。
    • 方法区(Method Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在 JDK 1.8 之前叫永久代,JDK 1.8 及以后改为元空间。
    • 程序计数器(Program Counter Register):可以看作是当前线程所执行的字节码的行号指示器,每个线程都有一个独立的程序计数器。
    • 本地方法栈(Native Method Stack):与虚拟机栈类似,只不过它是为本地方法服务的。
  5. 线程的几种状态

    • 新建(New):线程对象被创建,但还未调用 start() 方法。
    • 就绪(Runnable):线程对象调用了 start() 方法后,进入就绪状态,等待获取 CPU 时间片。
    • 运行(Running):线程获得 CPU 时间片,正在执行线程体中的代码。
    • 阻塞(Blocked):线程因为某些原因放弃 CPU 使用权,暂时停止运行。阻塞状态分为三种:等待阻塞(调用 wait() 方法)、同步阻塞(获取对象的同步锁失败)、其他阻塞(调用 sleep() 或 join() 方法等)。
    • 死亡(Terminated):线程执行完了所有代码或者因为异常退出了 run() 方法,线程生命周期结束。
  6. 如何实现一个线程,有几种方式?

    • 继承 Thread 类:创建一个类继承 Thread 类,重写 run() 方法,在 run() 方法中定义线程要执行的任务,然后创建该类的对象并调用 start() 方法启动线程。
    • 实现 Runnable 接口:创建一个类实现 Runnable 接口,实现 run() 方法,然后将该类的对象作为参数传递给 Thread 类的构造函数,再调用 Thread 对象的 start() 方法启动线程。
    • 实现 Callable 接口:创建一个类实现 Callable 接口,实现 call() 方法,该方法有返回值。使用 FutureTask 包装 Callable 对象,再将 FutureTask 对象作为参数传递给 Thread 类的构造函数,调用 start() 方法启动线程,最后可以通过 FutureTask 的 get() 方法获取线程执行结果。
  7. 线程池的好处有哪些?

    • 降低资源消耗:通过复用已创建的线程,减少了线程创建和销毁的开销,提高了系统性能。
    • 提高响应速度:当有任务提交时,线程池中如果有空闲线程可以立即执行任务,而不需要等待新线程的创建。
    • 方便线程管理:可以通过线程池的参数设置,如核心线程数、最大线程数、队列容量等,对线程进行有效的管理和监控。
  8. 在实际业务场景中,如何合理配置线程池的参数?

    • 核心线程数(corePoolSize):根据业务类型和服务器 CPU 核心数来设置。对于 CPU 密集型任务,核心线程数可以设置为 CPU 核心数 + 1;对于 IO 密集型任务,核心线程数可以设置得大一些,比如 CPU 核心数 * 2。
    • 最大线程数(maximumPoolSize):当任务队列满了之后,线程池会创建新的线程直到达到最大线程数。最大线程数的设置要考虑服务器的硬件资源和业务负载情况。
    • 任务队列(workQueue):常用的有 ArrayBlockingQueue(有界队列)、LinkedBlockingQueue(无界队列)、SynchronousQueue(直接提交队列)等。根据业务场景选择合适的队列,如果任务量比较稳定,可以使用有界队列;如果任务量不确定且对响应速度要求较高,可以使用无界队列。
    • 线程存活时间(keepAliveTime):当线程池中的线程数量超过核心线程数时,多余的空闲线程在等待 keepAliveTime 时间后会被销毁。
    • 拒绝策略(RejectedExecutionHandler):当任务队列满了且线程池中的线程数量达到最大线程数时,会执行拒绝策略。常见的拒绝策略有 AbortPolicy(直接抛出异常)、CallerRunsPolicy(由调用线程处理任务)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。
  9. Spring 的核心特性有哪些?

    • 依赖注入(Dependency Injection,DI):通过依赖注入,对象之间的依赖关系由容器来管理,而不是在对象内部直接创建依赖对象。这样可以降低代码的耦合度,提高代码的可维护性和可测试性。
    • 面向切面编程(Aspect-Oriented Programming,AOP):允许开发者在不修改原有业务逻辑的基础上,对程序进行增强,如日志记录、事务管理等。通过将这些通用的功能模块抽取出来,以切面的形式织入到业务逻辑中。
  10. Spring Boot 是如何实现自动配置的? Spring Boot 的自动配置基于条件注解和类路径扫描。在启动时,Spring Boot 会根据 classpath 下的依赖和配置文件中的属性,通过 @EnableAutoConfiguration 注解触发自动配置机制。Spring Boot 提供了大量的自动配置类,这些类使用 @Conditional 系列注解来判断是否满足配置条件,如果满足则自动进行配置。例如,如果 classpath 中存在 Hibernate 和 DataSource 相关的类,Spring Boot 会自动配置 JPA 相关的组件。同时,Spring Boot 还会读取 application.properties 或 application.yml 中的配置属性,根据这些属性来定制自动配置的行为。

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

    • #{} 是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 占位符,然后使用 PreparedStatement 进行参数设置,这样可以防止 SQL 注入攻击。
    • 是字符串替换,MyBatis在处理{} 是字符串替换,MyBatis 在处理 {} 时,会直接将 替换为相应的值,不会进行预编译,因此可能存在SQL注入风险。通常在需要进行表名、列名等动态替换时使用{} 替换为相应的值,不会进行预编译,因此可能存在 SQL 注入风险。通常在需要进行表名、列名等动态替换时使用 {},但要注意对输入进行严格的验证和过滤。
  12. Dubbo 的负载均衡策略有哪些?

    • RandomLoadBalance(随机负载均衡):随机选择一个服务提供者,默认的负载均衡策略。
    • RoundRobinLoadBalance(轮询负载均衡):按照顺序依次选择服务提供者。
    • LeastActiveLoadBalance(最少活跃调用数负载均衡):优先选择活跃调用数最少的服务提供者,如果有多个服务提供者的活跃调用数相同,则随机选择一个。
    • ConsistentHashLoadBalance(一致性哈希负载均衡):根据请求的某些关键信息(如请求参数)计算哈希值,将请求映射到同一个服务提供者上,保证相同的请求总是被路由到同一个服务提供者。