面试题总结笔记-供学习

1、暴露给外网的api的安全性从哪些方面考虑?

使用Nginx代理转发到真实的服务器;HTTPS协议实现接口地址(需要证书);采用RSA算法(公钥加密 私钥解密)进行接口数据传输;MD5加密(调用者对参数按照顺序进行md5加密,api接收者按照相同的规则对收到的参数md5加密,跟传进来的加密比较,不相等则基本判断数据被篡改);借助springcloud gateway建立网关,通过网关暴露接口;借助Oauth2实现api接口。

2、kafka保证数据不丢失机制?

kafka通信采用一套自行设计的基于TCP层的协议。生产者端kafka的acks值为0生产者只负责发送数据,不管broker端是否接收完成;值为1,生产者要等broker返回校验消息,只要主副本接收数据没有问题就返回校验消息;值为-1(all),生产者需要等待所有副本接收数据正常,才返回校验消息。broker保证数据不丢失主要靠副本机制,建议将acks设为-1。效率也是显而易见。

消费端kafka保证不丢数据依靠偏移量,broker根据groupid查询上次提交的offset(消费端需要手动或自动提交offset),从这个地方继续消费,通过这样可以保证数据不丢失,极端情况可能重复消费;

几个问题:当acks=1或-1需要等待broker回应,存在长时间不回的情况,所以要设置等待时间;如果等待时间内没有回应,那么可以设置重试策略,如果重试次数用完,仍然没有回应,需要触发告警系统,人工干预了;kafka生产者设置缓冲区(buffer.memory),然后把消息收集组装成一个个batch,发到broker;broker没有回应,缓冲区也满了,这时候可以把缓冲区数据保存下来或者清空。

Kafka重启一般不会导致数据丢失,因为kafka数据都是写入磁盘,但是重启过程中,有消费者没有及时提交offset,可能导致数据重复。

kafka快的原因:一是顺序IO,即kafka写入分区数据采用顺序IO追加的方式,而不是随机写入,这就大大提升了写入效率;二是Page Cache和零拷贝,即kafka生产者写入数据采用操作系统自身的Page Cache异步写入磁盘,kafka消费者通过系统的sendfile实现零拷贝;三是压缩和批量处理,即kafka在生产和消费消息时,进行批量生产和批量消费,并对数据进行压缩。

3、Kafka 之网络通信原理:这个博客写的很好,可以学习,图解Kafka的服务端的网络通信模型_石臻臻的杂货铺的博客-优快云博客_kafka网络模型

4、很多if-else优化方法,这里提供常用的方法:

多态技术+工厂模式可以解决多个else的判断,即把条件作为map的key,具体的对象可以放在value;具体的实现可以抽象出一个抽象类,具体方法逻辑实现其中的方法;如下:

   

判断对象是否是不是null可以使用如下:

5、mysql默认使用B+tree,而不是B-tree,avl:

AVL数据结构是一种相对平衡的二叉树,意思是树左右两边的节点个数绝对值不能大于1,如果新来的节点加在树上之后,出现大于1的情况,那么要进行左旋右旋。既然avl数据结构还是二叉树,那么第一树越高,IO操作次数越多;第二树的每个节点只有一个关键字,导致每次IO操作获取目标数据太少(IO吞吐能力差);

         B-tree(多路搜索树、多叉平衡查找树):相较于b+tree,b-tree叶子节点与叶子节点之间没有指针,不能高性能的支持范围查询;非叶子节点存放data,会导致每一层存放的数据比较少,进而导致b-tree的层数很多,那么查数据就需要走更多层次才能得到数据;

         B+Tree:所有的数据是存放在叶子节点,相较于b-tree优点有:基于索引的扫表操作更快,基于索引的排序更优秀,IO吞吐能力更强。

6、为啥建议主键id是递增的,和B+tree有啥关系,为啥不建议使用uuid?

如图所示,由于b+tree结构中数据是自然有序的,自增的话,新来的数据只会追加在后面,如果不是递增,那么会出现插入操作,必会导致这个值之后的所有数据进行后移,代价很大(性能差)。 不使用uuid作为主键,因为b+Tree结构中每个节点是关键字+指针,这个关键字占得地方越小,那么IO操作性能越好,UUID需要varchar(20)进行表示,太占地方,且uuid是随机的,不保证后面生成的值一定比前面生成的大。

