积累

final修饰的变量会指向一块固定的内存, 这块内存中的值不能改变.
存储过程

禁止使用存储过程,存储过程难以调试和扩展,更没有移植性

count()

count(*)会统计为NULL的行,count(列名)不会统计此列为NULL值的行
count(distinct col) 计算该列除NULL之外的不重复行数

当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,
避免NPE问题,select IFNULL(SUM(colum),0) from table;

泛型:是一种把明确类型的工作推迟到创建对象或调用方法的时候才去明确的特殊类型
多态

父类引用指向子类对象,调用方法时会调用子类的实现而不是父类的实现。多态的实现的关键在于“动态绑定”

transient

transient 关键字只能修饰变量,而不能修饰类,方法
被transient修饰的变量不能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化
一个变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问,也可以认为在将持久化的对象反序列化后,被transient修饰的变量将按照普通成员变量一样被初始化

素数 合数

指数又称为素数。一个大于1的自然数,除了1和它自身外,不能整除其他自然数的数叫做质数,2,3,5,7,11,13,17
合数:只自然数除了能被1和本身整除外,还能被其他数(0除外)整除的数

HashSet是基于HashMap来实现的,更像是对HashMap的封装
301 302

301 redirect : 永久性转移,搜索引擎在抓取新内容的同时也将旧网址交换为重定向之后的网址
302 redirect:暂时性转移,搜索引擎会抓取新内容保留旧网址

#{} ${}

{} 是经过预编译,${}未经过预编译,仅仅是取变量的值,是非安全的,存在SQL注入

并发 并行

并行:逻辑上同时发生,指在同一时间内同时运行多个程序
并发:物理上同时发生,指在同一时间点同时运行多个程序

守护线程:在没有用户线程服务时会自动离开,有用户线程,一直在
sleep() wait()

sleep() wait()

  1. Thread.sleep() Object.wait()
  2. sleep() 在任何地方使用,wait()方法只能在同步方法或同步块中使用
  3. sleep() 让出了cpu,并不会释放同步资源锁
    wait() 会释放锁,等待nofity()/notifyAll()唤醒指定的线程
线程池状态

running: 线程池一旦被创建,就出入running状态,任务数为0,能接受新任务,对已排队的任务进行处理
shutDown: 不接受新任务,但能处理已排队的任务。
stop: 不接受新任务,不处理已排队的任务,并会中断正在处理的任务。
tidying:
teminated: 线程池彻底终止。线程池在tidying状态执行完terminated()方法就会由tidying转变为terminated状态

避免死锁
  1. 尽量使用tryLock,设置超时时间
  2. 尽量使用concurrent 并发类代替自己手写锁
  3. 尽量降低锁的使用颗粒度,尽量不要几个功能用同一把锁
  4. 尽量减少同步代码块
ThreadLocal为每个使用该变量的线程提供独立的变量副本。
Synchronized

synchronized底层实现原理
jvm基于进入和退出monitor对象来实现方法同步和代码块同步

synchronized 是可重入的,所以不会自己把自己锁死
synchronized 锁一旦被一个线程持有,其他试图获取该锁的线程将被阻塞

乐观锁 悲观锁

悲观锁:
总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程

乐观锁:
总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间有没有去更新这个数据,可以使用版本号和CAS算法实现。
乐观锁适用于多读的应用类型,这样可以提供吞吐量

乐观锁:缺点
1.ABA
2.循环时间长开销大
自旋CAS(也就是不成功就一直循环执行直到成功),如果长时间不成功,会给CPU带来非常大的执行开销。

  1. 只能保证一个共享变量的原子操作
反射:就是通过class文件对象,去使用该文件中的成员变量、构造方法、成员方法。
序列化、反序列化

序列化:将java对象转换成字节流的过程
反序列化:将字节流转换成java对象的过程

当java对象需要在网络传输或者持久化存储到文件中时,就需要对java对象进行序列化处理

声明为static、transient的成员变量,不能被序列化

BIO NIO AIO

BIO(同步阻塞):客户端在请求数据的过程中,保持一个连接,不能做其他事情。
NIO(同步非阻塞):客户端在请求数据的过程中,不用保持一个连接,不能做其他事情。(不用保持一个连接,而是用许多个小连接,也就是轮询)
AIO(异步非阻塞):客户端在请求数据的过程中,不用保持一个连接,可以做其他事情。(客户端做其他事情,数据来了等服务端来通知。)

权限修饰符

private 、 默认 、protected 、public

本类、同一包下 、不同包下的子类、不同包下的无关类

Java克隆

相对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆,Java语言中克隆针对的是类的实例

浅拷贝:
浅拷贝只是复制了对象的引用地址,两个对象指向同一内存地址,所以修改其中任意的值,另一个值都会随着变化,这就是浅拷贝
深拷贝:
深拷贝是将对象及其值复制过来,两个对象修改其中任意的值,另一值不会改变,这就是深拷贝

实现对象克隆:

  1. 实现Cloneable接口并重写Object类中的clone()方法。
  2. 实现Serializable接口,通过对对象的序列化和反序列化实现克隆,可以实现真正的深度克隆
throw throws

throw 方式体中,后面跟异常对象,只能有一个,说明这里肯定有一个异常对象产生
throws 方式声明上,后面跟的是异常类名,可以是多个,说明这里可能会产生异常

TCP UDP

UDP 是一种无连接的协议
TCP 是一种面向连接的保证可靠的协议

区别:

  1. 把数据打包

  2. 数据有限制

  3. 不建立连接

  4. 速度快

  5. 不可靠

  6. 建立连接通道

  7. 数据无限制

  8. 速度慢

  9. 可靠

Class类是Java反射机制的起源和入口,用于获取与类相关的各种信息,提供了获取类信息的相关方法。
字符串拼接
  1. 直接使用+
  2. 使用String的concat方法
  3. StringBuilder . append
Servlet 中实现页面跳转两种方式

在Servlet中实现页面跳转的两种方式:转发和重定向

转发:由服务器端进行的页面跳转

  1. 地址栏不会发生改变
  2. 请求1次
  3. 请求域中的数据不会丢失

重定向:由浏览器进行的页面跳转

  1. 地址栏显示新的地址
  2. 请求2次
  3. 请求域中的数据会丢失
Session Cookie

session和cookie
1 . session在服务端,cookie在客户端

  1. session的运行依赖于sessionId,而sessionId是存在cookie中的,也就是说,如果浏览器禁用了cookie,同时session也会失效
    (但是可以通过其他方式实现,比如在url中传递sessionId)

session 原理
浏览器第一次访问服务器时,服务器会生成session,并把sessionId返回客户端,通过set-cookie
客户端收到sessionId会将它保存在cookie中,当客户端在此访问会带着这个sessionId

cookie : 由服务端生成,可以设置失效时间,默认关闭后失效 4kb
localStorage: 除非永久清楚,否则永久保存 5mb
sessionStorage: 仅在当前会话有效,关闭页面或者浏览器被清除 5mb

CSRF CORS

CSRF 是跨站请求伪造,Cross-site request forgery,

防范

  1. 检查Referer字段
  2. 添加效验token

CORS 跨域请求 Cross-orgin requests

XSS

XSS 是跨站脚本攻击 Cross Site Scripting,代码注入
恶意攻击者往Web页面里面插入恶意Script代码,当用户浏览该页的时候,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。

