Java备忘录
Java
一、HashMap和HashTable的区别
- 线程安全性:
- HashMap是非线程安全的,不适合多线程环境。
- HashTable是线程安全的,但可能在高并发情况下性能受限。
- null键值:
- HashMap允许有一个null键和多个null值。
- HashTable不允许null键或null值。
- 继承关系:
- HashMap继承自AbstractMap,实现了Map接口。
- HashTable继承自Dictionary,不是一个接口,而是一个类。
- 迭代器:
- HashMap的迭代器是fail-fast的。
- HashTable的迭代器不是fail-fast的。
二、HashMap
- 利用键计算哈希值决定存储索引
- null则添加,key一样则覆盖,key不一样,出现哈希碰撞的话,
JDK8 之前:
- 新数据添加到数组中,而旧数据挂载在新数据下面,形成链表
- 在JDK 1.7中,HashMap的扩容机制是在扩容时将桶的数量翻倍
JDK8
- 新数据挂载在旧数据下面,形成链表,当链表长度超过 8 && 数组长度>64的时候,链表自动转为红黑树
三、红黑树
红黑树(Red Black Tree)是一种特殊的自平衡二叉树, 它可以在O(log n)时间内做查找,插入和删除
map和list
Spring
Spring是一个框架,同时是一个容器,还是一个生态。Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用 于简化企业应用程序的开发,它使得开发者只需要关心业务需求。常见的配置方式有三种:基于XML的 配置、基于注解的配置、基于Java的配置。
AOP
任何个系统都是由不同的组件组成的,每个组件负责一块特定的功能,当然会存在很多组件是跟业务无关的,例如日志、事务、权限等核心服务组件,这些核心服务组件经常融入到具体的业务逻辑中,如果我们为每一个具体业务逻辑操作都添加这样的代码,很明显代码冗余太多,因此我们需要将这些公共的代码逻辑抽象出来变成一个切面,然后注入到目标对象(具体业务)中去,AOP正是基于这样的一个思路实现的,通过动态代理的方式,将需要注入切面的对象进行代理,在进行调用的时候,将公共的逻辑直接添加进去,而不需要修改原有业务的逻辑代码,只需要在原来的业务逻辑基础之上做一些增强功能即可。
IOC
之前我们需要什么对象自己去创建什么对象,有我们自己来控制对象,而有了I0C容器之后,就会变成由IOC容器来控制对象,控制反转也叫依赖注入,利用了工厂模式。是将*对象交给spring管理,只需要在spring配置文件中配置相应的bean,以及设置相关属性,让spring容器来生成类的实例对象以及管理对象。,依赖的对象直接由工OC容器创建后注入到对象中,由主动创建变成了被动接受,这是反转
Java
统计string出现的字母个数
JDK1.8新特性: lambda表达式,Stream流计算,Data类API,四大函数式接口
Spring
IOC:
控制反转也叫依赖注入,利用了工厂模式。是将*对象交给spring管理,只需要在spring配置文件中配置相应的bean,以及设置相关属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把配置文件配置的bean都初始化好,当需要调用的时候,就把它已经初始化好的bean分配给需要调用这些bean的类
AOP:
面向切面编程:
bean单例线程安全问题:使用”懒汉式”或”饿汉式”来创建单例
动态代理:jdk、cglib、apsectjAop
JDK动态代理通常用于代理接口实现,CGLIB代理适用于代理类,而AspectJ代理通常用于AOP编程。
SpringCloud
SOA架构:它将应用程序的不同功能单元(称为服务)进行拆分,
微服务架构:同一个系统中,把不同的业务把它拆分成一个个单独的服务,而对于这些服务,可以交给一个团队去独立的技术选型,独立的部署独立的运维,这是我对微服务的认识;
五大组件作用:微服务架构,拥有众服务,服务就要涉及到一个叫做注册中心,我学习常用的是nacos,注册中心主要承当的作用就是把各种服务注册到这里面去,实现要调用它的就来这个注册中心来拉取,就是服务的发现嘛,
调用:对于同一个服务我有可能注册多个,第二个组件就,使用ribbon/feign,实现负载均衡调用,从注册中心拿到一堆的服务列表,拿到的同一个服务可能注册了多个嘛,就需要一定的负载均衡策略进行调用,
Sentinel:当我们调用服务的时候,如果服务挂了。这时候为了服务的一个健壮性,要用到springcloud的另一个组件叫还是hystrix,我学习的是Sentinel,主要用于熔断和降级以及限流的
配置中心:我们一个项目拥有众多服务,每个服务可能都会拥有自己的配置,当这些配置分散到不同的项目中去,是不好管理的,所以需要用到另一个组件统一的配置中心springcloud configure,我使用了nacos实现了配置中心管理,
网关:使用gateway,一个项目拥有多个微服务,他最终他是要暴露给前端调用的,如果是一个服务就有一个地址呢,他是非常不好管理的,使用网关实现统一地址,也可以对微服务访问进行鉴权的作用
负载均衡:使用Ribbon,可以选择轮询规则、加权轮询规则、随机规则、最少连接规则,响应时间加权规则,本地集群优先规则
服务降级:服务降级通常是通过使用熔断器来实现的,使用最多的是Hystrix库,我学习的是Sentinel,他们之间的区别是:Sentinel注解提供实时监控和统计功能,可以直接在服务运行时动态修改流量控制和降级规则,实现服务降级的策略有:流控、热点参数限流、慢请求降级,断路器等策略进行降级处理,返回默认值或者缓存数据等
限流:采用流控模式进行限流,包括直接限流(对读取资源限流,设置指定qps阈值)、关联限流(统计两个端点的操作同一资源的qps阈值,当重要一个端达到阈值,对较不重要的端点进行限流,让步重要的一点)、链路限流(针对指定链路访问该资源的请求进行限流)
熔断策略:断路器的熔断策略有:
慢调用比例策略(当业务的响应时间RT大于指定的RT时,判为慢调用,当慢调用的比例超过设置的阈值就会触发熔断。防止由于慢请求导致系统性能下降)
还有异常比例也叫异常数策略,当请求中的异常比例超过设定的阈值时,也会触发熔断
当触发熔断后,Sentinel会将后续的请求拦截并进行降级处理,例如返回默认值、缓存数据等,以避免对不稳定的服务继续请求。
分布式事务:可以使用基于数据库的分布式事务或者分布式事务管理框架:Seata:三个角色TC:事务协调者,TM:事务管理器,RM:资源过滤器。包括四种模式
XA:统一提交(第一阶段:注册分支事务到TC,等待全部事务状态完成后统一提交,如果出现事务失败,通知RM在,在第二阶段完成回滚)实现强一致性,满足ACID原则,没有代码侵入
AT:记录undo-log数据快照,在各事务中,注册分支事务后,产生undolog,再执行业务并提交,如果其他分支事务状态成功时,则删除全部undolog,否则根据undolog进行回滚
TCC:TCC模式将一个分布式事务分解为三个关键步骤:Try(尝试)、Confirm(确认)和Cancel(取消),在Try中,事务的操作不会立即生效,而是进行预执行,confim确认事务操作,Cancel中对已经执行的 Try 阶段将被回滚,事务将被取消
Saga:一阶段:直接提交本地事务 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚,没有锁,没有事务隔离,会有脏写
Redis
Redis,一个非关系型数据库,一个开源的高性能键值对存储系统,也是一个支持持久化的内存数据库,通过持久化机制把内存中的数据同步到硬盘文件来保证数据持久化。
事务:multi开启事务,命令入队,exec提交事务,统一执行
乐观锁:事务开启前 使用 watch监视key,exec的时候根据被监视的key是否发生变化进而决定是否执行事务失败,失败后先先解锁再对新的值上锁
缓存穿透:
查询数据的时候,发现redis内存数据库不存在该数据,也就是缓存没有命中,于是向持久层数据库查询,发现也没有,那这次查询就失败了。例如秒杀活动中,当请求过多,而缓存都没命中,于是都向持久层数据库查询,给持久层造成极大压力,这就相当于出现缓存穿透;
解决方法:
缓存空对象:对于查询后返回了空结果的,可以将其缓存为一个特殊值和设置ttl,这样下次查询相同的键即可命中,避免总结访问底层数据库
布隆过滤器:已经将数据库的数据的哈希值保存到布隆过滤器,请求会预先过滤那些肯定不在缓存中的数据,减少缓存访问,缓解缓存穿透问题
还有热点数据缓存:对于频繁查询的数据设置较长的失效时间或者使用缓存预热
缓存击穿
缓存中一个数据在不停扛着大并发,大并发集中对这个点进行访问,当这个数据缓存过期瞬间,持续的大并发就穿破缓存,直接请求持久层数据库,从而导致了底层资源的瞬时压力
解决:设置热点数据延迟失效;也可设置快速失败:如果缓存失效时,可以使用快速失败机制,立即返回一个默认值或错误,而不再触发底层查询。使用互斥锁,保证每个数据只可以一个线程去查询持久层然后写入缓存,其他线程等待,这对分布式锁考验很大。redis也可以实现分布式锁(互斥锁),使用setnx插入key,并设置过期时间,如果插入成功则表面读取获取了锁,插入失败表面锁被占用,需等待,业务完成后需要手动删除key释放锁,不删除等待实现也会自动释放锁
缓存雪崩
在一段时间内,缓存集中失效,例如redis宕机,这样所有的查询都落到了持久层数据库上,会产生周期性的压力波峰,会造成持久层挂掉的情况,造成缓存雪崩现象;
解决方案:
搭建redis集群,异地多活,实现redis高可用
也可以限流降级,比如一个数据只可以一个线程去查询数据和写缓存,其他线程等待
预热缓存:在应用启动时,预先加载一部分热门数据到缓存中,以降低缓存冷启动时的压力。
RDB:
RDB持久化通过定期创建快照来保存Redis数据集的状态,我们可以通过判断save条件是否成立进而触发rdb规则,save我们可以设置例如在多少秒内对多少个key进行修改呀就触发持久化操作,当我们flushall清空数据库的之前,他也会自动触发rdb规则,退出redis也会触发rdb规则;持久化会生成后缀为rdb的文件,如果需要恢复则把该文件放到启动目录即可,redis启动会自动检查恢复
AOF:
就算将所有的命令全部记录下来保存到一个文件中,恢复的时候把这个文件的全部命令执行一遍,
主从复制:
MySQL
Mybatsi
1# 与 $ 区别
#
是预编译占位符,使用#将参数值安全地替代占位符,因为 MyBatis 在处理 #
占位符时,会将参数值作为参数绑定,而不是将其视为SQL的一部分
$
是直接替换占位符。在使用$
占位符时,MyBatis会将$
后的内容直接替换为参数的字符串表示
usegeneratekeys:useGeneratedKeys
是 MyBatis 中的一个属性,通常用于配置在插入操作(INSERT
)时是否返回生成的主键。
MybatisPlus
MyBatis-Plus:
- MyBatis的增强工具:MyBatis-Plus是对MyBatis的增强,提供了许多额外的功能,用于简化数据库操作。
- 自动生成SQL:MyBatis-Plus可以根据实体类自动生成SQL查询、更新等语句,减少了手动编写SQL的工作。
- 内置的CRUD方法:MyBatis-Plus内置了常见的CRUD(创建、读取、更新、删除)方法,可以通过简单的方法调用执行这些操作。
- Lambda表达式支持:MyBatis-Plus支持使用Lambda表达式来构建类型安全的查询条件。
haskmap底层
JDK 1.7中的HashMap:
- 底层数据结构:在JDK 1.7中,HashMap的底层数据结构是一个数组和链表的组合。具体说,它使用了数组存储桶(buckets),每个桶包含一个链表,用于解决哈希冲突。这种结构在处理哈希冲突时,需要遍历链表,因此当哈希冲突较多时,性能可能会受到影响。
- 扩容机制:在JDK 1.7中,HashMap的扩容机制是在扩容时将桶的数量翻倍。这会导致在扩容时性能下降,因为所有的元素需要重新计算哈希码和重新分配到新的桶中。
JDK 1.8中的HashMap:
- 底层数据结构:在JDK 1.8中,HashMap的底层数据结构也是一个数组和链表的组合,但引入了红黑树来替代链表,以提高性能。当链表变得过长时,JDK 1.8中的HashMap会将链表转换为红黑树,这可以减少查找时间。
- 扩容机制:在JDK 1.8中,HashMap的扩容机制更加智能化。它引入了树化阈值,当链表中的元素数量超过一定数量时,链表会转化为红黑树。这减少了在链表中查找元素的时间。
- Bucket数组初始化:在JDK 1.8中,HashMap的桶数组是在首次添加元素时初始化的,而不是在创建HashMap对象时就初始化。这可以节省内存,因为不必一开始就分配大量的桶。
嵌套查询和嵌套结果
SpringMVC
SpringMvc其实就是Spring框架其中的一个Web模块!Web相关的模块:封装了传统Servlet操作,让控制层开发起来更高效和简洁
M:Model 模型 (模型层:业务逻辑层+数据库访问层)
V:view 视图( JSP(翻译java—>编译class—–>运行)、模板引擎:Themleaf、FreeMarker..)
C: Controller控制 (Servlet控制层)
javaweb
cookie和session
Cookie:Cookie是存储在客户端(用户浏览器)的小型文本文件。每个HTTP请求都包含Cookie,这允许在不同的请求之间共享数据。
Session:Session数据通常存储在服务器上,而不是客户端。客户端只包含一个用于检索会话数据的唯一标识符,通常是一个称为Session ID的Cookie。
Cookie:Cookie通常用于存储小量的数据,例如用户首选项、标识和跟踪信息等。Cookie可以设置过期时间,允许数据在客户端上存储一段时间。
Session:会话通常用于存储更大量的数据,例如用户的购物车内容、登录状态、用户权限等。会话数据存储在服务器上,通常在用户登录后创建,并在用户注销或会话过期时销毁。
如果浏览器禁用cookie怎么办:使用URL重写会话标识符、使用隐藏字段、使用其他身份验证方法
接口幂等性
- 使用HTTP方法:HTTP协议本身支持幂等性,因此在设计RESTful API时,应根据HTTP方法的幂等性来选择适当的方法。GET和PUT方法通常是幂等的,而POST方法通常不是幂等的。因此,尽量使用GET和PUT来执行幂等操作。
- 生成唯一请求标识:为了确保每个请求的唯一性,可以在每个请求中生成一个唯一的请求标识,例如UUID。在处理请求时,可以在服务端保存已处理请求的标识,以确保相同的请求不会被重复执行。
- 使用数据库事务:如果接口操作涉及到数据库更新,使用数据库事务来保证操作的原子性。这可以确保即使同一个请求被多次执行,数据库中的数据也不会因为重复操作而出现问题。
- 使用乐观锁:在多线程环境下,使用乐观锁来防止并发冲突。乐观锁通常基于版本号或时间戳,可以帮助确保操作的幂等性。
servlet生命周期
- 初始化阶段(Initialization):
- 当Web服务器启动或者Servlet容器初始化时,Servlet容器会加载Servlet类并创建一个Servlet实例。
- 在这个阶段,
init()
方法会被调用。通常,开发人员可以在这个方法中进行一些初始化工作,例如创建对象、建立数据库连接等。
- 请求处理阶段(Request Handling):
- 当接收到客户端的HTTP请求,Servlet容器会创建一个新的线程或从线程池中获取线程来处理请求。
- 在这个阶段,
service()
方法被调用,它接收HTTP请求和响应对象,并处理请求。具体的业务逻辑在此方法中实现。
- 销毁阶段(Destruction):
- 当Web服务器关闭或者Web应用程序被卸载时,Servlet容器会调用
destroy()
方法来销毁Servlet实例。 - 在这个阶段,开发人员可以在
destroy()
方法中进行一些清理工作,如释放资源、关闭数据库连接等。
- 当Web服务器关闭或者Web应用程序被卸载时,Servlet容器会调用
- 服务可用性阶段:
- 在Servlet的生命周期中,Servlet实例通常保持在内存中,以提供服务。
- 这意味着Servlet可以在多个请求之间共享状态,但也需要小心处理多线程并发问题。
,每个Servlet实例都是单例的
SpringBoot
Component 和 bean
@Component
是一种基于注解的方式来定义Spring bean。您可以将@Component
注解放在Java类上,Spring容器会自动扫描并创建该类的bean实例。@Bean
是一种基于Java方法的配置方式。您需要在一个Java配置类中定义方法,并在方法上添加@Bean
注解,以指定要创建的bean实例。@Component
通常用于定义普通的Spring bean,适用于任何类。@Bean
通常用于在Java配置类中创建特殊类型的bean,或者在配置中需要更多控制的情况下使用。
Linux
查看日志 cat
使用日志工具查看 tail -f /var/log/syslog
。
使用 cat
或 less
命令来查看系统日志文件的内容,例如:cat /var/log/syslog
。
查看端口占用 lsof -i:8080
Html
二级联动
网络
http 与 https
- HTTP (HyperText Transfer Protocol):
- 不安全:HTTP传输的数据是明文的,不进行加密。
- 使用的默认端口是80。
- 通信速度较快,适用于不涉及敏感信息传输的场景,如普通网页浏览。
- HTTPS (HTTP Secure):
- 安全:HTTPS通过使用 SSL/TLS 加密协议来保护数据的安全性。传输的数据是加密的。
- 使用的默认端口是443。
- 通信速度较慢,因为加密和解密数据需要额外的计算资源。
- 适用于需要传输敏感信息(如个人信息、密码、信用卡信息等)的场景,如在线银行、电子商务网站等。
HTTPS通过使用数字证书来验证网站的身份,以确保用户连接到正确的服务器,而不是中间人攻击。HTTPS还提供数据完整性,确保传输的数据在传输过程中没有被篡改。
状态码
- 1xx(信息性状态码):请求正在处理。
- 100(继续):服务器已收到请求的一部分,客户端可以继续发送请求的其余部分。
- 2xx(成功状态码):请求成功被服务器接受、理解和处理。
- 200(OK):请求成功,服务器返回所请求的数据。
- 201(Created):请求已成功,并且创建了新资源。
- 204(No Content):请求成功,但服务器未返回任何内容。
- 3xx(重定向状态码):需要进一步操作以完成请求。
- 301(Moved Permanently):资源的URL已永久更改,客户端需要更新书签。
- 302(Found):资源的URL临时改变,客户端应使用新URL重新发起请求。
- 304(Not Modified):资源未被修改,客户端可以使用缓存的数据。
- 4xx(客户端错误状态码):请求包含错误或无法被服务器理解。
- 400(Bad Request):请求无效或包含无效数据。
- 401(Unauthorized):需要身份验证或认证失败。
- 403(Forbidden):禁止访问请求的资源。
- 404(Not Found):请求的资源未找到。
- 5xx(服务器错误状态码):服务器在处理请求时发生错误。
- 500(Internal Server Error):服务器遇到不可恢复的错误。
- 502(Bad Gateway):作为代理服务器的服务器从上游服务器接收到无效响应。
- 503(Service Unavailable):服务器暂时无法处理请求。
这只是HTTP状态码的一小部分,还有其他状态码用于表示不同的情况。HTTP状态码是与HTTP请求和响应一起使用的,以便客户端和服务器之间能够进行有效的通信和错误
RabbitMQ消息可靠
RabbitMQ 是一种消息队列中间件,用于在分布式系统中传递消息。为了确保消息的可靠性,RabbitMQ 提供了一些机制和最佳实践来减少消息丢失的风险和确保消息被可靠地传递:
- 消息持久性:在发送消息时,可以将消息标记为持久性。这样,即使RabbitMQ服务器在消息发送后宕机,消息也会被保存,并在服务器恢复时重新传递。要创建持久性消息,需要设置消息的deliveryMode属性为2。
- 队列持久性:创建队列时,可以将队列标记为持久性。这样,即使RabbitMQ服务器宕机,队列也会被保留。这需要在声明队列时设置durable属性为true。
- 确认机制:生产者可以等待消息被确认后才将其视为已成功发送。消费者可以发送确认消息来告诉RabbitMQ已成功处理消息。这种确认机制可以确保消息在生产者和消费者之间的可靠传递。
- 发布者确认:RabbitMQ提供了发布者确认机制,它允许生产者在消息被成功写入队列时收到确认。这可以在生产者代码中启用以确保消息在发送后不会丢失。
- 备份交换机:可以为交换机配置备份交换机,以便在消息无法路由到目标队列时将消息路由到备份交换机。这有助于防止消息丢失,因为备份交换机可以将消息存储在备份队列中,等待进一步处理。
- 高可用性和集群:配置RabbitMQ集群和高可用性设置可以确保即使某个节点宕机,消息仍然可以通过其他节点进行传递。这提供了容错性和可靠性。
- 死信队列:配置死信队列可以确保处理失败的消息不会被永久丢弃。失败的消息将被路由到死信队列,以便进一步分析和处理。
- 消息顺序:如果需要保持消息的顺序,可以使用单一队列来处理所有相关消息,或者使用多个队列,但要确保它们以确定的顺序绑定到交换机
数组(Array)和HashMap是两种不同的数据结构,它们在许多方面都有明显的区别:
数据结构:
- 数组是一种线性数据结构,它包含一组具有相同数据类型的元素,这些元素在内存中是连续存储的,并通过索引来访问。
- HashMap是一种集合类,用于存储键值对,它使用哈希表的数据结构,键(key)映射到值(value)。
访问方式:
- 数组的元素可以通过索引进行快速访问,索引通常是整数。
- HashMap的元素可以通过键(key)进行访问,而不是通过索引。要获得值,你需要知道键的值。
存储类型:
- 数组可以存储基本数据类型和对象,包括任何数据类型的元素。
- HashMap通常用于存储对象,要求键和值都可以是对象
抽象类个接口
- 定义:
- 抽象类是一种类,它可以包含抽象方法(有
abstract
关键字修饰的方法)以及普通的具体方法,同时可以包含字段和构造函数。抽象类的主要目的是为子类提供通用的实现。 - 接口是一种纯抽象的类,它只包含方法签名(没有方法体),常量(字段只能是
final
和static
的),默认方法(有默认实现的方法,Java 8引入的特性),以及静态方法。接口定义了一组行为,而不提供具体实现。
- 抽象类是一种类,它可以包含抽象方法(有
- 继承:
- 一个类可以继承自一个抽象类,使用
extends
关键字。子类继承抽象类时,必须实现抽象类中的所有抽象方法,除非子类自身也声明为抽象类。 - 一个类可以实现多个接口,使用
implements
关键字。类在实现接口时,必须提供接口中定义的所有方法的具体实现。
- 一个类可以继承自一个抽象类,使用
- 构造函数:
- 抽象类可以有构造函数,用于初始化对象状态。
- 接口不能包含构造函数,因为接口不能被实例化。
- 多重继承:
- Java不支持多重继承,即一个类不能同时继承多个类。但一个类可以实现多个接口,从而获得多重继承的效果。