7.1、为啥innodb引擎要求一定要建立主键索引

因为数据需要挂在主键索引下;如果没有主键,mysql会默认为我们创建一个int6字节的主键索引,这个默认索引,当在update数据时,会导致行锁变成表锁,大大降低效率。

7.2、为啥innodb中 hash index被定义成AHI(adaptive hash index)

innodb引擎如果检测到某个非主键索引不断被使用,那就认为该索引是热点索引,innodb引擎就会根据该索引树上的索引值构建一个哈希索引,来加速查询搜索,这种哈希搜索只适用于等值搜索。

 7.3、为啥非主键索引在innodb引擎下,非主键索引的B+树的叶子节点存放的是主键值

解决数据一致性和节省存储空间,如果一个表创建了10个非主键索引,那每个索引叶子节点都挂载行数据,那么就浪费很多存储空间;如果每个索引的叶子节点都存放数据,那么进行增改删操作,就需要维护每个索引叶子节点数据,开销大。

7.4、Mysql使用B+tree存储数据的原理:一是B+树是多路平衡查找树,相较于二叉树高度降低很多,降低了IO的次数,提升查询效率;二是B+树非叶子节点不存储真实数据,只存储索引,这样树的每一层可以存储更多的索引,进一步降低树的高度;三是叶子节点之间是双向链表有序排列的,数据已经按照主键的大小排好顺序,叶子节点之间采用双向链表的方式进行关联,便于范围查询。

myisam把主键索引信息存放在myi后缀的文件中,具体的数据存放在myd后缀对应的一行数据;如果建立非主键索引,那么在ibd文件中开辟一个空间来存放,叶子节点存放的是主键id和当前索引的信息。比如在非主键name字段上建立索引,使用name查询数据,流程是先去非主键name字段的索引表中寻找,找到叶子节点存放的主键id,再去主键索引表中根据查到的id查询数据。

在innodb中,只有主键是聚集索引,其他的都是非聚集索引。

7.5、某个字段是否适合建立非聚集索引规则:

count distinct col :count col,这个比值>=1:10,那么该字段建立索引比较靠谱,参考的是数据的离散性,不重复的数据越多,越适合建立索引;查询频率高的字段;可以将多个字段设置成聚合索引;尽量使用数据小的字段。

7.6、举例说明索引的使用,有张表user,使用的是innodb的引擎,表结构有主键是id,联合索引是name+phoneNum,唯一索引userNum,则下面sql的执行效率最高的是哪个(第四个)

  1. Select * from user where userNum=?;
  2. Select name,userNum from user where userNum=?;
  3. Select name,userNum from user where name=?;
  4. Select id,phoneNum from user where name=?;

第一句sql先去userNum索引树查询,找到对应的主键id,再去主键索引树找到真实的数据,并返回;第二句sql先去userNum索引树查询,找到对应的主键id,由于userNum索引树的叶子节点存放的是主键id和userNum,所以在主键索引树种找到真实的数据后,没有查询结果中的name字段,需要在内存中把不需要返回的字段过滤掉;第三第四唯一不同的是返回的字段不同,第四句sql返回的字段都在索引中,可以知道,第四句sql去name+phoneNum索引树上查找,找到之后叶子节点存放的数据就是需要返回的数据,即查询一次索引树就可以得到结果,所以效率最高。

总结:上述第四句sql的查询场景称为使用覆盖索引查询sql,即sql需要返回的字段都在索引字段中,这样效率很高,因为少了一次去主键索引树查询数据的步骤。

innodb引擎索引的结构如下:

7.7、联合索引使用规则:

