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

21 阅读8分钟

面试官:第一轮提问开始,首先问你,说说Java中的多线程如何创建和启动?

王铁牛:可以通过继承Thread类或者实现Runnable接口来创建线程,然后调用start方法启动线程。

面试官:回答得不错。那再问你,线程池有哪些参数,分别有什么作用?

王铁牛:线程池有corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler这些参数。corePoolSize是核心线程数,maximumPoolSize是最大线程数,keepAliveTime是线程池线程数超过corePoolSize时,多余的空闲线程存活时间,unit是keepAliveTime的时间单位,workQueue是任务队列,threadFactory是线程工厂,handler是拒绝策略。

面试官:很好。最后一个问题,简述一下HashMap的工作原理。

王铁牛:HashMap是基于数组和链表实现的,通过key的hash值来确定在数组中的位置,如果有相同hash值的key,则会形成链表。

面试官:第一轮提问结束。

面试官:第二轮提问,说说JVM的内存结构。

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

面试官:那类加载机制有哪些?

王铁牛:类加载机制有加载、验证、准备、解析、初始化。

面试官:再问你,Spring框架中IoC和AOP的概念是什么?

王铁牛:IoC是控制反转,把对象的创建和依赖关系交给Spring容器。AOP是面向切面编程,在不修改原有代码的基础上,动态地添加一些功能。

面试官:第二轮提问完毕。

面试官:第三轮提问,讲讲MyBatis的缓存机制。

王铁牛:MyBatis有一级缓存和二级缓存,一级缓存是SqlSession级别的缓存,二级缓存是namespace级别的缓存。

面试官:Dubbo的集群容错策略有哪些?

王铁牛:Dubbo的集群容错策略有failover、failfast、failsafe、failback、forking。

面试官:最后问你,Redis的数据类型有哪些?

王铁牛:Redis的数据类型有字符串、哈希、列表、集合、有序集合。

面试官:三轮提问结束,回家等通知吧。

