面试稿·Java
- 一、Java基础
- 二、Spring·前后端分离架构
一、Java基础
1、Java数据结构
2、Java网络编程
2.1 TCP&UDP


2.2 Netty
2.2.1 Netty项目
2.2.1.1 Push(推) & Pull(拉)
Pull 模式的另外一个好处是 consumer 可以自主决定是否批量的从 broker 拉取数据。Push 模式必须在不知道下游 consumer 消费能力和消费策略的情况下决定是立即推送每条消息还是缓存之后批量推送。如果为了避免 consumer 崩溃而采用较低的推送速率, 将可能导致一次只推送较少的消息而造成浪费。Pull 模式下, consumer 就可以根据自己的消费能力去决定这些策略。
Pull 有个缺点是,如果 broker 没有可供消费的消息,将导致 consumer 不断在循环中轮询,直到新消息到 t 达。为了避免这点,Kafka 有个参数可以让 consumer 阻塞知道新消息到达(当然也可以阻塞知道消息的数量达到某个特定的量这样就可以批量发送)。
3、Java多线程
3.1 Java并发基础(锁)
3.1.1 多线程活跃性问题
- 死锁(一个山上的景区,只有一天路,一辆车要上山去景区、一辆车逛完了要下山离开景区,两车互不相让)
- 活锁(互相谦让)
- 饥饿锁(低优先级线程一直拿不到资源,无法正常工作)
3.1.2 并发级别
- 阻塞(synchronized和可重入锁)
- 无饥饿(公平锁)
- 无障碍(乐观锁)
- 无锁(原子类、Disruptor)
- 无等待()
3.1.3 同步控制
3.1.3.1 可重入锁
lock、unlock、tryLock(限时等待)
3.1.4 锁优化
3.1.4.1 偏向锁
-XX:+UseBiasedLocking
3.1.4.2 自旋锁
3.1.4.3 锁消除
-XX:+DoEscapeAnalysis
-XX:+EliminateLocks
3.2 JDK并发包(J.U.C)
3.2.1 线程复用:线程池
- FixedThreadPool(固定数量线程池)
- CachedThreadPool
- ScheduledThreadPool
- SingleThreadExecutor(只有一个线程的线程池)
- SingleThreadScheduledExecutor(只有一个线程且周期性定时执行某任务的线程池)
3.2.2 自定义线程池
public ThreadPoolExecutor(
int corePoolSize, //线程数量
int maximumPoolSize, //最大线程数量
long keepAliveTime, //空闲线程存活时间
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue, //任务队列
ThreadFactory threadFactory, //线程工厂
RejectedExecutionHandler handler //拒绝策略)
3.2.2.1 合理的线程池大小
- 合理的线程池大小:
- I/O密集型:CPU核数 *(1+I/O耗时/CPU耗时)amp工具
- CPU密集型:CPU核数 + 1
Runtime.getRuntime().availableProcessors()
3.2.3 并发容器
3.2.3.1 BlockingQueue
ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue
3.3 并行模式&算法
3.4 Java8并发(CompletableFuture)


3.5 使用建议

4、Java虚拟机
4.1 内存管理


- 虚拟机的分代回收,又分为新生代(Eden、S0、S1)和老年代。对新生代的回收为Minor GC,对老年代的回收为Major GC。
- Stop The World:清理时暂停其他工作线程。
4.2 引用
- 强引用:大部分场景都是强引用
- 软引用:缓存(内存不足即回收)
- 弱引用:缓存(发现即回收)
- 虚引用:对象的回收跟踪
4.3 GC详解
4.4 性能调优
- CPU(线程上下文切换、频繁GC,解决方案:使用线程池、减少线程数量及线程间的切换)
- 内存(合理设置各个代的大小、选择合适的GC策略)
- I/O(异步代替同步、尽量批量写入、减少IO次数)

5、Java8新特性
5.1 Java8接口新特性
在Java8中,可以为接口添加 静态方法 和 默认方法
- 实现类不能调用接口中的 静态方法、只能通过接口调用
- 实现类可以 调用和重写 接口中的 默认方法
- 实现类同时继承了一个有与接口同名同参的 默认方法 的父类、根据类优先原则、在子类没有重写该方法时、默认调用父类的方法
5.2 Java8注解新特性
在Java8中,注解方面新增了 可重复注解 和 类型注解
5.3 HashMap源码 JDK1.7 vs JDK1.8
底层结构有变化、JDK1.8中HashMap的底层结构是:数组+链表+红黑树
5.4 Java8其他新特性:函数式接口、方法引用
Lambda表达式、Stream API
二、Spring·前后端分离架构
1、Spring
1.1 Spring 编程模型
Spring FrameWork 一共涉及到5种编程模型、面向对象的编程模型(接口、对象、设计模式)、面向切面的编程模型(动态代理、字节码提升)、注解驱动的编程模型、事件驱动的编程模型、模块驱动的编程模型
1.2 Spring FrameWork 有哪些核心模块?
spring-core(资源管理、泛型处理)、spring-beans(依赖注入)、spring-context(注解驱动、事件驱动、模块驱动)、spring-webmvc(Servlet)、spring-aop(动态代理、字节码提升)
1.3 Spring IOC
1.3.1 什么是IOC
Spring管理Bean的容器
1.3.2 BeanFactory 和 FactoryBean 有什么区别
BeanFactory是一种Spring IOC的容器
FactoryBean是创建Bean的一个工厂方法
1.3.3 BeanFactory 和 ApplicationContext有什么区别
ApplicationContext是BeanFactory的一个子接口,在BeanFactory上进行了很多扩展、比如扩展了消息国际化(MessageSource)、环境可配置(EnvironmentCapable)、应用事件发布(ApplicationEventPublisher)和资源模式解析(ResourcePatternResolver)等
1.3.4 @Autowired 与 @Resource有什么区别
IOC容器中允许按照名字或类型获取Bean
@Autowired根据类型进行注入、@Resource根据名字注入
@Primary 和 @Quelifier 消除歧义 配合@Autowired使用
@Primary(优先)、@Quelifier(限定)有参数的构造方法装配
1.3.5 延迟加载的好处
没有在SpringIoC容器初始化时就执行了Bean的实例化和依赖注入。
1.3.6 Spring IOC 生命周期
读取和解析配置元信息(通过BeanDefinition可以注册)、启动Spring上下文、实例化Bean、依赖注入(初始化Bean)、(Bean生存期)关闭Spring上下文(销毁Bean)
BeanNameAware , BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean
1.4 Spring MVC
- ApplicationContext初始化时用Map保存所有URL和Controller类的对应关系。
- 根据请求URL找到对应的Controller,并从Controller中找到处理请求的方法。
- 将Request参数绑定到方法的形参上,执行方法处理请求,并返回结果视图。
- 主要有DispatcherServlet、视图解析器(ViewResolver)和处理适配器(HandlerAdapter)

- DispatcherServlet是前端控制器,主要负责接收Request并将Request转发给对应的处理组件。
- HandlerMapping主要完成URL到Controller映射的组件。
- Controller主要处理Request,并返回ModelAndView对象。
- ④、⑤、⑥是视图解析器解析ModelAndView对象并返回对应的视图给客户端的过程。
1.4.1 Controller
mv.setViewName("")

1.4.1.1 @RequestMapping
把请求的URL和控制器的方法相对应
1.4.1.2 JSR-303(Spring数据校验)

1.4.2 RESTful
1.4.3 View
spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp
1.5 Spring AOP
1.5.1 什么是AOP
面向切面编程(目标增强、约定大于配置)、主要用在日志、事务、安全等方面,底层原理是动态代理
1.5.2 JDKDynamicAopProxy
- 创建目标对象、
- 创建代理对象Proxy.newProxyInstance(),传三个参数,目标类加载器(.getClass().getClassLoader())、目标类接口数组(.getClass().getInterfaces())、处理器类(new InvocationHandler())
- 创建一个匿名内部类,或者类实现InvocationHandler接口,重写invoke方法,对调用的目标方法进行增强(invoke方法有三个参数,Object proxy, Method method, Object[] args)
- method指的是代理对象调用的方法、args是代理对象调用方法时传的参数
- 通过代理对象调用方法
1.5.3 AOP 日志
2、MyBatis
Hibernate是全映射框架,在以管理系统为主的时代,它的模型化十分有利于公司业务的分析和理解,但是在近年兴起的移动互联网时代,这样的模式却走到了尽头。Hibernate的模式重模型和业务分析,移动互联网虽然业务相对简单,但却更关注大数据和大并发下的性能问题。全表映射规则下的Hibernate已无法满足SQL优化和互联网灵活多变的业务。
2.1 MyBatis 类图

StatementHandler(数据库会话器)、ParameterHandler(参数处理器)、ResultHandler(结果处理器)
2.2 MyBatis-Spring
2.2.1 MyBatis 常用配置
#定义Mapper的XML路径
mybatis.mapper-locations=
#定义别名扫描的包,需要与@Alias联合使用
mybatis.type-aliases-package=
#MyBatis配置文件,当你的配置比较复杂的时候,可以使用它
mybatis.config-location=
#配置MyBaits插件(拦截器)
mybatis.configuration.interceptors=
#具体类需要与@MappedJdbcTypes联合使用
mybatis.type-handlers-package=
#级联延迟加载属性配置
mybatis.configuration.aggressive-lazy-loading=
#执行器(Executor〕,可以配置SIMPLE、REUSE、BATCH,默认为SIMPLE
mybatis.executor-type=
2.3 MyBatis-Plus
2.4 CRUD 项目
2.4.1 MyBatis注解方式(一般不介意使用)
2.4.2 MyBatis代码生成器
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.7</version>
</dependency>
generatorConfig.xml

2.4.3 MyBatis二级缓存


2.4.3.1 EhCache
ehcache.xml
<!--
缓存配置
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
-->
<Cache name="simpleCache" 缓存的名字
maxElementsInMemory="10000" 内存中最大的对象数
eternal="false" 对象是否永久存活
timeToIdleSeconds="120" 当缓存闲置n秒后销毁
timeToLiveSeconds="120" 当缓存存活n秒后销毁
overflowToDisk="true" 当大小超过memory的大小时,可以缓存到本地硬盘
maxElementsOnDisk="10000000" 硬盘上最大的对象数
diskPersistent="false" 是否在硬盘上持久化保存
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" 内存清除对象的方式
/>

2.5 Spring 事务
2.5.1 ACID
- Atomic(原子性):事务中包含的操作被看作一个整体的业务单元,这个业务单元中的操作要么全部成功,要么全部失败,不会出现部分失败、部分成功的场景。
- Consistency(一致性):事务在完成时,必须使所有的数据都保持一致状态,在数据库中所有的修改都基于事务,保证了数据的完整性。
- Isolation(隔离性):多个线程同时访问同一数据,这样数据就会在各个不同的事务中被访问,会产生丢失更新。为了解决这个问题,数据库定义了隔离级别的概念。
- Durability(持久性):事务结束后,所有的数据会固化到一个地方,如保存到磁盘当中,即使断电重启后也可以提供给应用程序访问。
2.5.2 隔离级别
- Spring事务管理器里的隔离级别有:数据库默认的隔离级别、未提交读、读写提交、可重复读和串行化五种。
- 其中,数据库默认的隔离级别,Oracle的默认级别是读写提交、MySql的默认级别是可重复读。
- 未提交读(READ_UNCOMMITTED)是最低的隔离级别,它的意思是允许一个事务读取另外一个事务没有提交的数据。
- 读写提交(READ_COMMITTED)隔离级别,是指一个事务只能读取另外一个事务已经提交的数据,不能读取未提交的数据。
- 可重复读(REPEATABLE_READ)是克服读写提交中出现的不可重复读的现象,因为在读写提交的时候,可能出一些值的变化,影响当前事务的执行。
- 串行化(SERIALIZABLE)按顺序执行、完全保证数据一致性。

- 脏读:一个事务对数据进行了增、删、改,但未提交,另一个事务可以读取到未提交的数据。如果第一个事务这时候回滚了,那么第二个事务就读到了脏数据。
- 不可重复读:一个事务中发生了两次读操作,在第一次读操作和第二次读操作之间,另外一个事务对数据进行了修改,这时候两次读取的数据是不一致的。
- 幻读:第一个事务对一定范围的数据进行了批量修改,第二个事务在这个范围内增加了一条数据,这时候第一个事务就会丢失对新增数据的修改。
2.5.3 SpringBoot 统一配置
- 事务只能运用在那些低井发而又需要保证数据一致性的场景下。对于高井发下又要保证数据一致性的场景,则需要另行处理了。
# 隔离级别数字配置的含义
#-1 据库默认隔离级别
# 1 未提交读
# 2 读写提交
# 4 可重复读
# 8 串行化
# tomcat数据源默认隔离级别
spring.datasource.tomcat.default-transaction-isolation=2
2.5.4 传播行为

3、Spring Data JPA
JPA是一种规范,是JSR-220规范的一部分。
3.1 JPA 类图
3.2 CRUD 项目
4、MyCat
5、Redis(分布式缓存共享)
5.1 高级功能
5.1.1 持久化
Redis 提供两种持久化机制 RDB 和 AOF 机制
| 持久化机制 | 含义 | 优点 | 缺点 |
|---|---|---|---|
| RDB(Redis DataBase) | 把数据写入一个临时文件, 持久化结束后, 用这个临时文件替换上次持久化的文件, 达到数据恢复。 | RDB模式 是让主进程继续处理命令,使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 Redis 的高性能 | RDB模式 是隔一段时间进行一次持久化,如果持久化之前 Redis 发生了故障, 会发生数据的丢失。所以这种方式更适合数据要求不严格的时候 |
| AOF(Append-Only File) | 把每次Redis命令追加写入日志文件中 | 跟 RDB模式 相比,当发生故障时,丢的少了 | AOF 文件比 RDB 文件大, 且恢复速度慢。 |
5.1.2 Redis的淘汰策略
| 策略 | 含义 |
|---|---|
| volatile-lru | 从已设置过期时间的数据集中挑选最近最少使用的数据淘汰 |
| volatile-ttl | 从已设置过期时间的数据集中挑选将要过期的数据淘汰 |
| volatile-random | 从已设置过期时间的数据集中随机选择数据进行淘汰 |
| allkeys-lru | 从所有数据集中挑选最近最少使用的数据淘汰 |
| allkeys-random | 从所有数据集中随机选择数据进行淘汰 |
| no-enviction | 永不回收策略 |
使用策略规则:
1、如果数据呈现幂律分布,也就是一部分数据访问频率高,一部分数据访问频率低, 则使用 allkeys-lru
2、如果数据呈现平等分布, 也就是所有的数据访问频率都相同, 则使用allkeys-random
5.2 多级缓存项目(EhCache、Redis)
5.2.1 SpringBoot 统一配置
# 配置连接池属性
spring.redis.jedis.pool.min-idle=5
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.max-wait=2000
# 配置Redis服务器属性
spring.redis.port=6379
spring.redis.host=192.168.11.131
spring.redis.password=123456
# Redis连接超时时间,单位毫秒
spring.redis.timeout=1000
# 日志配置为DEBUG级别,这样日志最为详细
logging.level.root=DEBUG
logging.level.org.springframework=DEBUG
logging.level.org.org.mybatis=DEBUG
#缓存配置
spring.cache.type=REDIS
spring.cache.cache-names=redisCache
5.2.2 主从、哨兵、集群
Redis Sentinal 着眼于高可用, 在 Master 宕机时会自动将 Slave 提升为Master, 继续提供服务。
Redis Cluster 着眼于扩展性, 在单个 redis 内存不足时, 使用 Cluster 进行分片存储。
Redis 集群有16384 个哈希槽,每个 key 通过 CRC16 校验后对 16384 取模来决定放置哪个槽, 集群的每个节点负责一部分 hash 槽。
5.2.3 Redis缓存
@EnableCaching //启动类上添加开启缓存注解
@Cacheable //需要缓存的方法上添加
@CacheEvict //Delete时也清除缓存
# 禁用前缀
spring.cache.redis.use-key-prefix=false
# 允许保存空值
#spring.cache.redis.cache-null-values=true
# 自定义前缀
#spring.cache.redis.key-prefix=
# 定义超时时间,单位毫秒
spring.cache.redis.time-to-live=600000

6、ElasticSearch
7、消息中间件 MQ
7.1 Rabbit MQ
7.1.1 使用场景
- 服务间异步通信(异步解耦)
- 分布式事务(消息发送重试、消息消费重试、死信监听告警)
- 定时任务(延时队列)
- 请求削峰(业务高峰期缓冲)
7.1.2 AMQP~Exchange
- Rabbit MQ从消息发布方到消息消费方,需要经过Exchange(分拨中心)和Queue(取件点)。
- Exchange根据绑定关系(Binding)和路由键routingKey(快递地址)转发消息到相应队列。

7.1.3 高级功能
7.1.3.1 发送方确认机制(confirm)
waitforconfirms
7.1.3.2 如何避免消息重复投递或重复消费
在消息生产时, MQ 内部针对每条生产者发送的消息生成一个 inner-msg-id, 作为去重的依据( 消息投递失败并重传), 避免重复的消息进入队列;
在消息消费时,要求消息体中必须要有一个 bizId( 对于同一业务全局唯一,如支付 ID、订单 ID、帖子 ID 等) 作为去重的依据, 避免同一条消息被重复消费。
7.1.3.3 失败通知机制(return)
- RabbitTemplate.ReturnCallback重写returnedMessage方法
7.1.3.3 消费者限流
spring.rabbitmq.listener.simple.prefetch = 1
7.1.3.3 TTL & 死信队列(DLX)
信息成为死信的原因:
- 消费者拒收,重写basicNack方法、并将消息不放回原队列
- 消息设置了超时时间,然后超过了超时时间,就到了死信队列
- 队列达到了最大长度
7.1.3.4 延迟队列
7.1.4 RabbitMQ 项目(可靠性传输)

7.1.4.1 SpringBoot 统一配置
- 消息生产者:
# RabbitMQ服务器地址
spring.rabbitmq.host=localhost
# RabbitMQ端口
spring.rabbitmq.port=5672
# RabbitMQ用户
spring.rabbitmq.usernarne=admin
#RabbitMQ密码
spring.rabbitmq.password=l23456
#是否确认发送的消息已经被消费
spring.rabbitmq.publisher-confirms=true
# RabbitMQ的消息队列名称,由它发送字符串
rabbitmq.queue.msg=spring-boot-queue-msg
# RabbitMQ的消息队列名称,由它发送用户对象
rabbitmq.queue.user=spring-boot-queue-user
- 消息消费者:
spring.rabbitmq.listener.direct.acknowledge-mode=manual
spring.rabbitmq.listener.direct.prefetch=100
@RabbitListener(queues = 队列名)
@RabbitHandler
public void receiveDeadMsg(String msg, Channel channel, Message messages) throws Exception {
try {
业务代码...
// 回发ack,第二个参数multiple,表示是否批量确认,具体请百度,不赘述
channel.basicAck(messages.getMessageProperties().getDeliveryTag(), false);
} catch (Exception e) {
// 回发Nack,且重回队列为false,防死循环
channel.basicNack(messages.getMessageProperties().getDeliveryTag(), false, false);
}
}
- https://blog.csdn.net/weixin_44367006/article/details/105404218(RabbitTemplate封装类)
- https://blog.csdn.net/qq_37892957/article/details/89296157(RabbitMQ延迟队列)
7.2 Kafka
7.2.1 讲讲Kafka 维护消费状态跟踪的方法(Kafka分区)
大部分消息系统在 broker 端的维护消息被消费的记录:也就是一个消息被分发到 consumer 后 broker 就马上进行标记或者等待 customer 的通知后进行标记。这样也可以在消息在消费后立马就删除以减少空间占用。
一条消息发送出去之后就立即被标记为消费过的, 一旦 consumer 处理消息时失败了( 比如程序崩溃) 消息就丢失了。
为了解决这个问题, 很多消息系统提供了另外一个个功能: 当消息被发送出去之后仅仅被标记为已发送状态, 当接到 consumer 已经消费成功的通知后才标记为已被消费的状态。
这虽然解决了消息丢失的问题,但产生了新问题,首先如果 consumer 处理消息成功了但是向 broker 发送响应时失败了,这条消息将被消费两次。第二个问题时, broker 必须维护每条消息的状态, 并且每次都要先锁住消息然后更改状态然后释放锁。这样麻烦又来了, 且不说要维护大量的状态数据, 比如如果消息发送出去但没有收到消费成功的通知, 这条消息将一直处于被锁定的状态,
Kafka 采用的策略就是分区。把 Topic 分成了若干分区,每个分区在同一时间只被一个 consumer 消费。即使用 offset 来标记每个分区消费状态。
8、Vue
9、Spring Security
spring.security.user.name=myuser
spring.security.user.password=l23456
9.1 OAuth2
10、Swagger

11、Spring5 WebFlux





8875

被折叠的 条评论
为什么被折叠?