使用单个字段称为单列索引,多个字段组成称为联合索引。比如对name+phoneNum+age建立联合索引,由于b+tree采用的是从左到右建立索引树,因此这个联合索引相当于三个索引,分别是name、name+phoneNum、name+phoneNum+age。分别讨论如下情况:

  1. where phoneNum=12222 and age=12,用不到上面的索引,第一个比较因子缺失;
  2. where name=”abc” and age=12,这个只能用到name这一个索引;最左匹配原则;
  3. where name=”ABC” and phoneNum>12222 and age=12,只能用到name+phoneNum索引,因为范围后面的索引全失效,使用范围查询对应的字段在索引位置中的右边,不是sql语句所在的右边;
  4. where phoneNum=12222 and name=”ABC”,用到name+phoneNum索引,因为mysql有个优化器(尽管where中条件不是按照索引字段顺序,优化器会优化掉),对当前的sql进行优化,尽量不要这么写。
  5. where name <>’abc’ 或者where name!= ’abc’,不等于会使索引失效;
  6. is null会使用索引,is not null不会使用索引,因此涉及表尽量保证字段不可为null
  7. 以下操作符也可以用到索引:>=,between,in;这些用不到索引:<>,not in,!=

  8. where name like ”%ABC”,like紧接着%会导致索引失效,%在后面索引生效
  9. where name =” ABC” or age=12,or前后两个条件都用到索引,索引才会生效,其中一个用不到索引,那就导致全部都用不到索引。
  10. 关联查询可以考虑使用子查询只查询出主键ID,减少回表操作,类似这样写:select * from t1 inner join (select id from t1 where name=’ABC’ and age =12) t2 on t1.id=t2.id

Sql关于索引的记忆口诀:

全职匹配我最爱,最左前缀要遵守;

带头大哥不能死,中间兄弟不能断;

索引列上少计算,范围之后全失效;

Like百分写最右,覆盖索引不写*;

不等空值还有OR,索引影响要注意;

VAR引号不能丢,sql优化有诀窍。

