零碎面试题
Java基础
finally语句块什么时候不会执行
finally语句块在两种情况下不会执行:
程序没有进入到try语句块因为异常导致程序终止,这个问题主要是开发人员在编写代码的时候,异常捕获的范围不够
在try或者cache语句块中,执行了System.exit(0)语句,导致JVM直接退出
程序所在的线程死亡。
关闭 CPU。
⭐⭐⭐动态代理
- JDK动态代理:只适用于接口,代理类实现了目标对象的接口,并在方法调用时添加额外的功能(例如事务管理)。
- CGLIB代理:基于子类的动态代理,适用于类。CGLIB通过创建目标类的子类并覆盖其中的方法来添加额外的功能。
代理模式在软件开发中是一种常见的设计模式,用于在不改变目标对象的情况下,通过代理对象来控制对目标对象的访问。代理模式可以分为静态代理和动态代理,它们之间有一些显著的区别。以下是详细的解释:
1. 静态代理
定义:静态代理是在编译时由程序员手动编写或者通过工具生成代理类。代理类在编译时已经确定,不会在运行时变化。
实现方式:在静态代理中,代理类和目标类都实现相同的接口,代理类持有一个目标对象的引用,并在代理类的方法中调用目标对象的方法。
优点:
- 简单明了,易于理解和实现。
- 可以在编译时检查类型安全。
缺点:
- 代码量大,目标类每新增一个方法,代理类都需要进行相应的修改。
- 不够灵活,代理类和目标类之间的耦合度较高。
示例代码:
1 | // 接口 |
2. 动态代理
定义:动态代理是在运行时通过反射机制动态生成代理类。Java中主要有两种实现方式:JDK动态代理和CGLIB动态代理。
实现方式:
- JDK动态代理:通过
java.lang.reflect.Proxy
类和InvocationHandler
接口实现。只能代理实现了接口的类。 - CGLIB动态代理:通过继承目标类来生成子类进行代理。可以代理没有实现接口的类。
优点:
- 灵活性高,可以在运行时动态生成代理类。
- 代码复用性高,可以通过一个通用的代理类来代理多个目标类。
缺点:
- 性能稍低于静态代理,因为涉及到反射机制。
- 调试和排错相对困难。
JDK动态代理示例:
1 | import java.lang.reflect.InvocationHandler; |
CGLIB动态代理示例:
1 | import net.sf.cglib.proxy.Enhancer; |
总结
- 静态代理:在编译时创建代理类,优点是简单易懂,缺点是灵活性差、代码量大。
- 动态代理:在运行时创建代理类,优点是灵活性高、代码复用性好,缺点是性能稍差、调试困难。
根据具体的应用场景选择合适的代理模式,可以在保证代码简洁性的同时,实现灵活的功能扩展。
接口和抽象类有什么共同点和区别?
共同点:
- 都不能被实例化。
- 都可以包含抽象方法。
- 都可以有默认实现的方法(Java 8 可以用
default
关键字在接口中定义默认方法)。
区别:
- 接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系。
- 一个类只能继承一个类,但是可以实现多个接口。
- 接口中的成员变量只能是
public static final
类型的,不能被修改且必须有初始值,而抽象类的成员变量默认 default,可在子类中被重新定义,也可被重新赋值。
深拷贝,浅拷贝,引用拷贝
浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
引用拷贝: 引用拷贝就是两个不同的引用指向同一个对象。
为什么需要同时重写hashCode和equals
当你在集合如HashMap
、HashSet
或Hashtable
中存储对象时,这些集合使用哈希表来存储元素。哈希表基于哈希码来快速存储和检索元素。如果只重写了equals
方法而没有重写hashCode
方法,会导致以下问题:
- 违反
hashCode
和equals
的合同:- 根据Java规范,如果两个对象根据
equals
方法比较是相等的,那么它们的hashCode
也必须相等。 - 如果只重写
equals
方法但不重写hashCode
方法,则两个对象即使在逻辑上是相等的(equals
返回true),它们的哈希码也可能不同。这会违反合同,导致集合行为异常。
- 根据Java规范,如果两个对象根据
- 集合操作的正确性:
- 插入操作:当你往
HashSet
或HashMap
中插入一个对象时,集合会先调用对象的hashCode
方法来找到存储桶位置,然后在该位置用equals
方法检查对象是否已经存在。如果hashCode
不一致,相等的对象可能会被放到不同的存储桶,导致集合中包含重复的对象。 - 查找操作:类似地,查找操作(如
contains
或get
)也依赖于哈希码来快速定位对象。如果hashCode
不一致,即使两个对象通过equals
方法比较是相等的,查找操作也可能失败,因为它们可能在不同的存储桶中。
- 插入操作:当你往
String 为什么是不可变的?
String
类中使用final
关键字修饰字符数组来保存字符串,保存字符串的数组被final
修饰且为私有的,并且String
类没有提供/暴露修改这个字符串的方法。String
类被final
修饰导致其不能被继承,进而避免了子类破坏String
不可变。
String s1 = new String(“abc”);这句话创建了几个字符串对象?
会创建 1 或 2 个字符串对象。
如果字符串常量池中不存在字符串对象“abc”的引用,那么它会在堆上创建两个字符串对象,其中一个字符串对象的引用会被保存在字符串常量池中。
如果字符串常量池中已存在字符串对象“abc”的引用,则只会在堆中创建 1 个字符串对象“abc”。
Java值传递
Java 中将实参传递给方法(或函数)的方式是 值传递:
- 如果参数是基本类型的话,很简单,传递的就是基本类型的字面量值的拷贝,会创建副本。
- 如果参数是引用类型,传递的就是实参所引用的对象在堆中地址值的拷贝,同样也会创建副本。
Unsafe类
待补充。。。。。
SPI
https://www.bilibili.com/video/BV1E44y1N7Nk/
Java的SPI(Service Provider Interface)机制是一种服务发现机制,用于为某个接口寻找实现类,使得接口的实现可以在运行时动态加载。这一机制通过在META-INF/services目录下配置文件来实现,配置文件中指定了服务接口的实现类。这在Java生态系统中非常常见,尤其是在实现可插拔架构和模块化设计时。以下是对Java SPI机制的详细讲解:
SPI机制的组成部分
服务接口(Service Interface):这是一个Java接口,定义了服务的功能规范。所有的服务提供者都需要实现这个接口。
服务提供者(Service Provider):这是实现服务接口的具体类。在SPI机制中,服务提供者的类需要在一个特定的配置文件中注册。
配置文件:这是一个位于
META-INF/services
目录下的文件,文件名是服务接口的全限定名(包括包名)。文件内容是具体的服务提供者类的全限定名。
SPI机制的工作流程
- 定义服务接口:定义一个服务接口,描述所提供的服务。
- 实现服务接口:创建一个或多个实现类,实现服务接口。
- 创建配置文件:在
META-INF/services
目录下创建一个文件,文件名是服务接口的全限定名,文件内容是服务提供者实现类的全限定名。 - 加载服务提供者:使用
ServiceLoader
类动态加载并实例化服务提供者。
实际例子
假设我们有一个服务接口com.example.MyService
和一个实现类com.example.impl.MyServiceImpl
。
步骤1:定义服务接口
1 | package com.example; |
步骤2:实现服务接口
1 | package com.example.impl; |
步骤3:创建配置文件
在META-INF/services
目录下创建一个文件,文件名为com.example.MyService
,文件内容为:
1 | com.example.impl.MyServiceImpl |
步骤4:加载服务提供者
使用ServiceLoader
动态加载服务提供者:
1 | package com.example; |
运行结果
执行Main
类的main
方法时,会输出:
1 | Executing MyServiceImpl |
SPI机制的优势
- 松耦合:通过SPI机制,接口和实现类之间可以实现松耦合,不需要在编译时确定具体实现类。
- 可扩展性:可以在运行时动态加载新的实现类,方便系统扩展。
- 模块化设计:有利于实现模块化设计,服务提供者可以独立于服务接口开发和发布。
使用场景
SPI机制广泛应用于各种Java框架和库中,如Java的JDBC
、JAX-WS
、JAX-RS
等。这些框架和库通过SPI机制动态加载具体的驱动或实现类,增强了系统的灵活性和可扩展性。
通过SPI机制,可以很方便地实现服务的动态加载和管理,提供了良好的扩展能力和模块化支持。在实际开发中,可以利用这一机制构建灵活、可扩展的系统架构。
场景题
单点登录这块怎么实现的
使用jwt解决单点登录的流程如下:
回答要点:
1,先解释什么是单点登录
单点登录的英文名叫做:Single Sign On(简称SSO)
2,介绍自己项目中涉及到的单点登录
3,介绍单点登录的解决方案,以JWT为例
I. 用户访问其他系统,会在网关判断token是否有效
II. 如果token无效则会返回401(认证失败)前端跳转到登录页面
III. 用户发送登录请求,返回浏览器一个token,浏览器把token保存到cookie
IV. 再去访问其他服务的时候,都需要携带token,由网关统一验证后路由到目标服务
权限认证是如何实现的
Spring security
上传数据的安全性你们怎么控制
使用非对称加密(或对称加密),给前端一个公钥让他把数据加密后传到后台,后台解密后处理数据
- 传输的数据很大建议使用对称加密,不过不能保存敏感信息
- 传输的数据较小,要求安全性高,建议采用非对称加密
你负责项目的时候遇到了哪些比较棘手的问题
(1)设计模式
- 工厂模式+策略
- 责任链模式
回答思路
1,什么背景(技术问题)
2,过程(解决问题的过程)
3,最终落地方案
举例:
①:介绍登录业务(一开始没有用设计模式,所有的登录方式都柔和在一个业务类中,不过,发现需求经常改)
②:登录方式经常会增加或更换,每次都要修改业务层代码,所以,经过我的设计,使用了工厂设计模式和策略模式,解决了,经常修改业务层代码的问题
③:详细介绍一下工厂模式和策略模式(参考前面设计模式的课程)
(2)线上BUG
- CPU飙高
- 内存泄漏
- 线程死锁
(3)调优
- 慢接口
- 慢SQL
- 缓存方案
(4)组件封装
- 分布式锁
- 接口幂等
- 分布式事务
- 支付通用
你们项目中日志怎么采集的
采用的ELK
介绍ELK的三个组件:
- Elasticsearch是全文搜索分析引擎,可以对数据存储、搜索、分析
- Logstash是一个数据收集引擎,可以动态收集数据,可以对数据进行过滤、分析,将数据存储到指定的位置
- Kibana是一个数据分析和可视化平台,配合Elasticsearch对数据进行搜索,分析,图表化展示
扫码登录流程
存在临时token
二维码状态由PC端轮询等方式监控
生成二维码,二维码待确认,二维码已确认三种状态
Maven
https://mp.weixin.qq.com/s/K0Q-kwP0HFwZ8qD7awxkFQ
pom文件里的