防范手段

  1. 首先是过滤,对诸如<script>、<img>、<a>等标签进行过滤
  2. 其次是编码,像一些常见的符号,如<>在输入的时候要对其进行转换编码,这样做浏览器是不对该标签进行解释执行的,同时也不影响显示效果
  3. 最后是限制。通过以上的案例我们不难发现xss攻击要能达成往往需要较长的字符串,因此对于一些可以预期的输入可以通过限制长度强制截断来进行防御。
final finally finalize

final 最终的意思,可以修饰类,成员变量,成员方法
修饰的类,类不能被继承
修饰的变量,变量是常量
修饰的方法,方法不能被重写
finally: 是异常的一部分,用于释放资源
一般来说,代码肯定会执行,特许情况:在执行finally之前jvm退出了
finalize: 是Object类的一个方法,用于垃圾回收

Spring框架,可以解决对象创建以及对象之间依赖关系的一种框架,且可以和其他框架一起使用。
常见异常

NullPointException、
ClassCastException、
ArrayIndexOutOfBoundsException 数组下标越界异常
FileNotFoundException
IOException
NoSuchMethodException
IndexOutOfBoudsException 下标越界异常
ArithmeticException 算数异常
ConcurrentModificationException
NoSuchElementException

Spring
  1. Spring 是开源的轻量级框架
  2. 一站式框架,内部支持对多种优秀开源框架
  3. Spirng 核心 主要有两部分:
    a. AOP : 面向切面编程,扩展功能不是修改源代码实现,动态代理
    b. IOC : 控制反转,创建对象不是通过new方式来实现,而是交给Spring配置来创建对象
枚举

有时候一个类的对象是有限且固定的,这种情况我们使用枚举类比较方便

枚举类更加直观,类型安全

代理模式

提供了对目标对象另外的访问方式,即通过代理对象访问目标对象,这样的好处可以在目标对象实现的基础上,扩展目标对象的功能

Sping 核心模块

SpringCore
SpringContext
SpringAOP
SpringDAO
SpringORM
SpringWeb
SpringWebMVC

Spring Cloud 是基于 Spring Boot 开发的一套微服务架构下的服务治理方案。
Mysql

SQL语句在MySQL的各个功能模块中的执行过程:

MySQL可以分为Server层和存储引擎层两部分

Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有内置函数(如日期、时间、数学和加密函数等),所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等

存储引擎负责数据存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory等多个存储引擎

不同的存储引擎公用一个Server层

更新流程涉及两个重要日志模块:redo log 和 binlog

InnoDB的redo log可以保证数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为crash-save

两个日志的区别:

  1. redo log 是InnoDB引擎特有的;binlog 是MySQL层实现的,所有引擎都可以使用
  2. redo log 是物理日志,记录的是“在某个数据页上做了什么修改”;binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如:“给ID=2 这一行的字段加1”
  3. redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。“追加写”是指binlog写到一定大小会切换到下一个,并不会覆盖以前的日志。

两阶段提交

Mysql 事务

MySQL 事务 事务就是要保证一组数据库操作,要么全部成功,要么全部失败。

事务特性:ACID(Atomicity、Consistency、Isolation、Durability,即原子性、一致性、隔离性、持久性)

当数据库上有多个事务同时执行的时候,就可能出现脏读(dirty read)、不可重复读(non-repeatable read)、幻读(phantom read)的问题,为了解决这些问题,就有了“隔离级别”的概念。