7.8 事务的四个特性:

  1. 原子性(atomicity:事务是一个不可分割的工作单位,要不全成功,要不全失败,没有中间状态;
  2. 一致性(consistency):根据定义,一致性是指事务执行前后,数据从一个合法性状态变换到另一个合法性状态.这种状态是语义上的而不是语法上的,是跟具体的业务有关;啥是合法的数据状态?满足预定的约束的状态叫做合法的状态.即我们自己定义的符合现实世界的结束的一个状态,满足这个状态,数据就是一致的,不满足这个状态,数据就是不一致的。如果事务中的某个操作失败了,系统就会自动撤销当前正在执行的事务,返回到事务操作之前的状态。例如:一是A账户有200元,转300元出去,此时A账户余额是-100元,此时发现数据是不一致的,因为我们认知中账户余额必须>=0;二是A账户有200元,转50元给B账户,A账户的钱扣掉了,但是各种原因B账户余额没增加,此时数据也是不一致的,因为我们认知是要求A+B的总余额不能变;
  3. 隔离性(isolation):指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰.
  4. 持久性(durability):指一个事务一旦提交,他对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响.持久性是提供事务日志来保证的,日志包括了重做日志和回滚日志.当我们通过事务对数据进行修改的时候,首先会将数据库的变化信息记录到重做日志中,然后再对数据库中对应的行进行修改.这样做的好处是及时数据库崩溃,数据库重启后也能找到没有更新到数据库系统中的重做日志,重新执行,从而使事务具有持久性.总结:四个特性中,原子性是基础,隔离性是手段,一致性是约束条件,而持久性是我们的目的.

7.9、事务的使用:

  1. 显式事务:即手动开启事务,方法是BEGIN或者START TRANSACTION;结束事务采用COMMIT(数据持久化到磁盘)或者ROLLBACK(回滚到操作前的状态);具体格式:BEGIN;需要执行的SQL语句;COMMIT;
  2. 隐式事务:借助数据库本身提供的事务开关,autocommit=off表示事务关闭, autocommit=on表示事务开启,无需手动BEGIN;COMMIT;

7.10、数据库并发操作产生的问题

脏写:事务A明明更新数据成功了,但是过一会查询值没有更新,可能事务B紧接着A做了回滚操作,事务B在事务A提交之前对相同数据的写操作因为事务A的回滚而失效

脏读:事务A读取到事务B还未提交的更新数据,因为事务B可能会回滚

不可重复读:事务A查询多次数据,出现的结果不一样,可能事务B在事务A未结束时修改数据,并把事务B提交

幻读:查询到可能事务未提交的insert数据

7.11、事务的隔离级别(从上面可能出现的问题引申出隔离级别):

  1. Read Uncommited(未提交读):啥也解决不了
  2. Read Commited(已提交读):解决脏读
  3. Repeatable Read(可重复读):解决脏读和不可重复读,
  4. SERIALIZABLE(序列化):解决事务并发的所有问题,并发效率最低,基本不用

注意:mysql默认Repeatable Read,因为 innodb引擎不存在幻读的问题,借助MVCC机制。

7.12、事务日志:事务的隔离性锁机制完成,其他三个属性由redo日志和undo日志完成;Redo日志提供再写入操作,恢复已提交事务修改的页操作,保证数据持久性;Undo日志回滚行记录到某个特定版本,保证事务原子性和一致性;

7.13、Java中的锁的面试题

1.synchronized锁(悲观锁、同步锁)是jvm层面的锁,是Java的关键字。用来同步代码块和方法

2.Lock锁(同步锁),使用lock()和unlock()方法加锁释放锁,一定记得释放锁。

3.sychronized和ReentrantLock区别:

  • 底层实现:sychronized是jvm层面锁,ReentrantLock是jdk提供的API层面锁。ReentrantLock通过利用CAS自旋机制保证线程操作的原子性和volatile保证数据可见性以实现锁的功能。
  • 是否可手动释放锁:sychronized自动释放锁;ReentrantLock可以中断,通过trylock(long timeout,TimeUnit unit)设置超时方法或者将lockInterruptibly()放到代码块中,调用interrupt方法进行中断。
  • 是否是公平锁:sychronized是非公平锁,ReentrantLock可以通过构造函数的一个参数指定,false是非公平锁。

7.14 如何实现接口幂等性:1.前端表单做一些限制 2.token+redis机制 3.分布式锁 4.使用数据库唯一约束 5.使用消息队列,将请求放入队列,异步处理队列中的数据,并去除重复的请求

可以参考接口的幂等性 - SpringBreeze - 博客园 (cnblogs.com)https://www.cnblogs.com/dupengpeng/p/15526542.html

7.15、分布式锁实现思路:一是基于数据库实现分布式锁,用悲观锁(select ... for update)或乐观锁(基于CAS思想,添加version字段实现)实现;二是基于redisson实现分布式锁;三是基于zookeeper实现分布式锁,因为zk规定一个目录下只能有唯一一个文件名,apache提供Curator可以实现这个功能。

7.16、spring事务相关面试题:可以参考Spring面试——事务_千与千寻丶的博客-优快云博客_spring事务面试

7.17、netty实现长连接断开重连解决方案:一是心跳检测机制,一般做法是客户端是定时往服务端发送心跳包,客户端收到回复则说明服务端存活。netty中提供了一个IdleStateHandler类用于心跳检测,用法如下:

ch.pipeline().addLast("idleState", new IdleStateHandler(60, 20, 60 * 10, TimeUnit.SECONDS));

其中的参数说明:第一个参数 60 表示读操作空闲时间;第二个参数 20 表示写操作空闲时间;第三个参数 60*10 表示读写操作空闲时间;第四个参数 单位/秒。

具体实现方法可以参考:Netty 断线重连方案实现_HelloWorldBegin的博客-优快云博客

7.18、mysql索引为啥使用B+tree:能显著减少IO次数,提高效率;让查询效率更稳定,因为数据放在叶子节点;能提高范围查询的效率,因为叶子节点之间存放指针,便于查询。

7.19、mybatis动态sql请参考Mybatis常见面试题(10个必备面试题)_红目香薰的博客-优快云博客_mybatis面试题

7.20、mysql大表优化思路:(合理设计表、编写高效的sql、表分区、分表、分库)详细见大表优化大表优化2

7.21、ConcurrentHashMap原理分析:jdk8以上数据结构是Node 数组 + 链表 / 红黑树,当链表大于8时,就会转为红黑树,基于CAS+synchronized实现线程安全且高效。CAS的ABA问题可以通过版本号或者时间戳解决。

7.22、Netty的零拷贝原理:一是netty的接收和发送采用Direct Buffer(直接缓冲区)实现零拷贝,直接在内存区域分配空间(不在堆内存),避免读写数据的二次内存拷贝,这就实现读写socket的零拷贝;二是netty通过CompositeByteBuf实现对多个ByteBuf组合在一起,避免ByteBuf扩容拷贝的次数;三是Netty的文件传输类DefaultFileRegion通过调用FileChannel.transferTo()方法实现零拷贝,文件缓冲区的数据会直接发送到目标Channel。

7.23、netty的粘包和拆包

  • 原因:由于TCP是基于字节流的协议,数据消息无边界。由于netty为了提高效率,当客户端发送的数据比netty的发送缓冲区小,那么缓冲区就会把多个数据包拼在一起进行发送,即是粘包;当客户端发送的数据太大,那么就会拆包发送,即是拆包。
  • 解决办法:设置消息定长;明确消息边界;设置消息头和消息体。Netty提供了多种数据流编解码器,比如定长的解码器FixedLengthFrameDecoder、指定消息边界(分隔符)的解码器DelimterBasedFrameDecoder、对应消息头和消息体基于数据包长度的解码器LengthFieldBasedFrameDecoder,还可以自定义解码器。

7.24、Netty性能高的原因:

  • 异步非阻塞IO线程模型:IO多路复用技术,可以应对大量的并发请求;
  • 主从Reactor线程模型:主线程(池)用来监听客户端的TCP的连接请求,从线程(池)负责消息的读取、编解码和发送;
  • 无锁串行化设计:消息处理的线程尽可能在一个线程完成,期间不进行线程切换,避免多线程竞争和同步锁的使用;
  • 高效的并发编程:volatile的大量正确的使用;CAS和原子类的广泛使用;线程安全容器的使用;通过读写锁提升并发性能。
  • 高效的序列化框架:Netty默认使用Google Protobuf序列化结构化数据,通过protobuf扩展编解码器
  • 采用零拷贝机制:内存分配采用Direct Buffer,直接在堆外内存开辟空间;采用CompositeByteBuf聚合多个ByteBuf,避免手动操作ByteBuf的拷贝;Netty的文件传输使用transferTo()方法;
  • 采用内存池的ByteBuf:内部维护了一个内存池,可以循环利用已创建的 ByteBuf,提升内存的使用效率,降低由于高负载导致的频繁GC;
  • 灵活的TCP参数设置,如SO_RCVBUF 和SO_SNDBUF,建议设置为128k或256k。

7.25、分布式一致性事务:二阶段提交或三阶段提交(有个事务管理器的概念)、TCC(try,confirm,cancel,手动编写回滚代码逻辑)、本地消息表(操作记录写入数据库,借助MQ实现对状态字段值更新)、可靠消息最终一致性(直接使用RocketMQ实现);可以参考分布式事务博客1分布式事务博客2

7.26、Java内存分配模型:JVM内存模型,JVM类加载机制,JVM调优经验,JVM设计模式JVM内存模型 和 jvm内存解释

7.27、JVM垃圾回收机制:

  • JVM确定回收对象的算法:引用计数法(标记对象被引用的次数,当引用次数是0时就被标记为可回收对象,但是这种算法不用,因为解决不了对象之间循环引用的问题)、可达性分析算法(通过一系列名为GC Roots对象作为起始节点往下搜索,搜索所走过的路径称为引用链,如果一个对象没有引用链即没有可达性,那么没有这个对象会被标记为可回收对象);
  • 哪些对象可以作为GC Roots对象:方法区中类的静态变量的引用对象,方法区中类的常量的引用对象,虚拟机栈中的引用对象,本地方法栈中引用对象
  • 对象回收使用的算法:

标记-清除:第一步标记可回收的对象,第二步清除被标记的对象;缺点是效率问题,也会产生内存碎片;

标记-整理:第一步标记可回收的对象,第二步清除被标记的对象,完成后会重新整理内存,避免产生内存碎片;

复制算法:复制将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对其中的一块进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况。只是这种算法的代价是将内存缩小为原来的一半;

分代算法:分代收集算法是一种比较智能的算法,其实不是一个新的算法,而是他会在具体的场景自动选择以上三种算法进行垃圾对象回收。

7.28、jdk8中使用的JVM垃圾回收器:默认使用的useParallelGC即Parallel Scavenge(复制)+ Parallel Old(标记-整理)

7.29、GC优化可以参考GC优化1GC优化2

7.30、jdk线程池工作流程:

  • 在调用execute(new Runnable(){ })方法添加执行任务时,线程池会做如下操作:

a) 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;

