互联网大厂 Java 面试:核心知识、框架与中间件大考验
王铁牛怀揣着忐忑与期待,走进了这家互联网大厂的面试会议室。严肃的面试官早已坐在那里,桌上放着王铁牛的简历,一场对 Java 知识的严峻考验即将拉开帷幕。
第一轮提问
- 面试官:首先问几个 Java 核心知识的问题。Java 中基本数据类型有哪些?
- 王铁牛:有 byte、short、int、long、float、double、char、boolean 这八种。
- 面试官:回答得不错。那说说面向对象的四大特性是什么?
- 王铁牛:是封装、继承、多态和抽象。
- 面试官:很好。在 Java 里,String 类为什么是不可变的?
- 王铁牛:因为 String 类被 final 修饰,它的底层是用 char 数组存储的,而且这个数组也是被 final 修饰的,所以一旦创建就不能改变。
- 面试官:非常棒,基础很扎实。那 Java 中的自动装箱和拆箱是怎么回事?
- 王铁牛:自动装箱就是把基本数据类型自动转换为对应的包装类类型,比如 int 转 Integer;拆箱就是把包装类类型自动转换为基本数据类型,像 Integer 转 int。
- 面试官:回答得很准确,看来你对 Java 核心知识掌握得很牢固。
第二轮提问
- 面试官:接下来聊聊 JUC 和多线程相关的。什么是线程池,使用线程池有什么好处?
- 王铁牛:线程池就是预先创建好一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务。好处就是可以减少线程创建和销毁的开销,提高响应速度,还能更好地管理线程。
- 面试官:不错。那线程池有哪些重要的参数?
- 王铁牛:有核心线程数、最大线程数、线程空闲时间、任务队列、线程工厂和拒绝策略。
- 面试官:那说说常见的拒绝策略有哪些?
- 王铁牛:有 AbortPolicy,直接抛出异常;CallerRunsPolicy,让提交任务的线程来执行;DiscardPolicy,直接丢弃任务;DiscardOldestPolicy,丢弃队列中最老的任务。
- 面试官:很好。在多线程环境下,如何保证线程安全?
- 王铁牛:可以使用 synchronized 关键字、Lock 接口,还有使用一些线程安全的类,像 ConcurrentHashMap 等。
- 面试官:回答得很全面,多线程这部分你理解得很清晰。
第三轮提问
- 面试官:现在来谈谈一些框架和中间件。Spring 框架的核心特性有哪些?
- 王铁牛:有 IoC(控制反转)和 AOP(面向切面编程)。
- 面试官:那说说 IoC 和 AOP 的概念。
- 王铁牛:IoC 就是把对象的创建和依赖关系的管理交给 Spring 容器,而不是在代码中手动创建。AOP 就是在不修改原有代码的基础上,对程序进行增强,比如添加日志、事务等功能。
- 面试官:MyBatis 中 #{} 和 ${} 的区别是什么?
- 王铁牛:这个……嗯……好像一个是预编译,一个不是,但具体的我有点记不清了。
- 面试官:Dubbo 是做什么的,它的核心组件有哪些?
- 王铁牛:Dubbo 好像是个分布式服务框架,组件……我只记得有注册中心,其他的不太清楚了。
- 面试官:RabbitMQ 有哪些工作模式?
- 王铁牛:这个我不太确定,好像有简单模式吧,其他的我就不知道了。
面试总结 面试官扶了扶眼镜,表情依然严肃:“王铁牛,通过这三轮的提问,能看出你对 Java 核心知识、JUC、多线程以及线程池等方面掌握得比较扎实,回答问题准确且清晰,这是你的优势。在基础概念和一些常见问题上,你表现得很不错,说明你有一定的知识积累和理解能力。
然而,在框架和中间件部分,你对 Spring 的核心特性回答得还可以,但对于 MyBatis、Dubbo 和 RabbitMQ 等的问题,回答得不够完整和准确,反映出你在这些方面的知识储备还有所欠缺。对于一个想进入互联网大厂的 Java 程序员来说,不仅要掌握基础,对于常用的框架和中间件也需要有深入的了解和实践经验。
我们会综合考虑你的表现,你先回家等通知吧。”
问题答案详细解析
- Java 中基本数据类型有哪些?
- Java 中有 8 种基本数据类型,分为 4 类:
- 整数类型:byte(1 字节)、short(2 字节)、int(4 字节)、long(8 字节)。
- 浮点类型:float(4 字节)、double(8 字节)。
- 字符类型:char(2 字节)。
- 布尔类型:boolean(1 位)。
- Java 中有 8 种基本数据类型,分为 4 类:
- 面向对象的四大特性是什么?
- 封装:将数据和操作数据的方法绑定在一起,隐藏对象的内部实现细节,只对外提供必要的接口,提高了代码的安全性和可维护性。
- 继承:允许一个类继承另一个类的属性和方法,被继承的类称为父类,继承的类称为子类。继承可以实现代码的复用和扩展。
- 多态:同一个方法可以根据调用对象的不同而表现出不同的行为。多态通过继承、接口和方法重写来实现,提高了代码的灵活性和可扩展性。
- 抽象:抽象是指将一类对象的共同特征总结出来,形成抽象类或接口。抽象类和接口不能实例化,只能被继承或实现,用于规范子类的行为。
- 在 Java 里,String 类为什么是不可变的?
- String 类被 final 修饰,意味着它不能被继承。
- String 类的底层是用 char 数组存储字符串内容,并且这个数组也被 final 修饰,一旦数组被初始化,其引用不能再指向其他数组,而且数组中的元素也不能被修改。
- 不可变的好处有:可以保证字符串常量池的安全性,避免多个引用修改同一个字符串导致的问题;可以作为 HashMap 等集合的键,因为不可变对象的哈希值不会改变。
- Java 中的自动装箱和拆箱是怎么回事?
- 自动装箱:是指 Java 编译器在基本数据类型和对应的包装类之间进行自动转换。例如,当把一个基本数据类型赋值给对应的包装类对象时,编译器会自动调用包装类的 valueOf() 方法进行装箱操作。
- 自动拆箱:是指把包装类对象转换为基本数据类型。当把一个包装类对象赋值给基本数据类型变量时,编译器会自动调用包装类的 xxxValue() 方法进行拆箱操作。
- 什么是线程池,使用线程池有什么好处?
- 线程池是一种线程管理机制,它预先创建一定数量的线程,当有任务提交时,从线程池中获取线程来执行任务。任务执行完毕后,线程不会销毁,而是返回线程池等待下一个任务。
- 好处:
- 减少线程创建和销毁的开销:线程的创建和销毁需要消耗系统资源,使用线程池可以避免频繁创建和销毁线程,提高系统性能。
- 提高响应速度:当有任务提交时,线程池中已经有空闲线程可以立即执行任务,无需等待线程创建。
- 更好地管理线程:可以控制线程的数量,避免创建过多线程导致系统资源耗尽。
- 线程池有哪些重要的参数?
- 核心线程数(corePoolSize):线程池中的核心线程数量,当提交的任务数小于核心线程数时,线程池会创建新的线程来执行任务。
- 最大线程数(maximumPoolSize):线程池允许创建的最大线程数量。当任务队列已满,且线程数达到核心线程数时,线程池会继续创建线程,直到达到最大线程数。
- 线程空闲时间(keepAliveTime):当线程池中的线程数量超过核心线程数时,多余的空闲线程在等待新任务的最长时间,超过这个时间线程将被销毁。
- 任务队列(workQueue):用于存储等待执行的任务的队列。常见的任务队列有 ArrayBlockingQueue、LinkedBlockingQueue 等。
- 线程工厂(threadFactory):用于创建线程的工厂,通过线程工厂可以自定义线程的名称、优先级等属性。
- 拒绝策略(rejectedExecutionHandler):当任务队列已满,且线程数达到最大线程数时,新提交的任务将被拒绝,此时会调用拒绝策略来处理这些任务。
- 常见的拒绝策略有哪些?
- AbortPolicy:直接抛出 RejectedExecutionException 异常,阻止系统正常运行。
- CallerRunsPolicy:让提交任务的线程来执行该任务,这样可以减缓新任务的提交速度。
- DiscardPolicy:直接丢弃新提交的任务,不做任何处理。
- DiscardOldestPolicy:丢弃任务队列中最老的任务,然后尝试重新提交新任务。
- 在多线程环境下,如何保证线程安全?
- 使用 synchronized 关键字:可以修饰方法或代码块,保证同一时刻只有一个线程可以访问被修饰的方法或代码块。
- 使用 Lock 接口:如 ReentrantLock,通过 lock() 和 unlock() 方法来获取和释放锁,相比 synchronized 更加灵活。
- 使用线程安全的类:如 ConcurrentHashMap、CopyOnWriteArrayList 等,这些类内部使用了特殊的机制来保证线程安全。
- 使用原子类:如 AtomicInteger、AtomicLong 等,这些类使用 CAS(Compare-And-Swap)操作来保证对变量的原子性操作。
- Spring 框架的核心特性有哪些?
- IoC(控制反转):将对象的创建和依赖关系的管理交给 Spring 容器,而不是在代码中手动创建。通过依赖注入(DI)的方式,将对象的依赖关系注入到对象中,降低了代码的耦合度。
- AOP(面向切面编程):在不修改原有代码的基础上,对程序进行增强,如添加日志、事务等功能。AOP 通过代理模式实现,将横切关注点(如日志、事务)从业务逻辑中分离出来。
- 说说 IoC 和 AOP 的概念。
- IoC(控制反转):传统的程序设计中,对象的创建和依赖关系的管理由程序员手动完成,而在 IoC 中,这些工作交给了 Spring 容器。Spring 容器通过配置文件或注解来创建和管理对象,并将对象之间的依赖关系注入到对象中。控制反转的核心思想是将对象的控制权从代码中反转到 Spring 容器中。
- AOP(面向切面编程):AOP 是一种编程范式,它将程序中的横切关注点(如日志、事务、权限验证等)从业务逻辑中分离出来,形成独立的切面。通过在切面中定义切入点和通知,在程序运行时将切面的逻辑织入到目标方法中,实现对目标方法的增强。
- MyBatis 中 #{} 和 ${} 的区别是什么?
- #{} 是预编译处理,MyBatis 在处理 #{} 时,会将 SQL 中的 #{} 替换为 ? 占位符,然后使用 PreparedStatement 进行预编译,再将参数设置到占位符中。这样可以防止 SQL 注入攻击,因为参数会被自动进行转义处理。
- {} 时,会直接将 {} 可能会导致 SQL 注入攻击,一般用于动态表名、动态列名等场景。
- Dubbo 是做什么的,它的核心组件有哪些?
- Dubbo 是阿里巴巴开源的一个高性能、轻量级的分布式服务框架,用于解决分布式系统中服务之间的调用问题。它提供了服务注册与发现、远程调用、集群容错、负载均衡等功能,使得分布式系统的开发和维护更加简单和高效。
- 核心组件:
- 服务提供者(Provider):暴露服务的一方,将自己提供的服务注册到注册中心。
- 服务消费者(Consumer):调用服务的一方,从注册中心获取服务提供者的地址信息,并调用服务。
- 注册中心(Registry):用于服务的注册和发现,服务提供者将自己的服务信息注册到注册中心,服务消费者从注册中心获取服务提供者的地址信息。常见的注册中心有 Zookeeper、Redis 等。
- 监控中心(Monitor):用于统计服务的调用次数、调用时间等信息,为服务的性能优化提供依据。
- 调用代理(Proxy):Dubbo 使用代理模式来实现远程调用,服务消费者通过代理对象调用服务提供者的方法,代理对象负责将请求发送到服务提供者,并接收响应。
- RabbitMQ 有哪些工作模式?
- 简单模式(Simple Mode):一个生产者对应一个消费者,生产者将消息发送到一个队列,消费者从该队列中获取消息。
- 工作队列模式(Work Queues):一个生产者对应多个消费者,多个消费者竞争从一个队列中获取消息,用于实现任务的负载均衡。
- 发布 - 订阅模式(Publish/Subscribe):生产者将消息发送到一个交换机(Exchange),交换机将消息广播到所有绑定到该交换机的队列中,多个消费者可以从不同的队列中获取消息。
- 路由模式(Routing):生产者将消息发送到一个交换机,交换机根据消息的路由键(routing key)将消息路由到绑定了相应路由键的队列中,消费者从对应的队列中获取消息。
- 主题模式(Topics):与路由模式类似,但路由键可以使用通配符,更加灵活。交换机根据消息的路由键和绑定键的匹配规则将消息路由到相应的队列中。
- 头部模式(Headers):不使用路由键,而是根据消息的头部信息(headers)来进行消息的路由。生产者在发送消息时设置消息的头部信息,交换机根据头部信息将消息路由到绑定了相应头部信息的队列中。