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

30 阅读12分钟

互联网大厂 Java 面试:核心知识、框架与中间件大考验

王铁牛怀揣着紧张又期待的心情,走进了互联网大厂的面试会议室。严肃的面试官坐在对面,一场关于 Java 技术的考验即将开始。

第一轮提问 面试官:首先问几个 Java 核心知识的问题。Java 中基本数据类型有哪些? 王铁牛:嗯,有 byte、short、int、long、float、double、char、boolean。 面试官:回答得不错。那说说面向对象的四大特性是什么? 王铁牛:这个我知道,是封装、继承、多态和抽象。 面试官:很好。那在 Java 里,重写和重载有什么区别? 王铁牛:重写是子类对父类方法的重新实现,方法名、参数列表和返回值都一样;重载是在一个类里,方法名相同但参数列表不同。 面试官:非常棒,基础很扎实。接下来看看 JUC 相关的,说说 CountDownLatch 是干什么用的? 王铁牛:它可以让一个或多个线程等待其他线程完成操作后再继续执行,就像比赛时等所有选手都到终点裁判才宣布结果。

第二轮提问 面试官:看来基础不错,进入第二轮。聊聊 JVM,JVM 的内存区域是怎么划分的? 王铁牛:有方法区、堆、虚拟机栈、本地方法栈和程序计数器。 面试官:回答正确。那堆内存又可以细分为哪些部分呢? 王铁牛:好像有新生代、老年代,新生代又有 Eden 区、Survivor 区。 面试官:没错。再说说多线程,创建线程有哪几种方式? 王铁牛:有继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。 面试官:很好。那线程池的核心参数有哪些? 王铁牛:这个……有核心线程数、最大线程数、线程存活时间、阻塞队列还有拒绝策略。 面试官:回答得很全面。说说线程池的工作流程是怎样的? 王铁牛:嗯……就是先看核心线程,然后看队列,再看最大线程数,最后用拒绝策略。

第三轮提问 面试官:来到最后一轮,这轮会更有挑战性。先说说 HashMap,它的底层数据结构是什么? 王铁牛:是数组加链表,链表长度超过 8 就会变成红黑树。 面试官:不错。那 HashMap 在多线程环境下会有什么问题? 王铁牛:这个……好像会有数据不一致的问题。 面试官:那如何解决 HashMap 在多线程下的问题呢? 王铁牛:可以用 ConcurrentHashMap 或者 Collections.synchronizedMap。 面试官:看来你对 HashMap 有一定了解。说说 Spring,Spring 的核心特性有哪些? 王铁牛:有依赖注入和面向切面编程。 面试官:Spring Boot 相比 Spring 有什么优势? 王铁牛:这个……就是简化配置啥的。 面试官:那 MyBatis 中 #{} 和 {} 的区别是什么? **王铁牛**:嗯……#{} 是预编译的,{} 是直接替换。 面试官:Dubbo 是什么,有什么作用? 王铁牛:它是个分布式服务框架,能实现服务的调用啥的。 面试官:RabbitMq 有哪些应用场景? 王铁牛:可以用于异步处理、消息队列啥的。 面试官:xxl - job 是做什么的? 王铁牛:好像是个分布式任务调度平台。 面试官:Redis 有哪些数据类型? 王铁牛:有字符串、哈希、列表、集合、有序集合。

面试官:今天的面试就到这里,你先回家等通知吧。我们会综合评估你的表现,后续会有专人跟你联系。