SQL 标准的事务隔离级别包括:读未提交(read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(serializable )。

Mysql 索引

索引的出现其实就是为了提高数据查询的效率,就像书的目录一样。

常见数据结构:哈希表、有序数组和搜索树
哈希表这种结构适用于只有等值查询的场景
有序数组在等值查询和范围查询场景中的性能就都非常优秀

InnoDB索引模型
在InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。
InnoDB 使用了B+树索引模型,所以数据都是存储在B+树中。

每一个索引在 InnoDB 里面对应一棵 B+ 树。

主键索引的叶子节点存的是整行数据。在 InnoDB 里,主键索引也被称为聚簇索引(clustered index)。

基于非主键索引的查询需要多扫描一棵索引树。因此,我们在应用中应该尽量使用主键查询。

主键长度越小,普通索引的叶子节点就越小,普通索引占用的空间也就越小

B+ 树能够很好地配合磁盘的读写特性,减少单次查询的磁盘访问次数。

非主键索引的叶子节点内容是主键的值。在 InnoDB 里,非主键索引也被称为二级索引(secondary index)。

回到主键索引树搜索的过程,我们称为回表

覆盖索引
由于覆盖索引可以减少树的搜索次数,显著提升查询性能,所以使用覆盖索引是一个常用的性能优化手段。

最左前缀原则
B+ 树这种索引结构,可以利用索引的“最左前缀”,来定位记录。

索引下推

串行化

串行化:对于同一行记录,写会加写锁,读会加读锁。当出现读写锁冲突的时候,后访问的事务必须等待前一个事务执行完成,才能继续执行。

SpingAop

切面(Aspect):由切入点和通知组成
目标对象(Target):要被增强的对象
连接点(JonitPoint):被拦截到的程序执行点,Spring只支持方法类型连接点
切入点(PointCut):对连接点进行拦截的条件定义
通知(Advice): 拦截到连接点要执行的代码,包括了“aroud”,“before”,"after"等不同类型的通知
织入(Weaving): 将切面和业务逻辑对象连接起来,并创建通知代理的过程。

整个Aspect 可以描述为:满足pointcut规则的jointpoint会被添加相应的advice操作

代理:静态代理 动态代理(JDK动态代理和CGlib动态代理)

JDK动态代理,被代理类必须实现一个接口

Mysql 读写分离

MySQL 读写分离原理:server层binlog日志,从库IO线程,rely中继日志,SQL线程

MySQL 两个机制:半同步复制,用来解决主库数据丢失,
并行复制,用来解决主从同步延时问题

解决主从同步延时问题:

  1. 分库,讲一个主库拆分为多个主库,每个主库的写并发就减少了几倍,此时主从延迟可以忽略不计
  2. 开启并行复制
  3. 重写代码,要慎重,插入数据时立马查询可能查不到
ThreadLocal为每个使用该变量的线程提供独立的变量副本。
Spring 配置bean 的几种方式
  1. xml文件显示配置
    在xml文件的<beans/>标签下的<bean>标签中申明需要Spring管理的bean。
  2. Java配置类显示配置
    使用@Configuration标注配置类,@Bean标注bean产生的方法。
  3. 自动扫描及自动配置
    需要Spring管理的bean上标注@Component注解,并启用Spring的组件自动扫描。
Spring 实现事务的几种方式
  1. 编程式事务管理 PlateformTransactionManager
  2. 声明式事务管理 @Transactional
  3. Aspecttj AOP 配置事务

@RequestMapping 用来处理请求地址映射的注解

value,method,consumes,produces,params,headers\

Hystrix 服务容错

服务降级
异常处理
命名与分组
Hystrix缓存
请求合并

数据库三范式:
  1. 数据库表的每一列都是不可分割的原子项
  2. 每个表必须有且仅有一个数据元素为主键,其他属性完全依赖属性
  3. 数据库表的每一列都和主键直接相关,而不能间接相关
MySQL char varchar 区别 定长与变长
索引

在MySQL中,索引属于存储引擎级别的概念,不同存储引擎对索引的实现方式不同。

MyISAM引擎使用B+树作为索引结构,叶子节点的data域存在的是数据记录的地址。索引文件与数据文件分离

虽然InnoDB也使用B+树作为索引结构,但具体实现方式却与MyISAM截然不同。

在InnoDB中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引索引组织表
主键索引的叶子节点存的整行数据。
非主键索引的叶子节点内容是主键的值。

聚簇索引与非聚簇索引

OAuth是一种用来规范令牌(Token)发放的授权机制。
Redis

Redis 是一个基于内存的高性能key-value非关系型数据库

Redis有哪些好处:

  1. 速度快,因为数据在内存中
  2. 支持丰富数据类型,支持String、list、set、sorted set、hash
  3. 支持事务,操作都是原子性
  4. 丰富的特性:可用于缓存,消息,按key设置过期时间,过期后将会自动删除

Redis为什么这么快?

  1. 完全基于内存
  2. 数据结构简单
  3. 采用单线程
  4. 使用多路I/O复用模型,非阻塞IO
  5. 使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM机制,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求。

Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器(file event handler)。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。
redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。

Redis的持久化机制是什么?
RDB(默认)和AOF机制

RDB: 是Redis DataBase缩写快照
是Redis默认的持久化方式。按照一定的时间将内存的数据以快照的形式保存到硬盘中,对应产生的数据文件为dump.rdb

ADO持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。

Docker

Dock是一个用于开发、部署和运行应用的开源平台,意在是你能更快地进行应用分发。

  1. 更快速的交付和部署
  2. 更高效的虚拟化
  3. 更轻松的迁移和扩展
  4. 更简单的管理
SELECT NAME 
FROM
    student 
GROUP BY
NAME 
HAVING
    min( score ) > 80

update salary set sex = if(sex='f','m','f')

SUBSTRING_INDEX(GROUP_CONCAT(stuName ORDER BY score ASC),',',1)

DATEDIFF(date,date)

delect a 
FROM
    person a,
    person b 
WHERE
    a.email = b.email 
    AND a.id < b.id

SELECT
    `NAME`,
    format( avg( score ), 2 ) AS avgscore 
FROM
    grade_table 
GROUP BY
NAME 
HAVING
    sum( score < 60 ) >= 2;
docker

docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root mysql:5.7.25

Dock是一个用于开发、部署和运行应用的开源平台,意在是你能更快地进行应用分发。

  1. 更快速的交付和部署
  2. 更高效的虚拟化
  3. 更轻松的迁移和扩展
  4. 更简单的管理
SpringSecurity

SecurityContextHolder 用于存储应用程序的安全上下文
Authentication 认证信息
AuthenticationManager 认证管理器
DoAuthenticationProvider

UserDetails 代表最详细的用户信息与UserDetailsService 加载用户信息

UsernamePasswordAuthenticationFilter
usernamePasswordAuthenticationToken
AuthenticationManager
DoAuthenticationProvider
UserDetailsService
UserDetails

Redis的过期键的删除策略
  1. 定时过期
  2. 惰性过期
  3. 定期过期

Redis Key 的过期时间和永久有效分别怎么设置?
expire和persist命令

redis内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略

创建索引

alter table <表名> add index index_name('column')
主键索引
alter table 'table_name' add primary key('column')
唯一索引
alter table 'table_name' add unque('column')
全文索引
alter table 'table_name' add fulltext('column')

缓解雪崩
  1. redis 集群
  2. 数据不设置相同的生存时间,不然同时过期,数据库压力过大
Mybatis缓存
  • 一级缓存:
    默认开启sqlSession级别的缓存,会将我们第一次查询的数据放入sqlSession中,在第二次查询时直接从sqlSession中获取。当sqlSession关闭缓存清空,执行增、删、改操作也会使一级缓存失效
  • 二级缓存:
    需要手动开启,mapper级别的缓存,sqlsession之间的资源共享
内存溢出(OOM)/内存泄漏(Memory Leak)

内存溢出(Out of memory-也就是常说的OOM): 通俗理解就是内存不够

内存泄漏(Memory Leak): 指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢设置系统崩溃等严重后果

栈溢出(StackOverFlow): 就是栈空间无法分配多余的空间来使用

内存溢出(OOM)原因:

  1. 一次性分配大量数据空间,而导致内存空间不足,比如从数据库一次性查询大量数据
  2. JVM中启动参数设置过小
  3. 集合类中的对象使用完成后未被回收,导致对象内存泄漏而产生内存溢出
  4. 使用第三方软件包可能会出现不熟悉的BUG等
  5. 方法递归,死循环,从而导致栈内存溢出

如何避免内存溢出?

  1. 在JVM启动前,预估好内存分配空间大小设置
  2. 检查循环是否有重复产生大量新对象实体
  3. 检查List Map等集合框架是否有引用的对象在使用,并没有被回收
  4. 检查代码中是否有递归调用或死循环操作
  5. 尽早释放无用对象的引用
索引规则
  1. 覆盖索引
    覆盖索引可以减少树的搜索次数,显著提升查询性能

身份证号和姓名,不再需要回表查询整行记录,减少语句的执行时间

  1. 最左前缀原则
    建立联合索引,如何安排索引内的字段顺序
    第一原则:通过调整顺序,可以少维护一个索引,那么这个顺序往往就是需要优先考虑的
    第二原则:考虑空间问题

  2. 索引下推优化

自定义索引

InnoDB会把主键字段放到索引定义字段后面

行锁

MySQL的行锁是在引擎层由各个引擎自己实现的。MyISAM引擎不支持行锁。

行锁就是针对数据表中行记录的锁。

在 InnoDB 事务中,行锁是在需要的时候才加上的,但并不是不需要了就立刻释放,而是要等到事务结束时才释放。这个就是两阶段锁协议。

如果你的事务中需要锁住多个行,要把最可能造成锁冲突、最可能影响并发度的锁尽量往后放。

Nginx

Nginx 是一个轻量级/高性能的方向代理Web服务器

正向代理和反向代理

优点:

  1. 占内存小,可实现高并发连接,处理相应快
  2. 可实现http服务器、虚拟主机、方向代理、负载均衡
  3. Nginx配置简单
  4. 可以不暴露正式的服务器IP地址

Nginx 负载均衡策略

  1. 轮询
  2. 权重 weight
  3. ip_hash(ip绑定)

限流

  1. 正常限制访问频率
  2. 突发限制访问频率
  3. 限制并发连接数
Happens-Before规则

导致可见性的原因是缓存,导致有序性的原因是编译优化,
那解决可见性、有序性最直接的办法就是禁用缓存和编译优化
合理的方案应该是按需禁用缓存和编译优化

Happens-Before规则,前面一个操作的结果对后续操作是可见的

  1. 程序的顺序性规则
  2. volatile变量规则
  3. 传递性
  4. 管程中锁的规则
  5. 线程start()规则
  6. 线程join()规则
Java内存模型
  1. Java内存模型规定所有的变量都是存在主存当中,每个线程都有自己的工作内存。
  2. 线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。
  3. 并且每个线程不能访问其他线程的工作内存。
SpringBootActuator

SpringBootActuator 可以帮助你监控和管理SpringBoot应用,比如健康检查、审计、统计和HTTP追踪等。

所有的这些特征可以通过JMX或者HTTP endpoints来获得

List

lists.newArrayList(...)
Arrrays.asList(...)

从输入url到浏览器显示页面发生了什么?
  1. DNS解析
  2. TCP建立连接
  3. 发送HTTP请求
  4. 服务器处理请求
    如果有缓存直接读取缓存
    没有缓存返回响应内容
  5. TCP断开连接
  6. 浏览器解析渲染页面
TCP/IP参考模型
  1. 应用层(HTTP FTP TFTP SMTP SNMP DNS)
  2. 传输层(TCP UDP)
  3. 网络层(ICMP TGMP IP ARP RARP)
  4. 数据链路层
  5. 物理层

HTTP协议是建立在TCP之上,属于应用层协议。

元注解
  1. @Document是一个标记注解,没有成员变量
  2. @Target 注解用来指定一个注解的使用范围
  3. @Retention 用于描述注解的生命周期
  4. @Inherited 是一个标记注解,用来指定该注解可以被继承

  1. @Repeatable
  2. @Native
原子性

原子性的本质是什么?其实不是不可分割,不可分割只是外在表现,其本质是多个资源间有一致性的要求, 操作的中间状态对外不可见。

常量

常量

  1. 字面值常量
    1. 字符串常量 "Hello"
    2. 整数常量 12,23
    3. 小数常量 12.345
    4. 字符常量 'a','b','c'
    5. 布尔常量 true,false
    6. 空常量null
  2. 自定义常量
面试题

String a = "123";
String b = "123";
System.out.println(a == b); true

栈指向常量池
先看常量池中有没有要创建的数据,有就返回数据的地址,没有就创建一个

String a = new String("234");
String b = new String("234");
System.out.println(a == b);false

原因:Java虚拟机的解释器每遇到一个new关键字,都会在堆内存中开辟一块内存来存放一个String对象,所以a,b指向的堆内存虽然存储的都是“234”,但是由于两块不同的堆内存,因此 a==b 返回的仍然是false。

死锁

死锁:一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象。

日志

日志实现:Log4j Logback
日志接口:SLF4J

多态

父类引用指向子类对象,调用方法时会调用子类的实现而不是父类的实现。多态的实现的关键在于“动态绑定”。

mysqldump -h%s -p%s -u%s -p%s --database %s > %
泛型:是一种把明确类型的工作推迟到创建对象或调用方法的时候才去明确的特殊类型
list.toArray(new string[0])
docker logs -f -t --tail=300 operation-backend
SQL NULL

当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,
避免NPE问题,select IFNULL(SUM(colum),0) from table;

count(distinct col) 计算该列除NULL之外的不重复行数
count(*)会统计为NULL的行,count(列名)不会统计此列为NULL值的行
禁止使用存储过程,存储过程难以调试和扩展,更没有移植性
select * from sys_user where !ISNULL(name)
count(*) 是SQL92定义的标准统计行数的语法
break;return;continue;
  • break
    1. 结束当前整个循环,执行当前循环下边的语句
    2. 只能跳出一层循环,如果你的循环是嵌套循环,那么你需要按照你嵌套的层次,逐步使用break来跳出。[逐层判断,逐步跳出]
    3. 当break出现在循环体中的switch语句体内时,其作用只是跳出该switch语句体。
  • continue:
  • continue 结束当前循环,进行下一次的循环判断
  • return 从当前的方法中退出,返回到该调用的方法的语句处(中断函数执行),继续执行
String s = new String("hello")和String s = "hello"的区别?

前者创建两个对象,后者创建一个对象

分配存储SingletonTest对象的内存空间;
初始化SingletonTest对象;
将instance指向刚刚分配的内存空间。


image.png
final 修饰的变量会指向一块固定的内存,这块内存中的值不能改变
IntStream.range(0,500)
String 常用方法

length()、isEmpty()、charAt(int index)、getBytes()、startsWith(String prefix)、endsWith(String suffix)
indexOf(int ch)、lastIndexOf(int ch)、indexOf(String str)、lastIndexOf(String str)、
subString(int beginIndex, int endIndex)、concat(String str)、replace(char oldChar, char newChar)、
match(String regex)、 contains(CharSequence s)
split(String regex)、trim()、toCharArray()、valueOf(...)

抽象类与接口
  1. 成员区别
    • 抽象类:可以是常量也可以是变量
    • 接口:只可以是常量(public static final)
  2. 关系
    • 类与类:继承,单继承
    • 类与接口:实现,单实现,多实现
    • 接口与接口: 继承,单继承,多继承
  3. 设计理念
    • 抽象类: is a ,抽象类中定义的是共性功能
    • 接口: like a ,接口中定义的是扩展功能
Collection 与 Collections
  • 集合框架父接口
  • 包装类,工具类,静态方法
字符串反转
  1. 递归
    public static String reverse01(String originalStr) {
        if (originalStr != null && originalStr.length() <= 1) {
            return originalStr;
        }

        return reverse01(originalStr.substring(1)) + originalStr.charAt(0);
    }
  1. StringBuilder.reverse
    public static String reverse02(String originalStr) {
        return new StringBuilder(originalStr).reverse().toString();
    }
  1. 遍历倒序
    public static String reverse03(String originalStr) {
        StringBuilder result = new StringBuilder();

        for (int i = originalStr.length() - 1; i >= 0; i--) {
            result.append(originalStr.charAt(i));
        }
        return result.toString();
    }
transient
  • transient 关键字只能修饰变量,不能修饰类、方法
  • 被transient修饰的变量不能被序列化
    一个静态变量不管是否被transient修饰,均不能被序列化
    一个变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问,也可以认为在将持久化的对象反序列化后,被transient修饰的变量将按照普通成员变量一样被初始化
质数 合数
  • 指数又称为素数。一个大于1的自然数,除了1和它自身外,不能整除其他自然数的数叫做质数,2,3,5,7,11,13,17
  • 合数:只自然数除了能被1和本身整除外,还能被其他数(0除外)整除的数
HashSet原理

HashSet是基于HashMap来实现的,更像是对HashMap的封装

Iterator ListIterator

Iterator : hashNext()、next()、remove()
ListIterator:hashNext()、 next()、remove()、 hashPrevious()、 previous()、 nextIndex()、previousIndex()、set()、 add()、

线程安全类

HashTable
Vector
ConcurrentHashMap
Stack

数组和List之间的转换
  • Arrays.asList(T... a)
  • List.toArray()
Queue remove() 和 poll()

Queue中remove()和poll() 都是用来从队列头部删除一个元素
当队列元素为空的情况下,remove()方法会抛出NoSuchElementException异常,poll只会返回null

不可修改的map

Collections.unmodifiableMap()

301 302
  • 301 redirect : 永久性转移,搜索引擎在抓取新内容的同时也将旧网址交换为重定向之后的网址
  • 302 redirect:暂时性转移,搜索引擎会抓取新内容保留旧网址
#{} ${}
  • `#{} 是经过预编译
  • ${} 未经过预编译,仅仅是取变量的值,是非安全的,存在SQL注入
防止Sql 注入
  1. PreparedStatement
  2. 正则表达式过滤传入的参数
  3. 增加权限
  4. 利用工具SQL注入监测
并发 并行
  • 并行:逻辑上同时发生,指在同一时间内同时运行多个程序
  • 并发:物理上同时发生,指在同一时间点同时运行多个程序
进程与线程

线程依赖于进程而存在
进程是系统进行资源分配和调用的独立单位。每个进程都有自己独立的内存空间和系统资源。
线程是程序的执行单元,执行路径。是程序使用CPU的最基本单位。

守护线程

在没有用户线程服务时会自动离开,有用户线程,一直在

sleep() wait()
  1. Thread.sleep() Object.wait()
  2. sleep() 在任何地方使用,wait()方法只能在同步方法或同步块中使用
  3. sleep() 让出了cpu,并不会释放同步资源锁
    wait() 会释放锁,等待nofity()/notifyAll()唤醒指定的线程
JAVA内存模型

Java内存模型规定所有的变量都是存在主存当中,每个线程都有自己的工作内存。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。

线程池状态
  • running: 线程池一旦被创建,就出入running状态,任务数为0,能接受新任务,对已排队的任务进行处理
  • shutDown: 不接受新任务,但能处理已排队的任务。
  • stop: 不接受新任务,不处理已排队的任务,并会中断正在处理的任务。
  • tidying: 所有任务都被终止, worCount == 0的时候
  • teminated: 线程池彻底终止。线程池在tidying状态执行完terminated()方法就会由tidying转变为terminated状态
synchronized 锁 升级原理

无锁、偏向锁、轻量级锁、重量级锁

避免死锁
  1. 尽量使用tryLock,设置超时时间
  2. 尽量使用concurrent 并发类代替自己手写锁
  3. 尽量降低锁的使用颗粒度,尽量不要几个功能用同一把锁
  4. 尽量减少同步代码块
ThreadLocal

ThreadLocal为每个使用该变量的线程提供独立的变量副本

Synchronized底层实现原理

jvm基于进入和退出monitor对象来实现方法同步和代码块同步

synchronized 是可重入的,所以不会自己把自己锁死
synchronized 锁一旦被一个线程持有,其他试图获取该锁的线程将被阻塞

Atomic 类

Atomic 主要利用CAS(Compare And Wap) 和volatile 和 native方法保证原子操作,从而避免synchronized的高开销,执行效率大为提升

悲观锁和乐观锁:
  • 悲观锁:
    总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁
    共享资源每次只给一个线程使用,其他线程阻塞,用完后再把资源转让给其他线程

  • 乐观锁:
    总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间有没有去更新这个数据,可以使用版本号和CAS算法实现。
    乐观锁适用于多读的应用类型,这样可以提高吞吐量

乐观锁:缺点

  1. ABA
  2. 循环时间长开销大
    自旋CAS(也就是不成功就一直循环执行直到成功),如果长时间不成功,会给CPU带来非常大的执行开销。
  3. 只能保证一个共享变量的原子操作
反射

反射:就是通过class文件对象,去使用该文件中的成员变量、构造方法、成员方法。

序列化和反序列化

序列化:将Java对象转换成字节流的过程
反序列化:将字节流对象转化成Java对象的过程

当java对象需要在网络传输或者持久化存储到文件中时,就需要对java对象进行序列化处理

声明为static、transient的成员变量,不能被序列化

BIO NIO AIO
  • BIO(同步阻塞):客户端在请求数据的过程中,保持一个连接,不能做其他事情。
  • NIO(同步非阻塞):客户端在请求数据的过程中,不用保持一个连接,不能做其他事情。(不用保持一个连接,而是用许多个小连接,也就是轮询)
  • AIO(异步非阻塞):客户端在请求数据的过程中,不用保持一个连接,可以做其他事情。(客户端做其他事情,数据来了等服务端来通知。)
权限修饰符
权限修饰符 本类 同一包下 不同包下的子类 不同包下的无关类
private Y
默认 Y Y
protected Y Y Y
public Y Y Y Y
克隆
  • 为什么
    相对一个对象进行处理,又想保留原有的数据进行接下来的操作,就需要克隆,Java语言中克隆针对的是类的实例
  • 实现克隆的方法
    1. 实现Cloneable接口并重写Object类中的clone()方法。
    2. 实现Serializable接口,通过对对象的序列化和反序列化实现克隆,可以实现真正的深度克隆。
  • 浅拷贝与深拷贝
    1. 浅拷贝只是复制了对象的引用地址,两个对象指向同一内存地址,所以修改其中任意的值,另一个值都会随着变化,这就是浅拷贝
    2. 深拷贝是将对象及其值复制过来,两个对象修改其中任意的值,另一值不会改变,这就是深拷贝
throw throws
  • throw 方式体中,后面跟异常对象,只能有一个,说明这里肯定有一个异常对象产生
  • throws 方式声明上,后面跟的是异常类名,可以是多个,说明这里可能会产生异常
TCP UDP

UDP 是一种无连接的协议
TCP 是一种面向连接的保证可靠的协议

区别:

  1. 把数据打包

  2. 数据有限制

  3. 不建立连接

  4. 速度快

  5. 不可靠

  6. 建立连接通道

  7. 数据无限制

  8. 速度慢

  9. 可靠

Class类作用

Class类是Java反射机制的起源和入口,用于获取与类相关的各种信息,提供了获取类信息的相关方法。

字符串拼接
  1. 直接使用+
  2. 使用String的concat方法
  3. StringBuilder . append
在Servlet实现页面跳转的两种方式:转发和重定向
  • 转发:由服务端进行的页面跳转
    1. 地址栏不会发生改变
    2. 请求1次
    3. 请求域中的数据不会丢失
  • 重定向:由浏览器进行的页面跳转
    1. 地址栏显示新的地址
    2. 请求2次
    3. 请求域中的数据会丢失
Session Cookie
  1. session在服务端,cookie在客户端
  2. session的运行依赖于sessionId,而sessionId是存在cookie中的,也就是说,如果浏览器禁用了cookie,同时session也会失效
    (但是可以通过其他方式实现,比如在url中传递sessionId)

session 原理
浏览器第一次访问服务器时,服务器会生成session,并把sessionId返回客户端,通过set-cookie
客户端收到sessionId会将它保存在cookie中,当客户端在此访问会带着这个sessionId

cookie : 由服务端生成,可以设置失效时间,默认关闭后失效 4kb
localStorage: 除非永久清楚,否则永久保存 5mb
sessionStorage: 仅在当前会话有效,关闭页面或者浏览器被清除 5mb

CORS CSRF XSS
  • CORS 是一个W3C标准,全程是“跨域资源共享”,Cross-origin resource sharing

  • CSRF 是跨站请求伪造,Cross-site request forgery,
    防范
    * 1. 检查Referer字段
    * 2. 添加效验token

  • XSS 是跨站脚本攻击 Cross Site Scripting,代码注入
    恶意攻击者往Web页面里面插入恶意Script代码,当用户浏览该页的时候,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。

防范手段

  1. 首先是过滤,对诸如<script>、<img>、<a>等标签进行过滤
  2. 其次是编码,像一些常见的符号,如<>在输入的时候要对其进行转换编码,这样做浏览器是不对该标签进行解释执行的,同时也不影响显示效果
  3. 最后是限制。通过以上的案例我们不难发现xss攻击要能达成往往需要较长的字符串,因此对于一些可以预期的输入可以通过限制长度强制截断来进行防御。
final finally finalize
  • final 最终的意思,可以修饰类,成员变量,成员方法
    修饰的类,类不能被继承
    修饰的变量,变量是常量
    修饰的方法,方法不能被重写
  • finally: 是异常的一部分,用于释放资源
    一般来说,代码肯定会执行,特许情况:在执行finally之前jvm退出了
  • finalize: 是Object类的一个方法,用于垃圾回收
什么是Spring框架

Spring框架,可以解决对象创建以及对象之间依赖关系的一种框架,且可以和其他框架一起使用。

为什么使用 Spring
  1. Spring 是开源的轻量级框架
  2. 一站式框架,内部支持对多种优秀开源框架集成
  3. Spirng 核心 主要有两部分:
    a. AOP : 面向切面编程,扩展功能不是修改源代码实现,动态代理
    b. IOC : 控制反转,创建对象不是通过new方式来实现,而是交给Spring配置来创建对象
异常类

NullPointException、
ClassCastException、
ArrayIndexOutOfBoundsException 数组下标越界异常
FileNotFoundException
IOException
NoSuchMethodException
IndexOutOfBoudsException 下标越界异常
ArithmeticException 算数异常
ConcurrentModificationException
NoSuchElementException

枚举

有时候一个类的对象是有限且固定的,这种情况我们使用枚举类比较方便

枚举类更加直观,类型安全

代理模式

代理模式:提供了对目标对象另外的访问方式,即通过代理对象访问目标对象,这样的好处可以在目标对象实现的基础上,扩展目标对象的功能

JAVA 容器集合分类
image.png
  • Java中的容器集合分为两大阵营,一个是Collection,一个是Map
  • Map HashMap TreeMap ConcurrentHashMap
  • Collection: List , Set Queue(Queue LinkedList,ArrayBlockingQueue)
    • List 有序,可重复
    • ArrayList
      底层数据结构是数组,查询快,增删慢
      线程不安全,效率高
    • Vector
    • LinkList
      底层数据结构是链表,查询慢,增删快
      线程不安全,效率高
  • Set 无序,唯一
    • HashSet
    • LinkedHashSet
      底层数据结构是链表和哈希表
      由链表保证元素有序
      由哈希表保证元素唯一
    • TreeSet
      底层数据结构是红黑树
      如果保证元素排序呢?
      自然排序
      比较器排序
      如何保证元素唯一性?
      根据比较的返回值是否是0来决定
请谈一谈Java集合中的fail-fast和fail-safe机制

fail-fast是一种错误检测机制,Java在适合单线程使用的集合容器中很好地实现了fail-fast机制,举一个简单的例子:在多线程并发环境下,A线程在通过迭代器遍历一个ArrayList集合,B线程同时对该集合进行增删元素操作,这个时候线程A就会抛出并发修改异常,中断正常执行的逻辑。

而fail-safe机制更像是一种对fail-fast机制的补充,它被广泛地实现在各种并发容器集合中。回头看上面的例子,如果线程A遍历的不是一个ArrayList,而是一个CopyOnWriteArrayList,则符合fail-safe机制,线程B可以同时对该集合的元素进行增删操作,线程A不会抛出任何异常。

要理解这两种机制的表象,我们得了解这两种机制背后的实现原理:

我们同样用ArrayList解释fail-fast背后的原理:首先ArrayList自身会维护一个modCount变量,每当进行增删元素等操作时,modCount变量都会进行自增。当使用迭代器遍历ArrayList时,迭代器会新维护一个初始值等于modCount的expectedModCount变量,每次获取下一个元素的时候都会去检查expectModCount和modCount是否相等。在上面举的例子中,由于B线程增删元素会导致modCount自增,当A线程遍历元素时就会发现两个变量不等,从而抛出异常。

CopyOnWriteArrayList所实现的fail-safe在上述情况下没有抛出异常,它的原理是:当使用迭代器遍历集合时,会基于原数组拷贝出一个新的数组(ArrayList的底层是数组),后续的遍历行为在新数组上进行。所以线程B同时进行增删操作不会影响到线程A的遍历行为。

如何一边遍历一边删除Collection中的元素?

使用集合迭代器自身的remove方法进行删除

Iterator<Integer> it = list.iterator();
while(it.hasNext()){
   *// do something*
   it.remove();
}
谈谈ArrayList和LinkedList的区别

本质的区别来源于两者的底层实现:ArrayList的底层是数组,LinkedList的底层是双向链表。

数组拥有O(1)的查询效率,可以通过下标直接定位元素;链表在查询元素的时候只能通过遍历的方式查询,效率比数组低。

数组增删元素的效率比较低,通常要伴随拷贝数组的操作;链表增删元素的效率很高,只需要调整对应位置的指针即可。

以上是数组和链表的通俗对比,在日常的使用中,两者都能很好地在自己的适用场景发挥作用。

比如说我们常常用ArrayList代替数组,因为封装了许多易用的api,而且它内部实现了自动扩容机制,由于它内部维护了一个当前容量的指针size,直接往ArrayList中添加元素的时间复杂度是O(1)的,使用非常方便。

而LinkedList常常被用作Queue队列的实现类,由于底层是双向链表,能够轻松地提供先入先出的操作。

我觉得可以分两部分答,一个是数组与链表底层实现的不同,另一个是答ArrayList和LinkedList的实现细节。

谈谈ArrayList和Vector的区别

两者的底层实现相似,关键的不同在于Vector的对外提供操作的方法都是用synchronized修饰的,也就是说Vector在并发环境下是线程安全的,而ArrayList在并发环境下可能会出现线程安全问题。

由于Vector的方法都是同步方法,执行起来会在同步上消耗一定的性能,所以在单线程环境下,Vector的性能是不如ArrayList的

除了线程安全这点本质区别外,还有一个实现上的小细节区别:ArrayList每次扩容的大小为原来的1.5倍;Vector可以指定扩容的大小,默认是原来大小的两倍。

感觉可以顺带谈谈多线程环境下ArrayList的替代品,比如CopyOnWriteArrayList,但是要谈谈优缺点。

为什么ArrayList的elementData数组要加上transient修饰

由于ArrayList有自动扩容机制,所以ArrayList的elementData数组大小往往比现有的元素数量大,如果不加transient直接序列化的话会把数组中空余的位置也序列化了,浪费不少的空间。

ArrayList中重写了序列化和反序列化对应的writeObject和readObject方法,在遍历数组元素时,以size作为结束标志,只序列化ArrayList中已经存在的元素。

请介绍一下HashMap的实现原理
  1. 我们一般用HashMap存储key-value类型的数据,它的底层是一个数组,当我们调用put方法的时候,首先会对key进行计算得出一个hash值,然后根据hash值计算出存放在数组上的位置
  2. 这个时候我们会遇到两种情况:一是数组上该位置为空,可以直接放入数据;还有一种情况是该位置已经存放值了,这就发生了哈希冲突。
  3. 在现在使用较为普遍的JDK1.8中是这样处理哈希冲突的:先用链表把冲突的元素串起来,如果链表的长度达到了8,并且哈希表的长度大于64,则把链表转为红黑树。(在JDK1.7中没有转化为红黑树这一步,只用链表解决冲突)
HashMap是怎样确定key存放在数组的哪个位置的?

JDK1.8

首先计算key的hash值,计算过程是:先得到key的hashCode(int类型,4字节),然后把hashCode的高16位与低16位进行异或,得到key的hash值。

接下来用key的hash值与数组长度减一的值进行按位与操作,得到key在数组中对应的下标。
追问:为什么计算key的hash时要把hashCode的高16位与低16位进行异或?(变式:为什么不直接用key的hashCode)
计算key在数组中的下标时,是通过hash值与数组长度减一的值进行按位与操作的。由于数组的长度通常不会超过2^16,所以hash值的高16位通常参与不了这个按位与操作。

为了让hashCode的高16位能够参与到按位与操作中,所以把hashCode的高16位与低16位进行异或操作,使得高16位的影响能够均匀稀释到低16位中,使得计算key位置的操作能够充分散列均匀。

为什么要把链表转为红黑树,阈值为什么是8?

在极端情况下,比如说key的hashCode()返回的值不合理,或者多个密钥共享一个hashCode,很有可能会在同一个数组位置产生严重的哈希冲突。

这种情况下,如果我们仍然使用使用链表把多个冲突的元素串起来,这些元素的查询效率就会从O(1)下降为O(N)。为了能够在这种极端情况下仍保证较为高效的查询效率,HashMap选择把链表转换为红黑树,红黑树是一种常用的平衡二叉搜索树,添加,删除,查找元素等操作的时间复杂度均为O(logN)

至于阈值为什么是8,这是HashMap的作者根据概率论的知识得到的。当key的哈希码分布均匀时,数组同一个位置上的元素数量是成泊松分布的,同一个位置上出现8个元素的概率已经接近千分之一了,这侧面说明如果链表的长度达到了8,key的hashCode()肯定是出了大问题,这个时候需要红黑树来保证性能,所以选择8作为阈值。

追问:为什么红黑树转换回链表的阈值不是7而是6呢?
如果是7的话,那么链表和红黑树之间的切换范围值就太小了。如果我的链表长度不停地在7和8之间切换,那岂不是得来回变换形态?所以选择6是一种折中的考虑。

请说一下HashMap的扩容原理
  1. 首先得到新的容量值和新的扩容阈值,默认都是原来大小的两倍。
  2. 然后根据新容量创建新的数组
  3. 最后把元素从旧数组中迁移到新数组中
    在JDK1.7中,迁移数据的时候所有元素都重新计算了hash,并根据新的hash重新计算数组中的位置。

在JDK1.8中,这个过程进行了优化:如果当前节点是单独节点(后面没有接着链表),则根据该节点的hash值与新容量减一的值按位与得到新的地址。

如果当前节点后面带有链表,则根据每个节点的hash值与旧数组容量进行按位与的结果进行划分。如果得到的值为0,这些元素会被分配回原来的位置;如果得到的结果不为0,则分配到新位置,新位置的下标为当前位置下标加上旧数组容量。

还有一种情况是当前节点是树节点,那么会调用一个专门的拆分方法进行拆分。
追问:为什么HashMap不支持动态缩容?
如果要支持动态缩容,可能就要把缩容安排在remove方法里,这样可能会导致remove方法的时间复杂度从O(1)上升为O(N)。

还有一点可能和我们编写Java代码的习惯有关:由于Java有自动垃圾回收机制,让我们得以可劲地new对象,Java也默认了我们这种吃饭不收拾盘子的行为。既然对象会被回收,HashMap动态缩容在这样的大环境下似乎就显得没那么重要了,这可以说是一种空间换时间的策略吧。

为什么HashMap中适合用Integer,String这样的基础类型作为key?

因为这些基础类内部已经重写了hashCode和equals方法,遵守了HashMap内部的规范。

追问:如果要用我们自己实现的类作为key,要注意什么?
一定要重写hashCode()和equals()方法,而且要遵从以下规则:

equals()是我们判断两个对象是否相同的依据,如果我们重写了equals方法,用自己的逻辑去判断两个对象是否相同,那么一定要保证:

两个equals()返回true的对象,一定要返回相同的hashCode。

这样,在HashMap的put方法中才能正确判断key是否相同。

不是经常有一个问题嘛,两个对象hashCode相同,equals一定返回true吗?答案肯定是否的,这和你的设计密切相关:如果在你的编程思路中这两个对象是不同的,那么就算恰巧两个对象的hashCode相同,equals也应该返回false。

为什么HashMap数组的长度是2的幂次方?

因为这样能够提高根据key计算数组位置的效率。

HashMap根据key计算数组位置的算法是:用key的hash值与数组长度减1的值进行按位与操作。

在我们正常人的思维中,获取数组的某个位置最直接的方法是对数组的长度取余数。但是如果被除数是2的幂次方,那么这个对数组长度取余的方法就等价于对数组长度减一的值进行按位与操作。

在计算机中,位运算的效率远高于取模运算,所以为了提高效率,把数组的长度设为2的幂次方。

HashMap与HashTable有什么区别?

在JDK1.7之前,两者的实现极为相似,最大的区别在于HashTable的方法都用synchronized关键字修饰起来了,表明它是线程安全的。

但是由于直接在方法上加synchronized关键字的同步效率较低,在并发情况下,官方推荐我们使用ConcurrentHashMap。

所以我们看到在JDK1.8中,官方甚至没有对HashTable进行链表转树这样的优化,HashTable已经不被推荐使用了。

请说一下ConcurrentHashMap的实现原理

在JDK1.7中ConcurrentHashMap采用了一种分段锁的机制,它的底层实现是一个segment数组,每个segment的底层结构和HashMap相似,也是数组加链表。

当对segment里面的元素进行操作之前,需要获得该segment独有的一把ReentrantLock。ConcurrentHashMap如果不进行手动设置的话,默认有16个segment,可以支持16个线程对16个不同的segment进行并发写操作。

在JDK1.8之后摒弃了segment这种臃肿的设计,新的实现和HashMap非常相似,底层用的也是数组加链表加红黑树。

在新实现中,在put方法里使用了CAS + synchronized进行同步。如果插入元素的位置为空,则使用CAS进行插入。如果插入的位置不为空,则对当前位置的对象进行加锁,也就链表或红黑树的头节点,加锁后再进行后续的插入操作。

这样设计的好处是:

  1. CAS是十分轻量的加锁操作,如果能够直接插入,用CAS能够大幅度节省加锁的开销。
  2. 如果发生冲突,只用锁住当前位置的头结点,理论上数组的长度有多大,并发操作的线程数就能有多少,比原来只能有16个线程效率更高。
SpringBoot 特性
  • 使用 Spring 项目引导页面可以在几秒构建一个项目;
  • 方便对外输出各种形式的服务,如 REST API、WebSocket、Web、Streaming、Tasks;
  • 非常简洁的安全策略集成;
  • 支持关系数据库和非关系数据库;
  • 支持运行期内嵌容器,如 Tomcat、Jetty;
  • 强大的开发包,支持热启动;
  • 自动管理依赖;
  • 自带应用监控;
  • 支持各种 IDE,如 IntelliJ IDEA、NetBeans。
SpringBoot和微服务架构

SpringBoot的一系列特性有助于实现微服务架构的落地,从目前众多的技术栈对比来看它是Java领域微服务架构最优落地技术,没有之一。

为什么学习SpringBoot

SpringBoot本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。同时它集成了大量常用的第三方配置(如Redis、MongoDB、JPA、RabbitMQ、Quartz等),Spring Boot 应用中这些第三方库几乎可以零配置进行开箱即用,大部分的 Spring Boot 应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。

使用 Spring Boot 开发项目,有以下几方面优势:

  • Spring Boot 使开发变得简单,提供了丰富的解决方案,快速集成各种解决方案提升开发效率。
  • Spring Boot 使配置变得简单,提供了丰富的 Starters,集成主流开源产品往往只需要简单的配置即可。
  • Spring Boot 使部署变得简单,其本身内嵌启动容器,仅仅需要一个命令即可启动项目,结合 Jenkins、Docker 自动化运维非常容易实现。
  • Spring Boot 使监控变得简单,自带监控组件,使用 Actuator 轻松监控服务各项状态。

从软件发展的角度来讲,越简单的开发模式越流行,简单的开发模式解放出更多生产力,让开发人员可以避免将精力耗费在各种配置、语法所设置的门槛上,从而更专注于业务。这点上,Spring Boot 已尽可能地简化了应用开发的门槛。

Spring Boot 所集成的技术栈,涵盖了各大互联网公司的主流技术,跟着 Spring Boot 的路线去学习,基本可以了解国内外互联网公司的技术特点。

总结:
Spring Boot 是一套快速开发框架,在微服务架构的大环境下 Spring Boot 一经推出就受到开源社区的追捧。Spring Boot 有强大的生态整合能力,提供了众多的 Starters 包,非常方便 Spring Boot 集成第三方开源软件达到开箱即用的效果。

SpringBoot 由来

Spring平台饱受非议的一点就是大量的XML配置以及复杂的依赖管理。

Spring的核心:约定优于配置

那么什么是约定优于配置呢?

约定优于配置(Convention Over Configuration),也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量、获得简单的好处,而又不失灵活性。

本质是说,开发人员仅需规定应用中不符约定的部分。例如,如果模型中有个名为 User 的类,那么数据库中对应的表就会默认命名为 user。只有在偏离这一约定时,例如将该表命名为“user_info”,才需写有关这个名字的配置。

我们可以按照这个思路来设想,我们约定 Controller 层就是 Web 请求层可以省略 MVC 的配置;我们约定在 Service 结尾的类自动注入事务,就可以省略了 Spring 的切面事务配置。

在 Spring 体系中,Spring Boot JPA 就是约定优于配置最佳实现之一,不需要关注表结构,我们约定类名即是表名,属性名即是表的字段,String 对应 varchar,long 对应 bigint,只有需要一些特殊要求的属性,我们再单独进行配置,按照这个约定我们可以将以前的工作大大简化。

Spring Boot 体系将约定优于配置的思想展现得淋漓尽致,小到配置文件、中间件的默认配置,大到内置容器、生态中的各种 Starters 无不遵循此设计规则。Spring Boot 鼓励各软件组织方创建自己的 Starter,创建 Starter 的核心组件之一就是 autoconfigure 模块,也是 Starter 的核心功能,在启动的时候进行自动装配,属性默认化配置。

可以说正是因为 Spring Boot 简化的配置和众多的 Starters 才让 Spring Boot 变得简单、易用、快速上手,也可以说正是约定优于配置的思想彻底落地才让 Spring Boot 走向辉煌。Spring Boot 约定优于配置的思想让 Spring Boot 项目非常容易上手,让编程变得更简单,其实编程本该很简单,简单才是编程的美。

SpringBoot Starters

Spring Boot Starters 基于约定优于配置的理念来设计,Spring Boot Starter 中有两个核心组件:自动配置代码和提供自动配置模块及其它有用的依赖。也就意味着当我们项目中引入某个 Starter,即拥有了此软件的默认使用能力,除非我们需要特定的配置,一般情况下我仅需要少量的配置或者不配置即可使用组件对应的功能。

Spring Boot 由众多 Starter 组成,随着版本的推移 Starter 家族成员也与日俱增。在传统 Maven 项目中通常将一些层、组件拆分为模块来管理,以便相互依赖复用,在 Spring Boot 项目中我们则可以创建自定义 Spring Boot Starter 来达成该目的。

Spring Boot 拥有强大融合社区开源软件的能力,在没有使用 Spring Boot 之前,我们需要按照每个开源软件的特性,将对应的组件包集成到我们的开发项目中,因为每个组件的设计理念和开发团队都不一致,因此会有很多不同的调用风格在我们的项目中。

Spring Boot 整合了主流的开源软件形成了一系列的 Starter,让我们有了一致的编程体验来集成各种软件,Spring Boot 在集成的时候做了大量的优化,让我们在集成的时候往往只需要很少的配置和代码就可以完成。可以说各种 Starters 就是 Spring Boot 最大的优势之一。

以下为常用的 Spring Boot Starter 列表。

名称 描述 Pom
spring-boot-starter 核心 Starter,包括自动配置支持,日志和 YAML Pom
spring-boot-starter-activemq 用于使用 Apache ActiveMQ 实现 JMS 消息 Pom
spring-boot-starter-amqp 用于使用 Spring AMQP 和 Rabbit MQ Pom
spring-boot-starter-cache 用于使用 Spring 框架的缓存支持 Pom
spring-boot-starter-data-elasticsearch 用于使用 ElasticSearch 搜索,分析引擎和 Spring Data ElasticSearch Pom
spring-boot-starter-data-jpa 用于使用 Hibernate 实现 Spring Data JPA Pom
spring-boot-starter-data-mongodb 用于使用基于文档的数据库 MongoDB 和 Spring Data MongoDB Pom
spring-boot-starter-data-redis 用于使用 Spring Data Redis 和 Jedis 客户端操作键—值数据存储 Redis Pom
spring-boot-starter-jta-atomikos 用于使用 Atomikos 实现 JTA 事务 Pom
sring-boot-starter-mail 用于使用 Java Mail 和 Spring 框架 Email 发送支持 Pom
spring-boot-starter-quartz 用于定时任务 Quartz 的支持 Pom
spring-boot-starter-security 对 Spring Security 的支持 Pom
spring-boot-starter-test 用于测试 Spring Boot 应用,支持常用测试类库,包括 JUnit、Hamcrest 和 Mockito Pom
spring-boot-starter-thymeleaf 用于使用 Thymeleaf 模板引擎构建 MVC Web 应用 Pom
spring-boot-starter-validation 用于使用 Hibernate Validator 实现 Java Bean 校验 Pom
spring-boot-starter-web 用于使用 Spring MVC 构建 Web 应用,包括 RESTful。Tomcat 是默认的内嵌容器 Pom
spring-boot-starter-websocket 用于使用 Spring 框架的 WebSocket 支持构建 WebSocket 应用 Pom
Spring、Spring Boot 和 Spring Cloud 的关系

Spring 最初核心的两大核心功能 Spring IoC 和 Spring Aop 成就了 Spring,Spring 在这两大核心功能上不断地发展,才有了 Spring 事务、Spring MVC 等一系列伟大的产品,最终成就了 Spring 帝国,到了后期 Spring 几乎可以解决企业开发中的所有问题。

Spring Boot 是在强大的 Spring 帝国生态基础上面发展而来,发明 Spring Boot 不是为了取代 Spring,是为了让人们更容易的使用 Spring。所以说没有 Spring 强大的功能和生态,就不会有后期 Spring Boot 的火热,Spring Boot 使用约定优于配置的理念,重新重构了 Spring 的使用,让 Spring 后续的发展更有生命力。

Spring 并没有重复制造轮子,它只是将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过 Spring Boot 风格进行再封装并屏蔽掉复杂的配置和实现原理,最终给开发者提供了一套简单易懂、易部署、易维护的分布式系统开发工具包。

Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的开发便利性巧妙地简化了分布式系统基础设施的开发。服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用 Spring Boot 的开发风格做到一键启动和部署。

Spring Cloud 是为了解决微服务架构中服务治理而提供的具备一系列功能的开发框架,并且 Spring Cloud 是完全基于 Spring Boot 而开发,Spring Cloud 利用 Spring Boot 特性整合了开源行业中优秀的组件,整体对外提供了一套在微服务架构中服务治理的解决方案。

综上我们可以这样来理解,正是由于 Spring IoC 和 Spring Aop 两个强大的功能才有了 Spring,Spring 生态不断的发展才有了 Spring Boot,使用 Spring Boot 让 Spring 更易用更有生命力,Spring Cloud 是基于 Spring Boot 开发的一套微服务架构下的服务治理方案。

以下为它们之间的关系。

Spring Ioc/Aop > Spring > Spring Boot > Spring Cloud

总结

Spring Boot 诞生一方面是因为 Spring 自身发展所遇到的问题,另一方面在微服务思想诞生之际,急需要一款快速开发工具来实现微服务技术落地,在这样的背景下诞生了 Spring Boot。

Spring Boot 整体的设计思想是:约定优于配置。依赖此设计思路,Spring Boot 进行了大刀阔斧的改革,让开发、测试、部署更为便捷。众多的 Starters 成就了 Spring Boot 的发展,让使用 Spring Boot 开发项目变得更加简单。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342