答案:

  1. Java多线程创建和启动
    • 继承Thread类:定义一个类继承Thread类,重写run方法,然后创建该类的实例,调用start方法启动线程。例如:
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程执行");
    }
}
MyThread thread = new MyThread();
thread.start();
  • 实现Runnable接口:定义一个类实现Runnable接口,重写run方法,然后创建Thread类的实例,并将实现Runnable接口的类实例作为参数传入Thread构造函数,最后调用start方法启动线程。例如:
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程执行");
    }
}
Thread thread = new Thread(new MyRunnable());
thread.start();
  1. 线程池参数及作用
    • corePoolSize:核心线程数。当提交的任务数小于corePoolSize时,线程池会创建新线程来执行任务。
    • maximumPoolSize:最大线程数。当提交的任务数大于corePoolSize且任务队列已满时,会创建新线程直到线程数达到maximumPoolSize。
    • keepAliveTime:线程池线程数超过corePoolSize时,多余的空闲线程存活时间。
    • unit:keepAliveTime的时间单位。
    • workQueue:任务队列。用于存放提交的任务,当线程数小于corePoolSize时,会优先从任务队列中取任务执行。
    • threadFactory:线程工厂。用于创建线程,可自定义线程的名称、优先级等。
    • handler:拒绝策略。当线程数达到maximumPoolSize且任务队列已满时,会调用拒绝策略来处理新提交的任务。常见的拒绝策略有AbortPolicy(抛出异常)、CallerRunsPolicy(调用者运行)、DiscardPolicy(丢弃任务)、DiscardOldestPolicy(丢弃最旧的任务)。
  2. HashMap工作原理
    • HashMap基于数组和链表实现。
    • 首先计算key的hash值,通过hash值确定在数组中的位置。
    • 如果该位置为空,则直接插入新节点。
    • 如果该位置不为空,且key的hash值和已存在节点的hash值相同(通过equals方法比较key),则更新该节点的值。
    • 如果hash值相同但key不同,则形成链表,新节点插入链表头部。
    • 当链表长度超过一定阈值(默认8)时,链表会转换为红黑树,以提高查询效率。
  3. JVM内存结构
    • :是JVM中最大的一块内存区域,用于存放对象实例。堆被分为新生代、老年代和永久代(Java 8后为元空间)。新生代又分为Eden区、From Survivor区和To Survivor区。对象首先在Eden区创建,当Eden区满时,触发Minor GC,存活的对象会被移动到From Survivor区,经过多次Minor GC后,仍然存活的对象会被移动到老年代。
    • :每个线程都有自己独立的栈,用于存放局部变量、方法调用等。栈是线程私有的,生命周期和线程相同。
    • 方法区:用于存放类信息、常量、静态变量等。Java 8后,永久代被元空间取代,元空间使用本地内存,而不是JVM堆内存。
    • 程序计数器:记录当前线程执行的字节码指令地址,是线程私有的,生命周期和线程相同。
    • 本地方法栈:用于执行本地方法(用C或C++实现的方法),也是线程私有的,生命周期和线程相同。
  4. 类加载机制
    • 加载:将类的字节码文件加载到内存中,生成Class对象。
    • 验证:检查加载的字节码文件是否符合JVM规范,确保代码的安全性。
    • 准备:为类的静态变量分配内存,并设置默认初始值。
    • 解析:将常量池中的符号引用替换为直接引用。
    • 初始化:执行类的静态代码块、为静态变量赋初始值等操作。
  5. Spring框架中IoC和AOP概念
    • IoC(控制反转):把对象的创建和依赖关系交给Spring容器管理。传统方式下,对象之间的依赖关系由对象自身创建和维护,而在IoC容器中,容器负责创建对象并注入其依赖关系。例如,一个Service类依赖于Dao类,在IoC容器中,容器会创建Dao类的实例并注入到Service类中。
    • AOP(面向切面编程):在不修改原有代码的基础上,动态地添加一些功能。比如日志记录、事务管理等功能可以作为切面,在程序执行的特定切入点(如方法调用前、方法执行后等)织入这些功能。通过AOP可以将业务逻辑和横切关注点(如日志、事务)分离,提高代码的可维护性和复用性。
  6. MyBatis缓存机制
    • 一级缓存:是SqlSession级别的缓存。在同一个SqlSession中,执行相同的查询语句时,会先从一级缓存中查找,如果找到则直接返回结果,不会再查询数据库。当SqlSession关闭时,一级缓存会被清空。
    • 二级缓存:是namespace级别的缓存。多个SqlSession可以共享二级缓存。当一个namespace下的查询语句被执行时,会先从二级缓存中查找,如果找到则直接返回结果。二级缓存的开启需要在MyBatis的配置文件中进行相应配置,并且需要被缓存的POJO类实现Serializable接口。
  7. Dubbo集群容错策略
    • failover:失败自动切换。当调用失败时,会自动重试其他服务器,默认重试次数为2次。适用于读操作等对一致性要求不高的场景。
    • failfast:快速失败。当调用失败时,立即抛出异常,不进行重试。适用于幂等操作(如查询操作)。
    • failsafe:失败安全。当调用失败时,直接忽略,不抛出异常。适用于写操作等对结果要求不严格的场景。
    • failback:失败自动恢复。当调用失败时,会记录失败请求,然后在后台定时重试。适用于消息通知等操作。
    • forking:并行调用多个服务器,只要一个成功就返回。适用于对实时性要求较高的场景,如实时查询多个数据源。
  8. Redis数据类型
    • 字符串:最基本的数据类型,可以存储任何数据,如数字、字符串等。常用命令有set(设置值)、get(获取值)、incr(自增)等。
    • 哈希:适合存储对象,是一个键值对集合。常用命令有hset(设置哈希字段值)、hget(获取哈希字段值)等。
    • 列表:是一个有序的字符串列表,可以从两端进行插入和删除操作。常用命令有lpush(从左边插入元素)、rpop(从右边弹出元素)等。
    • 集合:是一个无序的字符串集合,集合中的元素是唯一的。常用命令有sadd(添加元素)、smembers(获取所有元素)等。
    • 有序集合:和集合类似,但每个元素都关联一个分数,通过分数来进行排序。常用命令有zadd(添加元素及分数)、zrange(获取指定范围内的元素)等。