b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;

c) 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要

创建非核心线程立刻运行这个任务;

d) 如果队列满了,而且正在运行的线程数量大于或等于 maximumPoolSize,那么线程池

会抛出异常(可以指定异常策略)。

  • 异常策略如下:

    AbortPolicy:直接抛出一个异常,默认策略

    DiscardPolicy: 直接丢弃任务

    DiscardOldestPolicy:抛弃下一个将要被执行的任务(最旧任务)

    CallerRunsPolicy:主线程中执行任务

7.31、jdk8新增了关于日期和时间的更多方便的操作和方法,接口如下:

7.31、java反射:反射的概念、作用、使用方法、原理等参考这个博客

7.32、java的lamda表达式使用:(parameters) -> expression 或 (parameters) ->{ statements; }

7.33、jdk8处理集合的stream流:合并stream(Stream.concat())、收集stream的结果(stream对象.collect(Collectors.toList()等其它集合)),参考这个博客

7.34、spring AOP:面向切面编程,是一种编程思想,即抽象出与主业务关系不大的逻辑(模块)单独编写,从而降低耦合度。请参考这个博客 和这个博客

7.35、动态代理:

  • jdk动态代理:利用反射机制生成一个业务接口类,代理类必须实现InvocationHandler接口,jdk动态代理只能代理实现了接口的类。Mybatis 底层封装使用的 JDK 动态代理(参考)

