《互联网大厂Java求职者面试:核心知识大考验》

23 阅读10分钟

面试官:请简要介绍一下 Java 的基本数据类型和引用数据类型。

王铁牛:基本数据类型包括整数类型 byte、short、int、long,浮点类型 float、double,字符类型 char,布尔类型 boolean。引用数据类型有类、接口、数组等。

面试官:回答得不错。那说说 JVM 的内存结构主要有哪些部分?

王铁牛:JVM 内存结构主要有堆、栈、方法区、程序计数器、本地方法栈。

面试官:很好。再问一个,多线程中如何实现线程同步?

王铁牛:可以使用 synchronized 关键字或者 Lock 接口来实现线程同步。

第一轮提问结束。

面试官:请详细讲讲线程池的工作原理。

王铁牛:嗯……线程池有个核心线程池,当提交的任务数小于核心线程数时,会创建新线程执行任务。如果任务数大于核心线程数,就会把任务放到队列里。要是队列满了,并且线程数小于最大线程数,就会创建新线程处理任务。当线程数达到最大线程数后,再有新任务,就会根据拒绝策略处理。

面试官:那 HashMap 的底层数据结构是什么?

王铁牛:是数组加链表,后来链表长度超过 8 会转成红黑树。

面试官:ArrayList 是如何实现动态扩容的?

王铁牛:大概就是当容量不够时,会创建一个新的更大的数组,然后把原来数组的元素复制过去。

第二轮提问结束。

面试官:说说 Spring 的核心特性。

王铁牛:Spring 有依赖注入、面向切面编程、IoC 容器这些特性。

面试官:Spring Boot 与传统 Spring 相比有哪些优势?

王铁牛:Spring Boot 更简单,自动配置,能快速搭建项目。

面试官:MyBatis 的工作原理是什么?

王铁牛:通过 XML 或注解配置 SQL,然后利用反射等机制执行 SQL。

面试官:Dubbo 的服务调用流程是怎样的?

王铁牛:呃……就是服务提供者注册服务,消费者通过注册中心获取服务信息然后调用。

第三轮提问结束。

面试结束,面试官表示会让王铁牛回家等通知。

