互联网大厂 Java 面试:核心知识、框架与中间件大考验
在互联网大厂的一间安静的面试室内,严肃的面试官正对面坐着略显紧张的求职者王铁牛,一场对 Java 多方面知识的考验即将展开。
第一轮提问 面试官:首先问几个基础的 Java 核心知识问题。Java 中基本数据类型有哪些? 王铁牛:嗯,有 byte、short、int、long、float、double、char、boolean。 面试官:回答正确,不错。那 String 是基本数据类型吗? 王铁牛:不是,String 是引用数据类型。 面试官:很好。那讲讲 Java 中多态的实现方式有哪些? 王铁牛:多态的实现方式主要有方法重载和方法重写。方法重载是在一个类中,方法名相同但参数列表不同;方法重写是子类重写父类的方法。 面试官:回答得很清晰。那 final 关键字在 Java 中有什么作用? 王铁牛:final 关键字可以修饰类、方法和变量。修饰类时,类不能被继承;修饰方法时,方法不能被重写;修饰变量时,变量变成常量,值不能被修改。 面试官:非常棒,基础很扎实。
第二轮提问 面试官:接下来聊聊 JUC 和多线程相关的。什么是线程安全,如何保证线程安全? 王铁牛:线程安全就是多个线程访问一个对象时,不会出现数据不一致的情况。可以用 synchronized 关键字或者 Lock 接口来保证线程安全。 面试官:回答得可以。那线程池的作用是什么,Java 中提供了哪些创建线程池的方式? 王铁牛:线程池的作用是减少线程创建和销毁的开销,提高性能。Java 中可以用 Executors 工具类创建,比如 newFixedThreadPool、newCachedThreadPool 等。 面试官:嗯,知道常见的创建方式。那讲讲 CountDownLatch 和 CyclicBarrier 的区别。 王铁牛:这个……好像都是和线程同步有关的,具体区别我有点不太清楚。 面试官:没关系,这是个稍复杂点的问题。那再问下,ReentrantLock 比 synchronized 有哪些优势? 王铁牛:呃……好像 ReentrantLock 更灵活吧,具体的我也说不太明白。 面试官:看来对 JUC 的一些细节掌握得还不够。
第三轮提问 面试官:现在来谈谈框架和中间件。Spring 框架的核心特性有哪些? 王铁牛:Spring 的核心特性有 IoC(控制反转)和 AOP(面向切面编程)。IoC 是把对象的创建和依赖关系的管理交给 Spring 容器;AOP 是在不修改源代码的情况下对程序进行增强。 面试官:回答得还行。那 Spring Boot 相比 Spring 有什么优势? 王铁牛:Spring Boot 简化了 Spring 的配置,有自动配置的功能,能快速搭建项目。 面试官:对的。那 MyBatis 中 #{} 和 ${} 的区别是什么? 王铁牛:这个……我记得好像一个是预编译,一个不是,但具体哪个是哪个我搞混了。 面试官:好吧。那 Dubbo 的主要功能有哪些? 王铁牛:Dubbo 是个分布式服务框架,能进行服务注册和发现,还有远程调用,但具体细节我不太清楚了。 面试官:看来在框架和中间件这部分,很多只是了解概念,细节还需要加强。
面试官:今天的面试就到这里,你先回家等通知吧。我们会综合评估后尽快给你反馈。
答案详解
- Java 基本数据类型:Java 中有 8 种基本数据类型,分为 4 类。整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节);浮点类型:float(4 字节)、double(8 字节);字符类型:char(2 字节);布尔类型:boolean(理论上 1 位,但在 Java 中没有明确规定具体大小)。
- String 不是基本数据类型:String 是引用数据类型,它是 Java 中 java.lang 包下的一个类,用于表示字符串。引用数据类型存储的是对象的引用,而不是对象本身。
- Java 多态的实现方式
- 方法重载:在同一个类中,方法名相同,但参数列表不同(参数的类型、个数、顺序不同)。方法重载与返回值类型无关,编译器根据调用方法时传入的参数来决定调用哪个重载方法。
- 方法重写:子类继承父类后,重写父类中具有相同方法名、参数列表和返回值类型的方法。重写时,子类方法的访问权限不能比父类方法的访问权限更严格,抛出的异常范围不能比父类方法抛出的异常范围更大。
- final 关键字的作用
- 修饰类:被 final 修饰的类不能被继承,即不能有子类。例如,Java 中的 String 类就是被 final 修饰的,所以不能创建 String 类的子类。
- 修饰方法:被 final 修饰的方法不能被重写,子类不能对该方法进行重新实现。
- 修饰变量:被 final 修饰的变量变成常量,一旦赋值后,其值不能再被修改。如果是引用类型的变量,那么它只能指向一个对象,不能再指向其他对象,但对象的内容可以修改。
- 线程安全及保证方式
- 线程安全:当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那么这个对象就是线程安全的。
- 保证线程安全的方式
- synchronized 关键字:可以修饰方法或代码块。修饰方法时,整个方法成为同步方法,同一时间只能有一个线程访问该方法;修饰代码块时,指定一个对象作为锁,同一时间只有获得该锁的线程才能执行代码块中的代码。
- Lock 接口:Lock 是一个接口,常见的实现类有 ReentrantLock。使用 Lock 可以更灵活地控制锁的获取和释放,例如可以实现公平锁、可中断锁等。
- 线程池的作用及创建方式
- 线程池的作用:线程池可以减少线程创建和销毁的开销,提高性能。当有任务提交时,线程池可以直接从线程池中获取空闲线程来执行任务,避免了频繁创建和销毁线程带来的性能损耗。
- Java 中创建线程池的方式
- Executors 工具类:提供了一些静态方法来创建不同类型的线程池,如 newFixedThreadPool(创建固定大小的线程池)、newCachedThreadPool(创建可缓存的线程池)、newSingleThreadExecutor(创建单线程的线程池)、newScheduledThreadPool(创建定时任务的线程池)。
- ThreadPoolExecutor 类:可以通过构造函数自定义线程池的参数,如核心线程数、最大线程数、线程空闲时间、任务队列等。
- CountDownLatch 和 CyclicBarrier 的区别
- CountDownLatch:它是一个同步辅助类,允许一个或多个线程等待其他线程完成操作。CountDownLatch 有一个计数器,初始值为一个正整数,当一个线程完成操作后,调用 countDown() 方法将计数器减 1,当计数器的值为 0 时,等待的线程可以继续执行。CountDownLatch 的计数器只能使用一次,不能重置。
- CyclicBarrier:它也是一个同步辅助类,允许一组线程相互等待,直到所有线程都到达一个屏障点,然后所有线程可以继续执行。CyclicBarrier 有一个计数器,初始值为线程的数量,当一个线程到达屏障点时,调用 await() 方法,计数器减 1,当计数器的值为 0 时,所有等待的线程同时释放,并且 CyclicBarrier 可以重复使用,即可以重置计数器。
- ReentrantLock 比 synchronized 的优势
- 可中断锁:ReentrantLock 可以实现可中断锁,即线程在等待锁的过程中可以被中断,而 synchronized 不具备这个特性。
- 公平锁:ReentrantLock 可以创建公平锁,即按照线程请求锁的顺序来获取锁,而 synchronized 是非公平锁,线程获取锁的顺序是不确定的。
- 锁绑定多个条件:ReentrantLock 可以通过 newCondition() 方法创建多个 Condition 对象,每个 Condition 对象可以实现不同的等待和唤醒操作,而 synchronized 只能使用一个隐式的条件。
- Spring 框架的核心特性
- IoC(控制反转):也称为依赖注入(DI),是指将对象的创建和依赖关系的管理交给 Spring 容器,而不是由对象本身来创建和管理。Spring 容器通过 XML 配置文件、注解等方式来配置对象的创建和依赖关系,这样可以降低代码的耦合度,提高代码的可维护性和可测试性。
- AOP(面向切面编程):是一种编程范式,它允许在不修改源代码的情况下对程序进行增强。AOP 通过将横切关注点(如日志记录、事务管理等)从业务逻辑中分离出来,形成独立的切面,然后在程序运行时将切面织入到目标对象中,从而实现对目标对象的增强。
- Spring Boot 相比 Spring 的优势
- 简化配置:Spring Boot 提供了自动配置的功能,它会根据项目中引入的依赖自动配置 Spring 应用,减少了大量的 XML 配置文件和 Java 配置类,提高了开发效率。
- 快速搭建项目:Spring Boot 提供了 Spring Initializr 工具,可以快速生成项目骨架,开发者只需要选择项目的依赖和配置信息,就可以快速创建一个 Spring Boot 项目。
- 嵌入式服务器:Spring Boot 内置了嵌入式服务器,如 Tomcat、Jetty 等,不需要单独部署服务器,直接运行项目即可启动服务器,方便开发和测试。
- MyBatis 中 #{} 和 ${} 的区别
- #{}:是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为占位符 ?,然后使用 PreparedStatement 来执行 SQL 语句,这样可以防止 SQL 注入攻击。
- ${}:是字符串替换,MyBatis 在处理 ${} 时,会将 ${} 直接替换为变量的值,不会进行预编译处理,因此可能会存在 SQL 注入的风险。一般在需要动态传入表名、列名等情况时使用 ${}。
- Dubbo 的主要功能
- 服务注册与发现:Dubbo 提供了服务注册中心(如 Zookeeper、Nacos 等),服务提供者将自己提供的服务注册到注册中心,服务消费者从注册中心获取服务提供者的地址信息,从而实现服务的发现。
- 远程调用:Dubbo 支持多种远程调用协议(如 Dubbo 协议、HTTP 协议等),服务消费者可以通过远程调用的方式调用服务提供者提供的服务,实现分布式系统之间的通信。
- 集群容错:Dubbo 提供了多种集群容错策略(如失败重试、快速失败等),当服务调用失败时,可以根据不同的容错策略进行处理,提高系统的可靠性。
- 负载均衡:Dubbo 支持多种负载均衡算法(如随机、轮询、最少活跃调用数等),可以根据服务提供者的性能和负载情况,将请求分发到不同的服务提供者上,实现负载均衡。