编写动态代理类使用步骤:1、编写需要代理的类和接口(类必须实现接口)2、编写代理类,实现实现InvocationHandler接口,重写invoke方法3、使用Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)动态创建代理类对象,通过代理类对象调用业务方法。

  • cglib动态代理:是针对类实现代理的,它会对目标类产生一个代理子类,通过方法拦截技术对类中方法的调用,代理子类需要实现MethodInterceptor接口,如果是基于springboot开发,可以采用注解的方式实现动态代理

以上的两种具体的说明可以参考这个博客这个博客

7.36、spring bean的生命周期、spring batch、spring data

7.37、redis缓存和数据库一致性问题:延时双删策略+key超时策略;基于Canal框架订阅binlog日志实现异步同步,请参考这个博客

7.38、springboot自动装配原理:@springbootapplication->@enableautoconfiguration->@import(autoconfigurationimportselector.class),参考这个博客这篇文章

7.39、feign的原理:eureka作为注册中心;@EnableFeignClient注解启动starter组件,扫描包下被@FeignClient注解的接口类,并注册到IOC容器;此时通过jdk动态代理生成代理类,当接口被调用时会被动态代理类逻辑拦截,将@FeignClient请求信息通过编码器生成Request;再交由ribbon进行负载均衡,挑选一个健康的server实例进行调用;继而通过Client携带Request调用远程服务。请参考这个博客

7.40、nacos中微服务注册与发现:服务提供这调用nacos接口注册服务,然后维护一个心跳的定时任务;nacos接收到微服务注册时,通过udp协议推到服务消费者,为防止udp数据丢失,消费者也会每10s请求下nacos,这样可以保证获取到最新的提供者列表,请参考这篇博客

7.41、SpringMVC的请求流程(原理):dispatchServlet,请参考这篇文章

7.42、springAOP怎么选择使用哪种动态代理:当被代理类有实现接口的类,那SpringAOP选择jdk动态代理用于,否则选择使用cglib代理,请参考这篇文章

7.43、dubbo相关可能会问的面试题,请参考这篇博客

7.44、explain执行结果分析:type和key_len的返回值多关注,可以为优化提供思路,请参考这篇博客

7.45、RPC框架为啥要序列化?概括说因为网络传输数据用的是二进制数据,那么就需要一个标准来告诉框架我是怎么把对象转二进制的,详细请参考这篇博客

7.46、代理模式:23中设计模式,其中单例模式,工厂模式、代理模式,可以参考这篇文章

7.47、spring的特性:IOC(控制反转)或叫DI(依赖注入)和AOP(面向切面编程),DI是指在IOC容器运行中,动态的将依赖关系注入到对象之中;AOP是指通知+切面,可以参考这篇文章

7.48、对于CAP理论,指的是一致性(consistency),可用性(availability),分区容错性(partition tolerance),在分布式中网络是不可控的,所以先要保证P,然后在A C之前选择,要不AP或者CP。常见的eureka是AP,zk是CP,nacos默认是AP,可以切换成CP,redis是AP。

7.49、锁升级(锁膨胀):偏向锁->轻量级锁->自旋锁->重量级锁(按照这个顺序进行升级),具体参考这篇文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值