-
讲讲JAVA的反射
"反射啊,就是Java在运行时能够动态获取类的信息,比如它的属性、方法、构造器这些。反射常用在框架开发里,比如Spring的IoC容器,它通过反射来实例化对象、注入依赖。但反射也有缺点,性能比直接调用要慢,还容易绕过编译期的类型检查,所以平时业务代码里得悠着点用。"
-
可以继续讲讲AOP,动态代理
"AOP就是面向切面编程,说白了就是把日志、事务这些跟业务逻辑无关但又通用的代码抽离出来,动态地织入到需要的地方。
动态代理啊,这个其实就是Java里一种在运行时动态生成代理类的技术。我打个比方,你想给某个类的某些方法进行扩展,但又不想改原来的代码,这时候就可以用动态代理。
Java里主要有两种方式:JDK自带的动态代理和CGLIB。先说**JDK动态代理,它要求目标对象必须实现接口。**它的核心是两个东西:一个是InvocationHandler接口,你要实现它的invoke方法,在里面写增强逻辑;另一个是Proxy类,它通过newProxyInstance静态方法去生成代理对象。代理对象调用方法时,就会自动走进invoke方法里,你可以在那前后插入代码。这种方式的底层是用反射来调用目标方法的。
另一种是CGLIB动态代理,它不要求目标类有接口,**直接通过生成目标类的子类来代理。**原理是用ASM字节码操作框架,在内存里构建目标类的子类,然后重写父类的方法,在里面织入增强逻辑。Spring的AOP在目标类没有接口的时候就会自动切换到CGLIB。CGLIB因为是继承的方式,所以目标类和方法不能被final修饰,否则没法重写。
两种方式各有优缺点:JDK动态代理性能好一点,但必须依赖接口;CGLIB可以代理没有接口的类,但需要引入额外的库,而且对final方法无能为力。Spring AOP默认用的是JDK动态代理,如果目标对象没有接口,才用CGLIB。
-
OK,讲讲 Redis 的数据类型?
String:就是最简单的键值对,值可以是字符串、数字或者二进制。它支持原子性的增减,所以既能当缓存存对象,也能做计数器、分布式锁。
Hash:像一个对象的字段集合,可以单独读写某个字段,不用整体序列化。适合存用户信息、商品详情这种需要频繁修改部分属性的场景。
List:底层是链表,可以从左边push、右边pop,实现先进先出的队列;也可以从两边push/pop实现栈。因为是有序的,还能做分页展示,比如取最新的N条粉丝。
Set:元素唯一且无序,支持交集、并集、差集运算。很适合做标签系统、共同好友,或者需要去重的场景。
Sorted Set(ZSet):每个元素带一个分数,按分数排序,分数可以重复。排行榜就是典型应用。
Bitmap:本质是字符串,但按位操作。一位代表一个状态,内存极小,适合做日活统计、用户在线状态。
HyperLogLog:无论存多少数据,内存固定(12KB左右)。非常适合统计UV、页面访问量这种不需要精确值的场景。
Geo:把经纬度编码成52位整数存在ZSet里,支持计算距离、范围查询。能做地理位置搜索。
Stream:是专门的消息队列类型,支持持久化、消费组、消息确认和阻塞读取。比List更可靠,适合需要保证消息不丢、分组的场景。
-
讲讲Redission分布式锁实现
它实现的分布式锁核心是利用Redis的Lua脚本保证原子性。加锁时会用SET NX命令设置一个带过期时间的键,防止死锁。而且Redisson有一个'看门狗'机制,如果业务没执行完,它自动给锁续期,避免锁提前释放。锁本身支持可重入,同一个线程可以多次加锁。解锁时也要用Lua脚本检查是不是自己加的锁,防止误删别人的锁。
-
ok,那讲讲JMM
JMM就是Java内存模型,它定义了多线程环境下变量怎么访问。每个线程有自己的工作内存(相当于CPU缓存),里面保存了主内存变量的副本。线程之间不能直接访问对方的工作内存,只能通过主内存来传递。这就带来了可见性、原子性和有序性问题。volatile关键字可以保证可见性和禁止指令重排序,但不能保证原子性。happens-before规则是JMM里判定数据是否冲突的依据,比如锁的解锁发生在加锁之前,volatile写发生在读之前。理解了这些,才能写好并发代码。
-
讲讲RabbitMQ如何保证的不丢消息
RabbitMQ 保证不丢消息,主要看三个环节:
生产者发消息:开 Confirm 模式。消息送到交换机后,MQ 会回调告诉你"收到了",如果没收到回调或者失败了,你就重发。
消息本身:交换机、队列、消息都设置成持久化(durable)。这样哪怕 MQ 重启,消息也不会丢,存在磁盘里。
消费者拿消息:关掉自动确认,用手动 ack。也就是你处理完业务逻辑,才告诉 MQ "我处理完了,你可以删消息了"。如果没 ack 就崩了,MQ 会再发给别人,确保不丢。
-
讲讲RabbitMQ延迟消息原理
它本身没直接支持任意时间的延迟消息,是通过一个插件(rabbitmq_delayed_message_exchange)实现的。
消息发到特殊的延迟交换机,交换机不立马投递,而是根据你设置的延迟时间(比如 30 分钟)把消息暂存起来,时间一到,才扔到队列里,消费者就能消费了。
这些是面试比较常见的问题,在看看自己还有那些不懂,背的时候,自己总结着背,祝你们都能获取到想要的offer。如果这篇文章对你有帮助请给一个赞或者关注,创作不易感谢你的阅读。