问题答案

  1. Java 中基本数据类型有哪些:Java 有 8 种基本数据类型,分别是 4 种整数类型(byte:1 字节,范围 -128 到 127;short:2 字节,范围 -32768 到 32767;int:4 字节,范围 -2147483648 到 2147483647;long:8 字节,范围很大),2 种浮点类型(float:4 字节,单精度浮点数;double:8 字节,双精度浮点数),1 种字符类型(char:2 字节,用于存储单个字符),1 种布尔类型(boolean:只有 true 和 false 两个值)。
  2. 面向对象的四大特性是什么
    • 封装:将数据和操作数据的方法绑定在一起,隐藏对象的内部实现细节,只对外提供必要的接口,提高数据的安全性和可维护性。
    • 继承:子类可以继承父类的属性和方法,并且可以在此基础上进行扩展和重写,实现代码的复用。
    • 多态:同一操作作用于不同的对象,可以有不同的表现形式。通过继承和接口实现,提高代码的灵活性和可扩展性。
    • 抽象:将一类对象的共同特征总结出来,形成抽象类或接口,只定义方法的声明,不实现具体逻辑,让子类去实现。
  3. 在 Java 里,重写和重载有什么区别
    • 重写(Override):发生在子类和父类之间,子类重写父类的方法。要求方法名、参数列表和返回值类型都相同(返回值类型可以是父类方法返回值类型的子类),访问修饰符不能比父类的更严格,抛出的异常不能比父类的更宽泛。
    • 重载(Overload):发生在同一个类中,方法名相同但参数列表不同(参数的类型、个数或顺序不同),与返回值类型和访问修饰符无关。
  4. CountDownLatch 是干什么用的:CountDownLatch 是 JUC 中的一个同步工具类,它允许一个或多个线程等待其他线程完成操作后再继续执行。通过一个计数器来实现,初始化时设置一个初始值,每个线程完成任务后调用 countDown() 方法将计数器减 1,等待的线程调用 await() 方法,当计数器变为 0 时,等待的线程才会继续执行。
  5. JVM 的内存区域是怎么划分的
    • 方法区:用于存储类的信息、常量、静态变量等,是各个线程共享的内存区域。
    • 堆:是 JVM 中最大的一块内存区域,用于存储对象实例,也是线程共享的。
    • 虚拟机栈:每个线程都有自己的虚拟机栈,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
    • 本地方法栈:与虚拟机栈类似,不过它是为本地方法服务的。
    • 程序计数器:记录当前线程执行的字节码的行号指示器,是线程私有的。
  6. 堆内存又可以细分为哪些部分:堆内存主要分为新生代和老年代,新生代又分为 Eden 区和两个 Survivor 区(通常比例为 8:1:1)。新创建的对象一般先放在 Eden 区,当 Eden 区满时,会进行 Minor GC,存活的对象会被移动到 Survivor 区,经过多次 Minor GC 后,仍然存活的对象会被移动到老年代。
  7. 创建线程有哪几种方式
    • 继承 Thread 类:创建一个类继承 Thread 类,重写 run() 方法,然后创建该类的实例并调用 start() 方法启动线程。
    • 实现 Runnable 接口:创建一个类实现 Runnable 接口,实现 run() 方法,然后将该类的实例作为参数传递给 Thread 类的构造函数,再调用 start() 方法。
    • 实现 Callable 接口:创建一个类实现 Callable 接口,实现 call() 方法,该方法有返回值。需要使用 FutureTask 来包装 Callable 实例,再将 FutureTask 作为参数传递给 Thread 类的构造函数启动线程,最后可以通过 FutureTask 的 get() 方法获取返回值。
    • 使用线程池:通过 Executors 工具类创建不同类型的线程池,将实现了 Runnable 或 Callable 接口的任务提交给线程池执行。
  8. 线程池的核心参数有哪些
    • 核心线程数(corePoolSize):线程池一直保持存活的线程数量,即使这些线程处于空闲状态。
    • 最大线程数(maximumPoolSize):线程池允许创建的最大线程数量。
    • 线程存活时间(keepAliveTime):当线程池中的线程数量超过核心线程数时,多余的空闲线程在等待新任务的最长时间,超过这个时间就会被销毁。
    • 阻塞队列(workQueue):用于存储等待执行的任务的队列。
    • 拒绝策略(handler):当线程池的工作队列已满且线程数量达到最大线程数时,对新提交的任务采取的处理策略,常见的有 AbortPolicy(直接抛出异常)、CallerRunsPolicy(由调用线程处理)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃队列中最老的任务)。
  9. 线程池的工作流程是怎样的:当有新任务提交时,线程池会先检查核心线程数,如果核心线程有空闲的,就将任务分配给核心线程执行;如果核心线程都在工作,就将任务放入阻塞队列;如果阻塞队列已满,就会创建新的线程,直到线程数量达到最大线程数;如果线程数量已经达到最大线程数且阻塞队列已满,就会执行拒绝策略。
  10. HashMap 的底层数据结构是什么:HashMap 的底层数据结构是数组加链表,在 JDK 1.8 及以后,当链表长度超过 8 且数组长度大于 64 时,链表会转换为红黑树,以提高查找效率。数组是 HashMap 的主体,每个元素称为一个桶(bucket),链表或红黑树存储在桶中。
  11. HashMap 在多线程环境下会有什么问题:在多线程环境下,HashMap 可能会出现数据不一致的问题,比如在扩容时可能会导致死循环,插入元素时可能会导致数据丢失等。这是因为 HashMap 不是线程安全的,它的 put、get 等操作没有进行同步处理。
  12. 如何解决 HashMap 在多线程下的问题
    • 使用 ConcurrentHashMap:它是线程安全的哈希表,在 JDK 1.7 中采用分段锁机制,在 JDK 1.8 中采用 CAS + synchronized 来保证线程安全。
    • 使用 Collections.synchronizedMap:这是一个同步包装器,将一个普通的 Map 转换为线程安全的 Map,它是通过在每个方法上加 synchronized 关键字来实现同步的。
  13. Spring 的核心特性有哪些
    • 依赖注入(DI):通过容器来管理对象之间的依赖关系,将对象的创建和依赖关系的维护从代码中分离出来,提高代码的可测试性和可维护性。
    • 面向切面编程(AOP):将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,通过代理模式在不修改原有代码的情况下增强功能。
  14. Spring Boot 相比 Spring 有什么优势
    • 简化配置:Spring Boot 提供了大量的自动配置,减少了繁琐的 XML 配置文件,通过注解和默认配置就能快速搭建项目。
    • 嵌入式服务器:内置了 Tomcat、Jetty 等服务器,无需单独部署服务器,直接运行项目即可。
    • 快速开发:提供了 Starter 依赖,通过添加相应的依赖就能快速集成各种功能,如数据库、缓存等。
    • 生产就绪:提供了一系列的监控和管理功能,方便在生产环境中对应用进行监控和管理。
  15. MyBatis 中 #{} 和 ${} 的区别是什么
    • #{}:是预编译的,会将参数作为一个占位符,使用 PreparedStatement 来执行 SQL 语句,能有效防止 SQL 注入攻击。
    • ${}:是直接替换,会将参数直接插入到 SQL 语句中,可能会导致 SQL 注入问题,一般用于动态表名、列名等场景。
  16. Dubbo 是什么,有什么作用:Dubbo 是一个高性能的分布式服务框架,用于实现服务的远程调用和服务治理。它可以将服务提供者和服务消费者解耦,通过注册中心实现服务的注册和发现,支持多种协议进行服务调用,提供了负载均衡、集群容错等功能,提高了系统的可扩展性和可维护性。
  17. RabbitMq 有哪些应用场景
    • 异步处理:将一些耗时的操作异步处理,提高系统的响应速度,比如用户注册时发送邮件通知。
    • 消息队列:实现生产者和消费者之间的解耦,生产者将消息发送到队列,消费者从队列中获取消息进行处理。
    • 流量削峰:在高并发场景下,将请求放入队列中,避免系统直接处理大量请求而崩溃。
    • 日志收集:将系统产生的日志发送到消息队列,由专门的日志处理程序进行处理。
  18. xxl - job 是做什么的:xxl - job 是一个分布式任务调度平台,提供了任务的调度、执行、监控等功能。它支持多种任务类型,如定时任务、固定间隔任务等,采用集群部署,具有高可用性和扩展性,方便开发人员对任务进行管理和维护。
  19. Redis 有哪些数据类型
    • 字符串(String):最基本的数据类型,可以存储字符串、整数、浮点数等,支持对字符串的各种操作,如设置、获取、增减等。
    • 哈希(Hash):类似于 Java 中的 HashMap,用于存储键值对的集合,适合存储对象信息。
    • 列表(List):按照插入顺序排序的字符串列表,支持在列表的两端进行插入和删除操作,常用于实现队列和栈。
    • 集合(Set):无序且唯一的字符串集合,支持集合的交集、并集、差集等操作。
    • 有序集合(Sorted Set):与集合类似,但每个元素都有一个分数,根据分数进行排序,常用于排行榜等场景。