答案

  1. Java 的基本数据类型和引用数据类型
    • 基本数据类型
      • 整数类型
        • byte:占 1 个字节,范围是 -128 到 127。主要用于存储较小的整数,比如网络协议中的一些状态码等。
        • short:占 2 个字节,范围是 -32768 到 32767。在一些对内存要求较高且数据范围不大的场景中使用。
        • int:占 4 个字节,范围是 -2147483648 到 2147483647,是最常用的整数类型。
        • long:占 8 个字节,用于表示较大范围的整数,比如表示时间戳等。
      • 浮点类型
        • float:占 4 个字节,用于表示单精度浮点数,精度相对较低,适用于对精度要求不是特别高的科学计算等场景。
        • double:占 8 个字节,用于表示双精度浮点数,精度较高,常用于金融计算等对精度要求高的场景。
      • 字符类型char:占 2 个字节,用于存储单个字符,比如一个字母、一个数字或一个标点符号等。
      • 布尔类型boolean:占 1 位,只有 true 和 false 两个值,常用于逻辑判断。
    • 引用数据类型
      • :是 Java 面向对象编程的核心,封装了数据和行为。比如自定义的 User 类来表示用户信息。
      • 接口:定义了一组方法签名,实现接口的类必须实现这些方法。例如 Comparable 接口用于对象比较。
      • 数组:可以存储多个相同类型的数据,比如 int[] array = new int[5]; 定义了一个长度为 5 的整数数组。
  2. JVM 的内存结构主要部分
    • :是 JVM 中最大的一块内存区域,用于存储对象实例。所有的对象实例以及数组都在堆上分配内存。当对象不再被引用时,会被垃圾回收机制回收。例如,我们创建一个 User 对象 new User(),这个对象就存放在堆中。
    • :主要用于存储局部变量和方法调用的上下文。每个线程都有自己独立的栈。方法执行时,局部变量会在栈中分配空间。比如方法中的 int i = 10; 变量 i 就在栈中。
    • 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量等数据。例如类的字节码文件加载后就在方法区。
    • 程序计数器:是一块较小的内存区域,它记录着当前线程执行的字节码指令的地址。每个线程都有自己的程序计数器。
    • 本地方法栈:与栈类似,用于执行本地方法(用 C 或 C++ 实现的方法)。
  3. 多线程中实现线程同步的方法
    • synchronized 关键字
      • 可以修饰方法,使得该方法在同一时刻只能被一个线程访问。例如:
        public synchronized void method() {
            // 方法体
        }
        
      • 也可以修饰代码块,对指定的代码块进行同步。比如:
        synchronized (this) {
            // 同步代码块
        }
        
    • Lock 接口
      • 常用的实现类有 ReentrantLock。例如:
        private Lock lock = new ReentrantLock();
        lock.lock();
        try {
            // 临界区代码
        } finally {
            lock.unlock();
        }
        
      • Lock 接口提供了比 synchronized 更灵活的同步控制,比如可以实现公平锁、可中断锁等。
  4. 线程池的工作原理
    • 核心线程池:当提交的任务数小于核心线程数时,线程池会创建新线程来执行任务。核心线程会一直存活在线程池中,除非设置了 allowCoreThreadTimeOut 为 true。
    • 任务队列:当任务数大于核心线程数时,新提交的任务会被放入任务队列中。常见的任务队列有 ArrayBlockingQueue(有界队列)、LinkedBlockingQueue(无界队列)等。
    • 最大线程数:如果任务队列已满,并且线程数小于最大线程数,线程池会创建新线程来处理任务。
    • 拒绝策略:当线程数达到最大线程数且任务队列已满时,再有新任务提交,就会根据拒绝策略处理。常见的拒绝策略有 AbortPolicy(抛出异常)、DiscardPolicy(丢弃任务)、DiscardOldestPolicy(丢弃队列中最老的任务)、CallerRunsPolicy(由调用线程处理任务)。
  5. HashMap 的底层数据结构
    • HashMap 底层是数组加链表(在 JDK 1.8 后链表长度超过 8 会转成红黑树)。
    • 初始时,HashMap 有一个默认大小的数组。当添加键值对时,会通过计算键的哈希值,然后对数组长度取模得到数组下标。如果该下标为空,就直接插入新节点。如果不为空,就会遍历链表(或红黑树),如果找到相同键,就更新值;如果没找到,就插入新节点。当链表长度超过 8 且数组容量大于等于 64 时,链表会转成红黑树,以提高查找效率。
  6. ArrayList 实现动态扩容的方式
    • 当 ArrayList 的容量不够时,会创建一个新的更大的数组。新数组的容量是原数组容量的 1.5 倍(如果原数组容量小于 64)或者原数组容量 + 原数组容量右移一位(如果原数组容量大于等于 64)。
    • 然后将原来数组的元素复制到新数组中,再把新的元素添加到新数组。例如,原数组容量为 10,扩容后新数组容量为 15;原数组容量为 100,扩容后新数组容量为 150。
  7. Spring 的核心特性
    • 依赖注入:通过控制反转(IoC)容器,将对象之间的依赖关系由程序代码直接控制转换为由容器来管理。例如,一个 Service 类依赖于 Dao 类,通过依赖注入可以在运行时动态地将 Dao 类的实例注入到 Service 类中。
    • 面向切面编程(AOP):可以在不修改原有代码的基础上,对业务逻辑进行横向切割,比如实现日志记录、事务管理等功能。例如,通过 AOP 可以在方法执行前后自动记录日志。
    • IoC 容器:负责创建、配置和管理对象之间的依赖关系。它使得对象之间的依赖关系更加清晰和易于维护。比如通过 XML 配置文件或者注解配置,让 IoC 容器知道如何创建和注入对象。
  8. Spring Boot 与传统 Spring 相比的优势
    • 更简单:Spring Boot 采用了自动配置的机制,大大减少了开发人员的配置工作量。例如,对于数据库连接,传统 Spring 需要配置很多参数,而 Spring Boot 只需要引入相关依赖,就能自动配置好大部分数据库连接相关的设置。
    • 自动配置:它能够根据项目中引入的依赖自动猜测并配置相关的组件和功能。比如引入了 Spring Data JPA 依赖,Spring Boot 就能自动配置好 JPA 的相关环境,使得开发人员可以快速开始数据访问层的开发。
    • 快速搭建项目:可以通过 Spring Initializr 快速创建一个基于 Spring Boot 的项目模板,包含了项目所需的基本依赖和结构,开发人员可以在此基础上快速进行业务功能的开发。
  9. MyBatis 的工作原理
    • 通过 XML 或注解配置 SQL 语句。例如在 XML 中可以这样配置一个查询用户的 SQL:
      <select id="getUser" parameterType="int" resultType="User">
          select * from user where id = #{id}
      </select>
      
    • 利用反射等机制执行 SQL。MyBatis 通过解析 XML 配置文件或注解,将 SQL 语句封装成 MappedStatement 对象。在执行 SQL 时,根据传入的参数,通过反射机制创建参数对象,然后根据 SQL 语句和参数执行数据库操作,并将结果封装成相应的 Java 对象返回。比如调用 getUser 方法时,传入用户 id,MyBatis 会根据配置的 SQL 去数据库查询并返回 User 对象。
  10. Dubbo 的服务调用流程
  • 服务提供者注册服务:服务提供者启动时,将自己提供的服务接口、实现类以及相关配置信息注册到注册中心。例如,一个订单服务提供者会把订单服务的接口和实现类等信息注册到 Zookeeper 等注册中心。
  • 消费者获取服务信息:消费者启动时,会从注册中心获取服务提供者的地址等信息。比如订单服务消费者会从注册中心获取订单服务提供者的 IP 和端口等信息。
  • 消费者调用服务:消费者根据获取到的服务信息,通过远程调用协议(如 Dubbo 协议)调用服务提供者的服务。例如,订单服务消费者通过 Dubbo 协议向订单服务提供者发送请求,获取订单相关信息。在这个过程中,涉及到网络传输、序列化和反序列化等操作,以确保请求和响应数据能够正确地在服务提供者和消费者之间传递。