互联网大厂面试:Java 核心、多线程、框架及中间件知识大考验
在互联网大厂的一间明亮的面试室内,严肃的面试官正襟危坐,对面坐着略显紧张的求职者王铁牛。面试即将开始,一场关于 Java 技术知识的考验拉开了帷幕。
第一轮提问 面试官:我们先从 Java 核心知识开始。Java 中基本数据类型有哪些? 王铁牛:Java 的基本数据类型有 byte、short、int、long、float、double、char、boolean。 面试官:回答得不错。那说说 Java 中 String 类为什么是不可变的? 王铁牛:因为 String 类被 final 修饰,它的底层是用 char 数组存储的,而且这个数组也被 final 修饰,所以它不可变。 面试官:很好,基础很扎实。那在 Java 里,重写和重载的区别是什么? 王铁牛:重写是子类对父类方法的重新实现,方法名、参数列表和返回值类型都一样;重载是在同一个类中,方法名相同但参数列表不同。
第二轮提问 面试官:接下来我们聊聊多线程和 JUC。多线程有什么好处? 王铁牛:多线程可以提高程序的执行效率,能让程序同时处理多个任务,充分利用 CPU 资源。 面试官:不错。那 JUC 里的 CountDownLatch 是怎么用的,应用场景有哪些? 王铁牛:呃……这个……好像是用来控制线程同步的吧,应用场景嘛,我不太确定。 面试官:看来你对这个掌握得不太好。那线程池有哪些创建方式? 王铁牛:可以通过 Executors 工具类创建,像 newFixedThreadPool、newCachedThreadPool 这些,还可以通过 ThreadPoolExecutor 手动创建。 面试官:回答得还行。那线程池的核心参数有哪些? 王铁牛:有核心线程数、最大线程数、空闲线程存活时间、时间单位、任务队列、线程工厂和拒绝策略。
第三轮提问 面试官:现在来谈谈框架相关的。Spring 的 IOC 是什么,有什么作用? 王铁牛:IOC 是控制反转,就是把对象的创建和依赖关系的管理交给 Spring 容器,这样可以降低代码的耦合度。 面试官:很好。那 Spring Boot 相比 Spring 有什么优势? 王铁牛:Spring Boot 简化了 Spring 项目的配置,有自动配置功能,能快速搭建项目,还内置了服务器。 面试官:不错。那 MyBatis 中 #{} 和 {} 的区别是什么? **王铁牛**:#{} 是预编译处理,能防止 SQL 注入,{} 是直接替换,可能会有 SQL 注入风险。 面试官:回答得很准确。那 Dubbo 是做什么的,有什么特点? 王铁牛:Dubbo 是分布式服务框架,特点嘛……好像有服务注册与发现,还有远程调用,其他的我不太清楚了。
面试接近尾声,面试官整理了一下手中的资料,看着王铁牛说:“今天的面试就到这里了,你在 Java 核心知识、多线程和框架基础方面有一定的了解,对于一些简单的问题回答得很不错,展现出了一定的知识储备。但在 JUC 的部分知识点以及 Dubbo 等一些框架的深入理解上还有所欠缺。你先回家等通知吧,后续我们会综合评估后给你答复。”
答案详解
- Java 基本数据类型:
- Java 有 8 种基本数据类型,分为 4 类。整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节);浮点类型:float(4 字节)、double(8 字节);字符类型:char(2 字节);布尔类型:boolean(理论上 1 位,但通常占 1 字节)。这些基本数据类型是 Java 编程的基础,用于存储不同类型的数据。
- String 类不可变的原因:
- String 类被 final 修饰,意味着它不能被继承。其底层使用被 final 修饰的 char 数组存储字符,final 修饰的数组引用不能被重新赋值,而且 String 类没有提供修改字符数组内容的方法,所以 String 对象一旦创建,其内容就不可改变。这种不可变性使得 String 对象可以被安全地共享,提高了性能和安全性,比如在作为 HashMap 的键时。
- 重写和重载的区别:
- 重写(Override)发生在子类和父类之间,子类重新实现父类的方法,要求方法名、参数列表和返回值类型(协变返回类型除外)都相同,访问修饰符不能比父类的更严格。重写是实现多态的一种方式,通过父类引用指向子类对象,调用重写方法时会根据实际对象类型调用相应的方法。
- 重载(Overload)是在同一个类中,多个方法具有相同的方法名,但参数列表不同(参数的类型、个数或顺序不同),返回值类型可以相同也可以不同。重载主要是为了提供更方便的调用方式,根据不同的参数选择合适的方法执行。
- 多线程的好处:
- 提高程序执行效率:在多核 CPU 环境下,多线程可以让程序同时处理多个任务,充分利用 CPU 的多个核心,减少程序的整体执行时间。
- 提高资源利用率:比如在等待 I/O 操作时,线程可以让出 CPU 资源,让其他线程继续执行,避免 CPU 空闲,提高系统资源的利用率。
- 增强程序的响应性:对于 GUI 程序或服务器程序,多线程可以保证在处理耗时任务时,界面仍然能够响应用户操作,提高用户体验。
- CountDownLatch 的使用和应用场景:
- CountDownLatch 是 JUC 包中的一个同步工具类,它允许一个或多个线程等待其他线程完成操作。使用时,创建一个 CountDownLatch 对象并指定一个初始计数,线程可以调用 await() 方法等待计数变为 0,其他线程完成任务后调用 countDown() 方法将计数减 1。
- 应用场景:比如在一个大型任务需要多个子任务并行完成后才能继续执行后续操作的场景中,主线程可以使用 CountDownLatch 等待所有子任务线程完成,然后再进行后续处理。
- 线程池的创建方式:
- 通过 Executors 工具类创建:
- newFixedThreadPool:创建一个固定大小的线程池,线程数量固定,当有新任务提交时,如果线程池中有空闲线程则立即执行,否则将任务放入任务队列等待。
- newCachedThreadPool:创建一个可缓存的线程池,线程数量根据需要动态调整,当有新任务提交时,如果有空闲线程则使用空闲线程执行,否则创建新线程执行任务,空闲线程会在一定时间后被回收。
- newSingleThreadExecutor:创建一个单线程的线程池,所有任务按顺序执行,保证任务的顺序性。
- newScheduledThreadPool:创建一个可定时执行任务的线程池,用于执行定时任务或周期性任务。
- 通过 ThreadPoolExecutor 手动创建:可以更灵活地配置线程池的参数,如核心线程数、最大线程数、空闲线程存活时间、任务队列等。
- 通过 Executors 工具类创建:
- 线程池的核心参数:
- 核心线程数(corePoolSize):线程池保持的最小线程数量,即使这些线程处于空闲状态也不会被销毁。
- 最大线程数(maximumPoolSize):线程池允许的最大线程数量,当任务队列满了且线程数小于最大线程数时,会创建新的线程执行任务。
- 空闲线程存活时间(keepAliveTime):当线程池中的线程数量超过核心线程数时,空闲线程在经过 keepAliveTime 时间后会被销毁。
- 时间单位(unit):keepAliveTime 的时间单位,如 TimeUnit.SECONDS 表示秒。
- 任务队列(workQueue):用于存储等待执行的任务,常见的有 ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue 等。
- 线程工厂(threadFactory):用于创建线程的工厂,通过自定义线程工厂可以设置线程的名称、优先级等属性。
- 拒绝策略(handler):当任务队列满了且线程数达到最大线程数时,新提交的任务会被拒绝,此时会调用拒绝策略处理这些任务,常见的拒绝策略有 AbortPolicy(直接抛出异常)、CallerRunsPolicy(让提交任务的线程执行任务)、DiscardPolicy(直接丢弃任务)、DiscardOldestPolicy(丢弃任务队列中最老的任务)。
- Spring 的 IOC 及其作用:
- IOC(Inversion of Control)即控制反转,是 Spring 框架的核心特性之一。传统的对象创建和依赖关系管理是由程序员在代码中手动完成的,而在 Spring 中,对象的创建和依赖关系的管理交给了 Spring 容器。
- 作用:降低代码的耦合度,使得代码更加可维护和可测试。通过将对象的创建和依赖关系的管理集中到 Spring 容器中,对象之间的依赖关系由容器来注入,而不是在代码中硬编码,这样当需求发生变化时,只需要修改配置文件或注解,而不需要修改大量的代码。
- Spring Boot 相比 Spring 的优势:
- 简化配置:Spring Boot 提供了自动配置功能,根据项目中引入的依赖和配置,自动为项目生成合适的配置,减少了大量的 XML 配置文件或 Java 配置类,提高了开发效率。
- 快速搭建项目:Spring Boot 提供了 Spring Initializr 等工具,可以快速生成项目骨架,包含了基本的依赖和配置,开发者可以直接开始编写业务代码。
- 内置服务器:Spring Boot 内置了 Tomcat、Jetty 等服务器,不需要额外部署服务器,只需要将项目打包成可执行的 JAR 或 WAR 文件,直接运行即可,方便开发和测试。
- MyBatis 中 #{} 和 ${} 的区别:
- #{} 是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为占位符?,然后使用 PreparedStatement 进行预编译,这样可以防止 SQL 注入攻击,因为参数会以安全的方式传入。
- {} 时,会将 {},但要确保参数的安全性。
- Dubbo 的作用和特点:
- 作用:Dubbo 是阿里巴巴开源的分布式服务框架,用于解决分布式系统中服务之间的调用问题,实现服务的远程调用和服务治理。
- 特点:
- 服务注册与发现:Dubbo 提供了服务注册中心(如 Zookeeper、Nacos 等),服务提供者将自己的服务信息注册到注册中心,服务消费者从注册中心获取服务提供者的信息,实现服务的自动发现和调用。
- 远程调用:Dubbo 支持多种远程调用协议,如 Dubbo 协议、HTTP 协议等,通过这些协议实现服务之间的远程调用,屏蔽了网络通信的细节。
- 集群容错:Dubbo 提供了多种集群容错策略,如失败重试、快速失败、集群切换等,当服务调用失败时,可以根据不同的策略进行处理,提高系统的可靠性。
- 负载均衡:Dubbo 支持多种负载均衡算法,如随机、轮询、最少活跃调用数等,根据不同的算法将请求分发到不同的服务提供者,提高系统的性能和可用性。