2022秋招面经总结(补充前面文章中不含的内容)

面经总结

  • MySQL
  • JAVA语言基础
  • JVM
  • Spring
  • 计算机网络
  • OS
  • 排序算法
  • 参考文献

MySQL

一.有用到模糊查询吗?怎么用的?
[]:匹配括号内所列字符中的1个,类似正则表达式。
select * from table_name where name like ‘[ABC]D’
select * from table_name where name like ‘%COM’

二.索引?
索引的目的在于提高查询效率,索引存储在磁盘文件里,索引的数据结构是B+树,使用B+树是为了减少IO。(一张表中,id不是索引,只是一个字段,但若给id加了primary key,id就变成了索引了

  1. 聚簇索引:找到了索引就找到了需要的数据,那么这个索引就是聚簇索引,所以主键就是聚簇索引,修改聚簇索引其实就是修改主键。
    The InnoDB term for a primary key index.

  2. 非聚簇索引:索引的存储和数据的存储是分离的,也就是说找到了索引(比如unique key)但没找到数据,需要根据索引上的值(主键)再次回表查询,非聚簇索引也叫做辅助索引。

总结:主键一定是聚簇索引(MyisAM引擎没有聚簇索引),,其它普通索引需要区分SQL场景。

2022秋招面经总结(补充前面文章中不含的内容)_第1张图片
补充:索引进阶

  • 索引点:提高询效率,减少IO(内外存交互)
    索引点:索引也要占空间;有了更新表的速度成本,对数据进行增删改时需要更新变化。(这也是为什么不能全部字段都加索引的原因,太占空间,而且当索引过多对于写操作也会浪费时间

  • 你知道什么索引?
    主键(聚簇索引)唯一索引(加unique,特点是索引列的值必须唯一,但允许有多个null)、复合索引(一个索引包含多个列,有个左前缀特性(别提了))、普通索引(就是直接add index myIdxName(列))、全文索引(add fulltext我的理解是和like的作用相似)

  • 索引的创建:

create index myIdxName on myTable(myColofTable);

show index from myTable;

drop index myIdxName on myTable;

三.B树和B+树?

  1. B树中每个节点(包括叶节点和非叶节点)都存储真实的数据,B+树中只有叶子节点存储真实的数据。
  2. B+树的叶子节点包含了所有的关键字,且B+树的叶节点之间通过双向链表链接。我认为是这样方便整体全部数据的遍历查询。不用像B树一样往上返回。

查询过程:B+树的查询与B树大致上一样,但不同的是在B+树里面查找必须找到叶节点层才行,因为B+树里面最底层的叶节点才是全集,而在B树里面非叶子节点也可以存储数据,所以直接查询遇到查找的值就返回。
B+树的两种查询方式:
(1)从root出发一直查询到叶节点。
(2)从叶节点的最左侧出发,进行顺序查找,这个比较适合迭代输出全量数据(或范围查找)的时候使用。

为什么用B+而不用B:
(1)范围查询
(2)做排序 desc时

补:一棵三层的B+树能存多少数据量?
Innodb的最小存储单位,页,大小为16k
除了叶子节点存数据,其他节点存的是主键(排序)和指针。大小为bigint8字节,指针6字节,一共14个字节
单个叶子节点存的数据一般为16条。(一行数据大约为1k
所以高度为2的b+树:16348(16k)/14=1170, 117016=18720,约2万条数据
所以高度为3的b+树:1170
1170*16,约2千万条数据

四. 二叉搜索树 平衡二叉树 红黑树?

  1. 首先二叉搜索树(=二叉排序树Binary Sort Tree)。正常的情况下,查找的时间复杂度为O(logn)。但存在那种都是小的节点插入,二叉查找树已经近似退化为一条链表,这样的二叉查找树的查找时间复杂度顿时变成了 O(n)。 定义:左子树中所有节点的值 << 根节点的值;右子树中所有节点的值 >> 根节点的值;其左、右子树也分别为二叉搜索树。
  2. 平衡二叉树首先是一个二叉搜索树,但带有了平衡条件,每个结点的左右子树的高度之差的绝对值(平衡因子)最多严格为1。解决了二叉搜索树退化的情况,最坏的查找时间复杂度也为 O(logn)。AVL树适合用于插入与删除次数比较少(因为严格执行平衡为1,需要频繁左旋右旋),但查找多的情况。
  3. 红黑树也是二叉搜索树(还是弱平衡二叉树),红黑树确保没有一条路径会比其它路径长出两倍,多了着色。时间复杂度也是 O(logn)
    (HashMap在JDK1.8中为了解决过度哈希冲突带来的长链表,会将链表转为红黑树)

红黑树与B树(2-3树是三阶B树,2-3-4树是4阶B树)联系:红黑树其实就是对概念模型2-3树(或者2-3-4树)的一种实现。

  1. 对于2-3-4树:2节点直接转化为黑色节点;3节点这里可以有两种表现形式,左倾红节点或者右倾红节点。而4节点被强制要求转化为一个黑父带着左右两个红色儿子。
    2022秋招面经总结(补充前面文章中不含的内容)_第2张图片
  2. 对于2-3树:左倾红黑树。顾名思义,左倾红黑树限制了如果在树中出现了红色节点,那么这个节点必须是左儿子。
    2022秋招面经总结(补充前面文章中不含的内容)_第3张图片

2022秋招面经总结(补充前面文章中不含的内容)_第4张图片

五. GROUP BY和聚合函数?

join等价于inner join内连接抄,是返回两个表中都有的符合条件的行。
left join左连接,是返回左袭表知中所有的行及右表中符合条件的行。
right join右连接,是返回右表中所有的行及左表中符合条件的行。

MAX() 函数返回指定列的最大值。

GROUP BY 语句可结合一些聚合函数来使用,如count(), sum()
2022秋招面经总结(补充前面文章中不含的内容)_第5张图片

六. 组合两个表?
2022秋招面经总结(补充前面文章中不含的内容)_第6张图片
解题步骤:
1)查询结果是两个表里的列名,所以需要多表查询
2)考虑到有的人可能没有地址信息,要是查询结构要查所有人,需要保留表1(Person)里的全部数据,所以用左联结(left join)
3)两个表联结条件:两个表通过personId产生联结。

select FirstName, LastName, City, State  
from Person  
left join Address  
on Person.PersonId = Address.PersonId;

2022秋招面经总结(补充前面文章中不含的内容)_第7张图片

七. 如何查找第N高的数据?2022秋招面经总结(补充前面文章中不含的内容)_第8张图片

  1. distinct :可以去重。语法:select distinct 列…
  2. limit :接受一个或两个数字参数。如果只给定一个参数,它表示返回最大的记录行数目(SELECT * FROM table LIMIT 5 ; // 检索前 5 个记录行)。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。(SELECT * FROM table LIMIT 5 , 10 ; // 检索记录行 6 - 15)。
  3. ifnull :ifnull(a,b)函数解释,如果value1不是空,结果返回a,如果value1是空,结果返回b。语法:select ifNull(a,b)
select ifNull((select distinct Salary
from Employee
order by Salary desc
limit 1,1),null) as SecondHighestSalary;

八. innodb特性?
InnoDB的内存架构主要分为三大块,缓冲池(Buffer Pool)、重做缓冲池(Redo Log Buffer)和额外内存池

  1. 插入缓冲
    等数据达到某个阈值(例如50条)才批量的写入磁盘,提高读写性能
  2. 二次写
    解决缓冲池中将数据刷回磁盘的时候宕机的问题,提高了数据的可靠性。
  3. 自适应哈希
    InnoDB会监控对所有索引的查询,对热点访问的页建立哈希索引
  4. 预读
    提前将一些page读到缓冲池Buffer Pool

九. innodb与MyISAM的区别?

  1. 事务: InnoDB支持事务、回滚、事务安全和奔溃恢复。而MyISAM不支持,但查询的速度要比InnoDB更快
  2. 主键:InnoDB规定,如果没有设置主键,就自动的生成一个6字节的主键,所以InnoDB肯定有主键,而MyISAM没有主键的存在,索引就是行的地址
  3. 外键:InnoDB支持外键,而MyISAM不支持
  4. 锁差异:InnoDB支持行锁和表锁(例如:select…for update),而MyISAM 只支持表级锁(用户在操作myisam表时,select,update,delete,insert语句都会给表自动加锁,如果加锁以后的表满足insert并发的情况下,可以在表的尾部插入新的数据)

总结:MyISAM只适用于查询大于更新的场景

十. 谈谈你对Mysql的MVCC的理解?
Multi-Version Concurrency Control, 多版本并发控制。

  1. 发展过程:读写锁读锁和读锁之间不互斥,而写锁和写锁、读锁都互斥并发读还不够,又提出了能不能让读写之间也不冲突的方法,就是读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了。

  2. 运行条件:MVCC只在 READ COMMITTEDREPEATABLE READ 两个隔离级别下工作。其他两个隔离级别够和MVCC不兼容, 因为 READ UNCOMMITTED 总是读取最新的数据行, 而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。

  3. 解释:用到版本链 and 聚簇索引记录中有两个必要的隐藏列:trx_id, roll_pointer。已提交读和可重复读的区别就在于它们生成ReadView的策略不同。对于已提交读每次都重新生成一个ReadView,然后顺着链找上一个。对于可重复读,则在第一次读的时候生成一个ReadView之后的读都复用之前的ReadView

十一. innodb事务日志undo log和redo log?mysql的bin log日志?

  1. undo日志实现ACID的原子性,回滚日志,逻辑日志记录DML语句

MVCC 的多版本指的是多个版本的快照,快照存储在 Undo 日志中,该日志通过回滚指针 ROLL_PTR 把一个数据行的所有快照连接起来。

2022秋招面经总结(补充前面文章中不含的内容)_第9张图片

  1. redo log(物理日志)实现ACID的持久性,重做日志,物理日志记录修改。存储了数据被修改的值。在数据库重启恢复的时候被使用。循环队列实现。

具体解释:首先我们先明确一下InnoDB的修改数据的基本流程,当我们想要修改DB上某一行数据的时候,InnoDB是把数据磁盘读取到内存缓冲池上进行修改。这个时候数据在内存中被修改,与磁盘中相比就存在了差异,我们称这种有差异的数据为脏页。InnoDB对脏页的处理不是每次生成脏页就将脏页刷新回磁盘,这样会产生海量的IO操作,严重影响InnoDB的处理性能。对于此,InnoDB有一套完善的处理策略。既然脏页与磁盘中的数据存在差异,那么如果在这期间DB出现故障就会造成数据的丢失。为了解决这个问题,redo log就应运而生了。

  1. bin log(二进制日志,逻辑日志),它记录了所有的 DDL 和 DML 语句(除了数据查询语句select、show等),还包含语句所执行的消耗的时间binlog 的主要目的是复制&备份&同步。

具体解释:任何时间对数据库的修改都会记录在bin log(二进制日志,逻辑日志)中。当数据发生增删改,创建数据库对象都会记录到binlog中,数据库的复制也是基于binlog进行同步数据。它记录了所有的 DDL 和 DML 语句(除了数据查询语句select、show等),以事件形式记录,还包含语句所执行的消耗的时间,MySQL的二进制日志是事务安全型的。binlog 的主要目的是复制和恢复。

补:
崩溃断电场景:恢复机制会将redo log中已提交的事务重做,保证事务的持久性;而undo log中未提交的事务进行回滚,保证事务的原子性。

mysql实现一致性:一致性就是:应用系统从一个正确的状态到另一个正确的状态.而ACID就是说事务能够通过AID来保证这个C的过程.C是目的,AID都是手段

隔离性:mvcc和锁机制去实现的(四种隔离级别)。

十二. select count(*) from table 时间是常数级吗?
只知道,MySQL里面的MyISAM引擎做全表count特别快
阿里java开发手册sql规约第一条:不要使用 count(列名)或 count(常量)来替代 count( * ),count( * )就是 SQL92 定义 的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。 说明:count( * )会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

十三. mysql中int(10)和char(10),varchar(10)的10代表什么?

  • int(10),10代表最大显示宽度,和存储无关, int(3)、int(4)、int(8) 在磁盘上都是占用 4 btyes 的存储空间,若短了会在前面补0
  • char(10)== 定度(一辈子占10个字符空间)==,不足补空格,最多10个字符
  • varchar(10)变长(看真实是多少存多少),可以存储的最大字符串长度为10个字符(字节),如果不满10个就根据实际存储的字节来确定。(4.0版本以下,varchar(20),指的是20字节5.0版本以上,varchar(20),指的是20字符(无论是中文还是数字还是字母)

十四. 联合索引的最左匹配原则?

  • 最左匹配原则,在遇到范围查询的时候,就会停止匹配
  • 对(b,a,c)建立索引。在执行sql的时候,优化器会帮我们调整where后a,b,c的顺序,让我们用上索引(如果索引用不上,就会全表查询(乱序查询),用b+树的结构去想)

复合索引(abc) 所有情况的罗列:
abc 能用到索引
bca 能用到索引(优化器)
cab 能用到索引(优化器)
ab 能用到索引
ac 能用到索引(这样好像虽然a会用到索引,但c用不到索引)
ba 能用到索引(优化器)
ca 能用到索引(优化器,这样好像虽然a会用到索引,但c用不到索引)
bc 用不到索引
b 用不到索引
c 用不到索引
cb 用不到索引

十五. 覆盖索引(解决回表问题)?

  • 聚簇索引:InnoDB的聚簇索引的叶子节点存储数据
  • 普通索引:普通索引也叫二级索引,除聚簇索引外的索引,即非聚簇索引。InnoDB的普通索引叶子节点存储的是主键(聚簇索引)的值,而MyISAM的普通索引存储的是记录指针。

如果查询条件为普通索引(非聚簇索引),需要扫描两次B+树,第一次扫描通过普通索引定位到聚簇索引的值,然后第二次扫描通过聚簇索引的值定位到要查找的行记录数据(回表)。非主键索引的查询需要多扫描一棵索引树,因此需要尽量使用主键索引查询。

例子:

  • select * from t_user where name=张三 即普通索引查询方式,则需要先搜索name索引树,得到id的值为3,再到id索引树搜索一次。这个过程称为回表。
  • select id from user_table where name= ‘张三’。因为 name索引树 的叶子结点上保存有 name和id的值 ,所以通过 name索引树 查找到id后,因此可以直接提供查询结果,不需要回表,也就是说,在这个查询里面,索引name 已经 “覆盖了” 我们的查询需求,我们称为覆盖索引。
  • select password from user_table where name= ‘张三’。name索引树 上 找到 name=‘张三’ 对应的主键id, 通过回表在主键索引树上找到满足条件的数据。

当sql语句的所求查询字段(select列)和查询条件字段(where子句)全都包含在一个索引中(复合索引),可以直接使用索引查询而不需要回表。这就是覆盖索引。

注意复合索引引出来最左匹配原则

十六. 范式和反范式的区别?

  • 范式优点:(第二范式)去除冗余数据;(第三范式)解决数据添加、删除问题。
    范式缺点:查询时需要更多的关联,可能引起索引策略无效。例如,范式化可能将列存放在不同的表中,而这些列如果在一个表中本可以属于同一个索引。

  • 反范式优点:所有的数据可以在一张表上显示,可以避免关联,可以设计有效的索引
    反范式缺点:肯定是范式的优点

十七.limit分页
select * from table limit (start-1)*pageSize,pageSize(start是传进来的页码,limit index,num)

JAVA语言基础

一. synchronized和lock?

  1. Synchronized是关键字,lock是个接口
  2. Synchronized会自动释放,lock需要在finally中调用lock.unlock()释放锁
  3. Synchronized写死了需要一直等待,Lock可以调用lock.tryLock()来判断能否得到锁。
  4. synchronized可以加在方法上,也可以加在特定代码块中,Lock一般使用ReentrantLock类做为锁,Lock=new ReentrantLock。
  5. Synchronized为非公平锁,时间片轮转竞争资源。ReentrantLock可以实现公平锁,给其初始化参数为true。

补充:

  1. synchronized两种用法:同步方法同步代码块(两种用法均包含对象锁类锁的形式)
  2. synchronized 加到static 方法前面是给class加锁,即类锁;而synchronized 加到非静态方法前面是给对象上锁
  3. 一个线程持有对象锁,另一个线程可以以异步的方式调用对象里面的非synchronized方法。(但不能调用其他synchronized普通方法,因为对象被锁了,都是获取到对象本身的锁,这也就是为什么叫对象锁而不叫方法锁

二. synchronized做了哪些优化(jdk1.6)?
新增的Java对象头(锁信息的标志位)实现了锁升级功能。Synchronized 同步锁初始为偏向锁,随着线程竞争越来越激烈,偏向锁升级到轻量级锁,最终升级到重量级锁

  • 偏向锁:主要用来优化同一线程多次申请同一个锁的竞争,也就是现在的Synchronized锁实际已经拥有了可重入锁的功能。在我们的应用中,可能大部分时间是同一个线程竞争锁资源(比如单线程操作一个线程安全的容器),如果这个线程每次都要获取锁和释放锁,那么就在不断的从内核态与用户态之间切换。那么有了偏向锁,当一个线程再次访问这个同步代码或方法时,该线程只需去对象头中去判断一下是否当前线程是否持有该偏向锁就可以了。

  • 轻量级锁:当有另外一个线程竞争获取这个锁时,由于该锁已经是偏向锁,当发现对象头中的线程 ID 不是自己的线程 ID,就会进行 CAS(compare and swap, 操作系统的TSL)操作获取锁,如果获取成功,直接替换对象头中的线程 ID 为自己的 ID,该锁会保持偏向锁状态;如果获取锁失败,代表当前锁有一定的竞争,偏向锁将升级为轻量级锁。轻量级锁适用于线程交替执行同步块的场景,绝大部分的锁在整个同步周期内都不存在长时间的竞争。用的是CAS乐观锁(自旋锁)

  • 重量级锁:操作系统的pthread_mutex

补充:
CAS, compare and swap?

  • CAS操作包含三个操作数 —— 内存位置(V),期望值(A),和新值(B)。
    如果内存位置的值与期望值匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不作任何操作。
  • TSL和CMP这两条源语2022秋招面经总结(补充前面文章中不含的内容)_第10张图片

为什么要自旋锁?

  • 自旋锁相当于Busy wating,并不放弃cpu,而是一直测一直测有没有资源,yield the CPU;

  • 互斥锁相当于Sleep and Wakeup,把自己阻塞了,等待cpu重新调度(锁的东西相当于资源,从running进入blocking,当满足锁释放条件了再从blocking到ready,等待cpu调度

  • 在线程竞争不激烈时,用自旋锁,因为需要busy waiting的时间比较少。且减少了用互斥锁用户态内核态切换的开销。

  • 在线程竞争激烈时,用互斥所,因为busy waiting时间较长,浪费了CPU资源.

什么是内核态?什么是用户态?为什么切换开销大?

  • 用户态比如printf(),c的函数api。调用的是wirte()系统调用(system call)。用户态的进程和内核态的进程权限不一样
  • 系统调用一般都需要保存用户程序得上下文(context), 在进入内核得时候需要保存用户态得寄存器,在内核态返回用户态得时候会恢复这些寄存器得内容。系统调用在两个状态切换时相当于中断,会保存现场到寄存器,然后切回来需要恢复现场。

二.Volatile关键字?

  1. 不使用副本,直接操作原始变量(直接操作这块内存),相当于节约了拷贝副本,重新放回的步骤。(主要在attribute上使用)
    在正常进行变量处理三个步骤:
    获取变量原有数据内容副本;
    利用副本为变量进行数学计算;
    将计算后的变量,保存到原始空间之中;
    通过以上,可以保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。但是,volatile关键字能保证可见性没有错,但是上面的程序错在没能保证原子性。

  2. 禁止进行指令重排序。即volatile变量的相关的语句执行,一定保证在前面的代码和后面的代码中间。内存屏障。

三.线程池及线程池参数
线程池在并发情况下用。线程池七大参数:

  1. Int corePoolSize:按百分之80的情况去统计1s产生多少个任务,再根据执行任务时间,最后根据需求算出核心线程数量。
  2. Int maxmumPoolSize:需要参照最大任务数(高并发情况下)的一个公式计算。(经验公式:如果是CPU密集型应用,则线程池大小设置为CPU核数+1;如果是IO密集型应用,则线程池大小设置为2*CPU核数+1
  3. Long keepAliveTime:参考系统运行环境和硬件压力
  4. TimeUnit unit:
  5. BlockingQueue workQueue:用于保存等待执行的任务阻塞队列,核心线程数量/单个任务执行时间*等待时间(2s)
  • ArrayBlockingQueue:基于数组结构的有界阻塞队列,先进先出
  • LinkedBlockingQueue:基于链表结构的无界阻塞队列,先进先出
  • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。
  • PriorityBlockingQueue:一个具有优先级无限阻塞队列。传入Comparator对任务进行优先级处理
    (在多线程领域:所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒)
  1. ThreadFactory threadFactory 允许程序员自己创建线程对象(提交任务的submit方法是:提交了实现runnable接口的class task,区分一下)
  2. RejectedExecutionHandler handler:拒绝策略

ExecutorService接口是java内置的线程池接口(不过在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors中提供的3个常用静态方法来创建线程池。

从它们的具体实现来看,它们实际上也是调用了ThreadPoolExecutor,只不过参数都已配置好了。 除此之外ScheduledExecutorService接口接收Executors提供的Scheduled线程池方法,可以控制线程执行时间。 除此之外,java中future接口就是专门用于描述异步计算结果的(带返回值的)。

补充:
调用方法 execute 向线程池提交任务时,以下操作:
核心线程满了,接下来进队列,队列也满了,创建新线程,直到达到最大线程数,之后再超出,会进入拒绝rejectedExecution

默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0

在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中

拒绝策略有四种:

  1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常(默认)
  2. ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常
  3. ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
  4. ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务(比如,主线程main也执行了任务)

关闭线程池
可以通过调用线程池的方法== shutdownshutdownNow关闭线程池。这两个方法的原理是遍历线程池中的工作线程,对每个工作线程调用 interrupt 方法中断线程==,无法响应中断的任务可能永远无法终止

  • 方法 shutDown 将线程池的状态设置成 SHUTDOWN,正在执行的任务继续执行,没有执行的任务将中断
  • 方法 shutDownNow 将线程池的状态设置成 STOP,正在执行的任务被停止,没有执行的任务被返回

四.sleep()和wait()?

  1. sleep()释放CPU执行权,但不释放同步锁;wait()释放CPU执行权,也释放同步锁,使得其他线程可以使用同步控制块或者方法。
  2. sleep可以在任意地方使用;wait只能在同步代码方法或者同步代码块中使用
    (进入wait状态的线程被唤醒后,是接着上次执行的地方接着执行的。)

五. final、finally、finalize?

  1. final用在类、属性、方法上,用在类上表示不能继承,用在属性上表示是一个常量,用在方法上表示表示不能被重写
  2. finally用在异常处理try-catch代码块,无论是否出现异常,finally里的代码也一定执行,通常写关闭资源的代码
  3. finalize是一个方法,属于Object类,在对象被GC回收前会被执行,保证类在垃圾回收之前完成对特定资源的回收。

六. finally什么情况不被执行?

  1. 没有进入try-catch代码块必然不会被执行
  2. 在try-catch代码块中调用了System.exit()(注意,try-catch里有return也会执行finally)

七. HashMap重写 hashcode 和 equals 方法?

  1. 一般是自定义对象为key时用。
  2. 若不重写hashcode方法,返回的是对象地址的hashcode,下一次自定义对象.get(key)必然不一样。
  3. 重写了hashcode方法后,若不重写equals,还是找不到。因为就算hash值一样,但某一条链表上的所有的元素的hashCode返回的hash值全是一样的,只重写hashCode方法只能证明hash值相同。equals方法还是比较的地址,所以还需要重写equals。

八. HashMap版本区别?头插尾插?

  1. jdk1.7中HashMap默认采用数组+单链表(头插方式存储元素,当元素出现哈希冲突时,会存储到该位置的单链表中。
  2. jdk1.8不同,除了数组+单链表(尾插)外,当单链表中元素个数超过8个时,会进而转化为红黑树存储,巧妙地将遍历元素时时间复杂度从O(n)降低到了O(logn)

jdk1.7中采用头插入法,在扩容时会改变链表中元素原本的顺序,以至于在并发场景下导致链表成环的问题。而在jdk1.8中采用尾插入法,在扩容时会保持链表元素原本的顺序,就不会出现链表成环的问题了。

jdk1.8的CourrentHashMap从分段锁变回了cas+synchronized,synchronized也做了升级

九. Map 中,键能否重复?如果重复,会有什么现象?
Map 中不允许键重复,因为每个键对应一个值,如果有重复的键,则会出现一个键对应多个值的情况,这违背了映射的定义但是从调用put方法角度,会覆盖之前的值。

九补. HashMap和HashTable?

  • HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。(当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。)
  • HashTable既不支持Null key也不支持Null value(父类被废弃)。
  • HashMap不是线程安全的;Hashtable是线程安全的,它的每个方法中都加入了Synchronize方法。

九再补. map的put步骤以及map节点?
map再put一组key和value时,创建一个entry,放在数组中去。entry包含四个属性,key value hash next(引用)

map的put步骤
1.put一个元素时,判断数组是否为空,为空则创建
2.根据key算hash值
3.通过hash值找bucket的index,如果entry != null,用equals找这条链表上的相等的值,没有的话就头插
4.如果entry == null,就直接添加元素

十.Hashmap扩容为什么都是2倍扩容?
总结:hashmap底层计算存储位置用了与运算,只有容量为2的幂次方,才能使充分散列,避免哈希冲突。所以一直保持两倍扩容
因为Hashmap计算存储位置时,使用了(n - 1) & hash。只有当容量n为2的幂次方,n-1的二进制会全为1,位运算时可以充分散列,避免不必要的哈希冲突,所以扩容必须2倍就是为了维持容量始终为2的幂次方。

十. HashSet 和 TreeSet 的区别有哪些?

  1. HashSet 的底层实现基于 HashMap(HashMap底层是数组链表or红黑树,看上面),元素是无序的,通过方法 hashCode 和 equals 保证元素没有重复
  2. TreeSet 的底层实现基于 TreeMap(TreeMap底层是红黑树),元素是有序的,通过Comparable 接口或 Comparator 接口保证元素没有重复
    (Map 接口定义映射,Set 接口定义集合)

十一. 锁的可重入性
一个线程再次请求自己持有对象锁的临界资源时,这种情况属于重入锁,请求将会成功。

lock和synchronized一样都有可重入性:在一个线程调用synchronized方法的同时在其方法体内部调用该对象另一个synchronized方法,也就是说一个线程得到一个对象锁后再次请求该对象锁,是允许的。这就是synchronized的可重入性。(需要特别注意另外一种情况,当子类继承父类时,子类也是可以通过可重入锁调用父类的同步方法)。原理是:同一个线程每进入一次,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。

十二. yield()?join()?

  1. yield:让当前线程从running进入ready,通过cpu调度选择哪个ready执行。(注意,被yield掉还是可能被选中)
  2. join:线程的合并指的是:将指定的线程加入到当前的线程之中,可以将两个交替执行的线程合并为顺序执行的线程。让“主线程”等待“子线程”结束之后才能继续运行。有点操作系统fork的意思,从并行变成了串行。

十三. threadlocal?
线程局部变量:用域是当前单个线程,在线程开始时分配,线程结束时回收。
在TreadLocal中有一个静态内部类ThreadLocalMap,他的get():首先获取到当前的线程t,再根据t获取ThreadLocalMap,再根据map以ThreadLocal为键取值;他的put()先取到当前的线程t,再根据t获取ThreadLocalMap,再根据map以ThreadLocal为键设值

十四. 强引用?软引用?弱引用?虚引用?

  • 强引用StrongReference:Object o = new Object()。只要引用还在就不会回收掉引用对象
  • 软引用SoftReference:网页缓存、图片缓存。只有在内存不足的时候JVM才会回收该对象
    2022秋招面经总结(补充前面文章中不含的内容)_第11张图片
  • 弱引用WeakReference:ThreadLocalMap类,Entry对象当中的Key值对TheadLocal的引用就是WeakReference。会被回收,与软引用相比有更短暂的生命周期gc一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
  • 虚引用:回收时收到通知

十五. 单例模式一般用在哪?
资源共享的一些情况。比如日志应用、网站的计数器、windows回收站、spring的bean。

十六. AQS?
AbstractQueuedSynchronizer,AQS内部是使用了双向链表等待线程链接起来

十七. String常量池

public class Demo {
    public static void main(String[] args) {
        String str1="abc";
        String str2="abc";
        String str3=new String("abc");
        String str4=new String("abc");
        String str5=str4.intern();

        System.out.println(str1==str2);//第一行,true
        System.out.println(str1==str3);//第二行,false
        System.out.println(str1==str5);//第三行,true
        System.out.println(str3==str4);//第四行,false
        System.out.println(str3==str5);//第五行,false

        System.out.println(str1.equals(str2));//第六行,true
        System.out.println(str1.equals(str3));//第七行,true
        System.out.println(str1.equals(str5));//第八行,true
        System.out.println(str3.equals(str4));//第九行,true
        System.out.println(str3.equals(str5));//第十行,true
    }
}
  • str1和str2在赋值时,使用的是字符串常量。Java虚拟机有一个常量池机制,它会直接把字符串常量放入常量池中,从而实现复用。
  • str3是用new关键字创建的,在Java中,new关键字代表创建一个新对象。因此str3指向的是一个全新的内存地址。(第三行代码,创建了几个对象?答案是一个或者两个,因为有用到new关键字,所以肯定会创建一个String对象,它的值是“abc”。但同时如果这个字符串在常量池中不存在,Jvm还会常量池中创建这个对象“abc”。
  • str5是用String类的intern()方法创建的。Jdk文档中对intern方法是这样描述的:“返回一个常量池中的固定对象。否则,就向常量中添加这个对象,并返回对它的引用”。

问题:String str1=“abc”;String str3=new String(“abc”);两个语句分别创建了几个对象?对象存储位置?s1和s2相等吗?执行顺序是先第一条再是第二条创建几个对象?

第一个语句一个,第二个语句一个或两个。
第一个语句存在常量池,第二个存在堆(若常量池没有,还会在常量池创建一个)。
(==)不相等,(equals())相等。
先第一条再是第二条的话,一共两个。

补充问题:String str = “ab” + “c”;创建了几个对象?
三个,“ab” , “c” , “abc” 。若之前有某一个了,就俩呗。

十八. interface和abstract的区别?

  1. interface一般是去定义规范or解耦用的,具体用法是implements这个接口;abstract一般是抽象类,比如说苹果和鸭梨都是水果这种共同的属性,具体用法是extends这个父类;(c++里也有这个,但java里的abstract不支持多继承,interface支持多继承(实现))

  2. 抽象类可以有构造方法,接口中不能有构造方法。

  3. 抽象类中可以有成员变量,接口中没有成员变量。

  4. 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的

十九. abstract类里一定要有抽象方法吗?
抽象类不一定有抽象方法;但是包含一个抽象方法的类一定是抽象类。(可能是继承父类的成员变量用吧)

二十. interface可以写方法的实现吗?
可以。jdk1.8后,可以在interface中写实现方法,前提是需要被default或static关键字所修饰。(当就算写实现方法也支持Override重写)

二十一. JRE 和 JDK 的区别是什么?
JRE: Java Runtime EnvironmentJDK:Java Development Kit。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具,还包含了java程序编写所需的文档和demo例子程序。如果你需要运行java程序,只需安装JRE就可以了。如果你需要编写java程序,需要安装JDK。

二十二. hashmap为什么不一开始用红黑树?
Because TreeNodes are about twice the size of regular nodes。只有当链表长度大于8时,才转换成红黑树来提高查询的效率。而当长度降到 6 就转换回去,这体现了时间和空间平衡的思想。同时红黑树插入和删除操作也需要耗时。

二十三. hashmap什么时候扩容?负载因子为什么0.75?
当HashMap中元素总个数达到阈值(bucket * 负载因子(一般为0.75)a时就会扩容2倍。
冲突的机会与空间利用率之间寻找一种平衡与折衷。0.75让其满足泊松分布(桶中元素到达8个的时候,概率已经变得非常小,也就是说用0.75作为加载因子,每个碰撞位置的链表长度超过8个是几乎不可能的。)

二十四. 死锁?
可抢占资源、不可抢占资源(eg. 打印机)
死锁的四个条件:

  1. Mutual exclusion condition 互斥使用
  2. Hold and wait condition 拥有并等待(部分拥有,期待更多)
  3. No preemption condition 不可抢夺
  4. Circular wait condition 循环等待

解决死锁(忽略、检测、避免、预防)

  • Just ignore the problem
    鸵鸟算法,假设不发生死锁
  • Detection and recovery.
    Detection: 矩阵、资源有向图. Recovery: 杀死年轻进程
  • Dynamic avoidance by careful resource allocation
    银行家算法(安全序列、安全状态)
  • Prevention by structurally negating one of the four required conditions.
    破坏死锁的条件1。进程把这个送到某个队列(spool),等待这个临界资源,相当于送到队列就认为任务完成。(eg. 打印机、送邮件)。该方法和鸵鸟常用广用,其他的无法确定或预期进程需要哪些资源。

具体场景下解决死锁:
解决循环等待: 线程t1先获取资源A然后获取资源B,而线程t2先获取资源B再获取资源A,这就导致了死锁。
控制加锁顺序:此场景下,我们可以让线程t2和线程t1 保持一致就可以解决死锁问题。

二十五. 线程安全的List

  • vector
  • Collections.SynchronizedList(new ArrayList<>())方法可以转换list为线程安全
  • CopyOnWriteArrayList,适合于读多写少,get读的时候不锁,写的时候加锁,进行复制,然后增加或替换,然后解锁。

二十六.问linux的时候提这俩java的

  • jstack:查看java线程的调用堆栈,可以看线程状态(那幅图 可运行阻塞)
  • jmap:查看堆内存,检查内存泄漏

二十七.什么语言使用了引用计数法?
脚本语言。Python、PHP
指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程。

二十八.泛型擦除
比如在集合中都是List,擦出了< T >

List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());

打印的结果为 true 是因为 List< String >和 List< Integer >在 jvm 中的 Class 都是 List.class。

这是因为,泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。

二十九.lambda表达式
()->{}。priorityqueue做排序,比如我们想倒序,正常是implements Comparator时,要覆写comapre方法,有lambda表达式后直接(x1,x2)->{return x2-x1;}

三十.Stream api
函数式编程,给容器加强聚合操作。我理解是整了一种流操作
容器.parallelStream().filter(过滤).sorted(排序).collect(收集还原成list)

三十一.基本数据类型
byte short char int long float double boolean

三十二.jvm调优
jmap、jstat查看堆内存信息
jstack看函数调用栈,检查死锁

Xms是初始堆的大小,Xmx堆的最大值,XX:SurvivorRatio,新生代中eden区和两个survivor的比例。

三十三.java中的异常有哪些
exception
空指针异常类:NullPointerException
输入输出异常:IOException
找不到类:java.lang.ClassNotFoundException

error
堆内存满:java.lang.OutOfMemoryError
迭代栈溢出:java.lang.StackOverflowError

三十四.collections和collection
Collections是工具类,如sort。Collection是集合的顶级接口,比如set和list的父接口就是它

JVM

一. 类加载过程,分几步?
加载、验证、准备、解析、初始化

1.补充一个为什么单例懒汉式必须加volatile关键字
double check后,instance = new SingletonClass()这条语句,并不是一个原子操作,在JVM中其实是3个操作

  1. 给instance分配空间、
  2. 调用 Singleton 的构造函数来初始化
  3. 将instance对象指向分配的内存空间(instance指向分配的内存空间后就不为null了)

在JVM中的及时编译存在指令重排序的优化,也就是说不能保证1,2,3执行的顺序。如果是 1-3-2,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错。volatile可以保证1、2、3的执行顺序

2.补充一个static和final赋值阶段

  • stiact属性:
    准备阶段,为类的静态变量(static)分配内存并设置初始值。
    初始化阶段,对static变量赋值,执行静态代码块。

  • final属性:
    final修饰的实例属性,在实例创建的时候才会赋值。

  • static+final属性:
    JVM规范是在初始化阶段赋值,但是HotSpot VM直接在准备阶段就赋值了

二. 类初始化的过程
原则:先静态
原则:先父后子
原则:先属性、再构造、再方法

父类的静态代码块(包括静态属性)
子类的静态代码块(包括静态属性)
初始化父类的属性值
父类的构造方法
父类的普通方法
初始化子类的属性值
子类的构造方法
子类的普通方法

三. JVM分成哪几部分?方法区放的什么?
类加载器、运行时数据区、字节码执行引擎
已被虚拟机加载的类的信息、常量、静态变量等

四. OOM内存泄露,出现原因,解决方法?(memory leak 最终会导致 out of memory)
出现原因:

  1. 分配的内存少,比如JVM本身可使用的内存太少
  2. 一些逻辑上不用的对象,用完没释放,或者被其他的对象给引用了,通过gc root可达性分析发现不是垃圾,造成大量对象挤满(内存泄漏)。
    解决方法:
  3. java.lang.OutOfMemoryError: Java heap space ——>java堆内存溢出:一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数设置-Xms,-Xmx等。
  4. java.lang.OutOfMemoryError: PermGen space ——>java永久代溢出:检查是否过多常量,或者通过更改方法区的大小来解决 ,-XX:MaxPermSize=256m。
  5. java.lang.StackOverflowError ——> 不会抛OOM error:检查死循环或者深度递归栈大小设置太小也会出现此种溢出,-Xss来设置。

五. 说一下JMM(java memory model)?

  • JMM是一种规范,目的是解决由于多线程通过共享内存进行通信时,存在的本地内存数据不一致、编译器会对代码指令重排序、处理器会对代码乱序执行等带来的问题。
  • Java内存模型规定了所有的变量存储在主内存中,每条线程还有自己的工作内存,线程的工作内存保存了该线程中是用到的变量的主内存副本拷贝线程对变量所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。
  • 还可以继续聊一聊:synchronized原子性,volatile可见性、有序性。

Spring

一. Spring和Springboot区别?

  1. Spring Boot是Spring框架的扩展,是一个微服务框架,简化了应用的开发和部署,而无需过多关注XML的配置。
  2. 嵌入Tomcat(和项目结合说一下)。

二. AOP?IoC?

  1. AOP面向切面编程。
    例子:不同的业务代码都需要日志(公共业务),单独被抽取出来(比如把某个公共业务模块化成一个类,此时就成了切面aspect),与业务代码分离,横切在核心业务代码之上or之下。这就是Spring AOP的强大之处,在运行时通过动态代理技术对UserService的add方法(这个方法就是连接点joinpoint)进行了增强,添加了记录日志的功能。

实现:类实现aop,比如这个log类,需要如implements MethodBeforeAdvice(也有之后,也有环绕)。并覆写方法。
然后在.xml文件中配置这个bean,并设置
(当然也可以不这样implements MethodBeforeAdvice,可以自定义类orLog类加注解实现aop,相应的配置文件也不一样了,注解实现的xml配置最简单)
2022秋招面经总结(补充前面文章中不含的内容)_第12张图片
下面是注解实现aop
2022秋招面经总结(补充前面文章中不含的内容)_第13张图片

  1. IoC控制反转(控制指谁来创建对象)
    new 变成 传字符串or接收bean对象,达到解耦目的。(反射机制)
    IoC的实现就是通过IoC容器,即BeanFactory。如果想把bean配置成多例,就设置一下scope(作用域) = “prototype”,Spring的bean默认是单例

三. Instantiation bean 实例化bean的方式?

  1. Instantiation with a Constructor(项目中entity那种最普通的)

  2. Instantiation with a Static Factory Method(自己写一个工厂单例类,并用单例返回)
    2022秋招面经总结(补充前面文章中不含的内容)_第14张图片

  3. Instantiation by Using an Instance Factory Method(factory本身作为bean,然后这样可以自己写工厂下多个返回单例方法)
    2022秋招面经总结(补充前面文章中不含的内容)_第15张图片

四. Spring依赖注入(dependency injection)方式?
(首先,依赖注入是另一个角度的控制反转。)
依赖:bean对象创建依赖于容器,bean对象的依赖资源(property给的字符串)等等
注入:bean对象依赖的资源由容器来设置和装配

  1. Constructor-based dependency injection(constructor-arg ref = “foo”)
  2. Setter-based dependency injection(property name=“dataSource” ref=“dataSource” or value=“张三”,注入的属性必须有set方法)
    a. 常量注入(value=“张三”)b. bean注入(ref=“dataSource”)c. 数组注入(array标签内value)d.List注入(list标签内value)…h. 空注入(标签为null)等等
  • Dependency Resolution Process 依赖解析过程
  1. ApplicationContext创建并根据配置元数据初始化。The ApplicationContext is created and initialized with configuration metadata that describes all the beans.

(spring的docs中间还有两条对属性或引用的设置的解释)

  1. spring会把字符串(format)转换成属性或者构造器参数的实际类型,把属性(properties)等信息在bean实际创建时注入

ApplicationContext是用来管理bean对象的。(如我们项目中的applicationContext.xml)。通过配置beans.xml(or applicationContext.xml)的property属性传值。然后bean这个类就会通过set方法注入这个值。(注意,属性property配置的是set方法的名字去掉set这三个字符,所以要命名规范!)

五. bean在什么时候被创建?

  • Beans that are singleton-scoped and set to be pre-instantiated (the default) are created when the container is created.
    默认Spring容器创建时就创建了bean

  • Otherwise, the bean is created only when it is requested.
    否则,只有在被请求时才创建

六. Spring循环依赖的解决方式?

  • 产生原因:
    If you use predominantly constructor injection, it is possible to create an unresolvable circular dependency scenario.
    如果主要使用构造函数注入,则有可能创建不可解决的循环依赖场景。

  • 解决方案:
    One possible solution is to edit the source code of some classes to be configured by setters rather than constructors.
    使用setter注入而不是构造器注入

七. Spring如何装载bean?

八. Spring bean生命周期?
2022秋招面经总结(补充前面文章中不含的内容)_第16张图片

九. MVC?
Model View Controller, 使用MVC是将M和V实现代码分离,从而使同一个程序可以使用不同的表现形式。最重要的一点是多个视图能共享一个模型(解耦,可重用

十. 静态代理?动态代理(与AOP联合说)?
References:https://www.zhihu.com/question/40536038/answer/685995295

  • 静态代理:
public interface Programmer {

    // 程序员每天都写代码
    void coding();

}

public class Java3y implements Programmer {

    @Override
    public void coding() {
        System.out.println("Java3y最新文章:......给女朋友讲解什么是代理模式.......");
    }
}

public class ProgrammerBigV implements Programmer {
    // 代理类
    // 指定程序员大V要让谁发文章(先发文章、后点赞)
    private Java3y java3y ;

    public ProgrammerBigV(Java3y java3y) {
        this.java3y = java3y;
    }

    // 程序员大V点赞评论收藏转发
    public void upvote() {
        System.out.println("程序员大V点赞评论收藏转发!");
    }

    @Override
    public void coding() {

        // 让Java3y发文章
        java3y.coding();

        // 程序员大V点赞评论收藏转发!
        upvote();
    }
	
	//......
	// 若有更多其他需要的功能,还需要Override多个方法,比如reading()、delete()里也要upvote()这个业务
}
public class Main {

    public static void main(String[] args) {

        // 想要发达的Java3y
        Java3y java3y = new Java3y();

        // 受委托程序员大V
        Programmer programmer = new ProgrammerBigV(java3y);

        // 受委托程序员大V让Java3y发文章,大V(自己)来点赞
        programmer.coding();
    }  
}
  • 动态代理:

动态代理的代理类是动态生成的。静态代理的代理类是提前写好的。
动态代理的好处:对于同一种业务,不需要Override各种方法,只需要在invoke里把我们的公共业务放进去(当然了不同的业务肯定要写不同的动态代理)

基于接口动态代理----jdk动态代理(Spring的AOP帮我们封装了动态代理
javassist
Proxy类和InvocationHandler接口
2022秋招面经总结(补充前面文章中不含的内容)_第17张图片
十一.@Resources和@Autowired?

  • @Resources按byName的方式进行注入,如果失败了则进行byType的方式进行注入,是JDK的
  • @Autowired按byType注入,如果是失败了再通过byName进行注入,是Spring的(与spring强耦合)

遇上两个bean的class相同,但是配置信息不同,那么怎么区分这两个bean呢???注入时注入的是哪个呢?这就可以利用@Resource了。把两个Bean取名不同,一个叫simpleTairConfig,一个叫simpleTair,这样在注入时通过@Resource(name = “simpleTairConfig”)

十二. spring事务、事务的传播属性

  • 编程式事务
  • 声明式事务:声明式事务管理建立在AOP之上。写xml配置文件 or 直接在类或public方法上写@Transactional注解

常见的事务传播属性(7种):

  • propagation_required
    Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行

计算机网络

一. HTTP(80)和HTTPS(443)?
HTTP协议运行在TCP之上,所有传输的内容都是明文,HTTPS运行在SSL/TLS之上SSL/TLS运行在TCP之上,所有传输的内容都经过加密的。就是在HTTP和TCP之间套了一层。
HTTPS 协议的主要功能基本都依赖于== TLS/SSL 协议==,TLS/SSL 的功能实现主要依赖于三类基本算法:散列函数 、对称加密和非对称加密,其利用非对称加密实现身份认证和密钥协商对称加密算法采用协商的密钥对数据加密,基于散列函数验证信息的完整性

2022秋招面经总结(补充前面文章中不含的内容)_第18张图片

二. 服务器保持了大量TIME_WAIT状态?
产生原因:TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说他本身就是“客户端”,在完成一个爬取任务之后,他就会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime)时间之后,彻底关闭回收资源。(即四次挥手中,服务器端也可以主动发起FIN=1)
高并发短连接的TCP服务器上,当服务器处理完请求后立刻主动正常关闭连接。持续的到达一定量的高并发短连接,会使服务器因端口资源不足而拒绝为一部分客户服务。

解决方法:让服务器能够快速回收和重用那些TIME_WAIT的资源。编辑内核文件/etc/sysctl.conf。开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接。开启 TCP连接中TIME-WAIT sockets的快速回收

三. 短连接与长连接?
短连接一般只会在client/server间传递一次读写操作,即一次完事儿后就close关闭了。长连接client与server完成一次读写之后,它们之间的连接并不会主动关闭,后续的读写操作会继续使用这个连接(在一个TCP连接上可以传送多个HTTP请求和响应,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点)。(HTTP/1.0默认非持久连接,如果想要在旧版本的 HTTP 协议上维持持久连接,设置Keep-Alive,相当于长连接了)。

  • 连接数较少或即时通讯情况一般用长连接
  • 高并发情况一般用短连接

四. HTTP1.0 HTTP1.1 HTTP2.0?HTTP/3了解吗?
HTTP1.0与HTTP1.1区别:4个

  1. HTTP1.0只支持短连接。而HTTP 1.1支持长连接
  2. HTTP1.0认为每台服务器都只能绑定一个IP地址,不支持虚拟网络。而HTTP1.1支持虚拟网络,请求和响应都支持Host头域(即一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。)
  3. HTTP1.1引入了更多的缓存控制策略
  4. HTTP1.1在请求头引入了range头域,它允许只请求资源的某个部分。

HTTP2.0与HTTP1.x区别:

  1. 增加header头压缩,HTTP1.x的header带有大量信息,造成头重身轻问题
  2. HTTP2.0选择了二进制格式,而HTTP1.x的解析是基于文本,提高解析效率
  3. HTTP2.0增加了多路复用,即一个连接上可以有多个请求(与长连接区别是:长连接是串行,会阻塞,而多路复用是并行,不会阻塞)

HTTP/3 是在 QUIC(Quick UDP Internet Connections) 基础上发展起来的,其底层使用UDP进行数据传输,上层仍然使用 HTTP/2。

五. url从输入到回车?

  1. DNS解析得到IP地址
  2. 建立TCP连接
  3. 发送HTTP请求
    (这一过程涉及到负载均衡,即反向代理。正向代理代理客户端,反向代理代理服务器。Nginx 是一个很强大的高性能Web和反向代理服务器,Nginx 既可以在内部直接支持 Rails。联系到了session)
  4. 服务器处理请求,然后发回响应
  5. 浏览器接收到响应后,显示渲染页面
  6. 客户端和服务器通过四次挥手终止 TCP 连接。

六. get请求和post请求

  1. 安全:一般认为POST在传输数据时更加的安全,因为GET传输时将在URL中显示参数,而POST的数据则放在了请求体(body),所以更安全。
  2. 数据量:POST传输数据较多,GET传输的数据比较少。
  3. 缓存角度:GET请求多是用来获取数据的,比如一件商品的信息、商品列表等,在一定时间内,比如商品的信息不会那么快的变化,因此它通常用于缓存。而POST请求多是用于将表单数据提交到服务端,比如修改姓名、修改密码等操作,需要浏览器必须发送请求到达服务器才能进行操作,因此POST请求不能被缓存
  4. TCP角度:GET会产生一个TCP数据包(发http header和data,服务端返回200),POST会产生两个TCP数据包(先发送http header,服务端返回100,再发送一个data,服务端返回200)。

七. 除了post和get还了解哪些http请求方法?
connect请求:connect请求告诉代理你要和哪个服务建立连接,代理给你返回200以后,你后续发送的所有流量才会被代理转发给你本来想要访问的服务器。(将服务器作为代理,让服务器代替用户进行访问。

八. TCP 是如何保证可靠性的?

  1. 使用确认和重传机制,用的是一个ARQ协议。也会超时重传
  2. 流量控制
  3. 拥塞控制
  4. 三次握手的SYN,ACK标志位以及seq序列号和ack的确认序号
  5. 校验和checksum:(这块不问就别说具体的了)伪首部、TCP报头、TCP数据为16位的字。伪首部pseudo-header是为了增加TCP校验和的检错能力。TCP的校验和是必需的,而UDP的校验和是可选的。

九. HTTP 是不保存状态的协议,如何保存用户状态?
HTTP 服务器并不保存关于客户机的任何信息,所以我们说 HTTP 是一个无状态协议

  1. 基于 Session实现的会话保持
    在服务端保存。在客户端第一次向服务器发送 HTTP 请求后,服务器会创建一个 Session 对象并将客户端的身份信息以键值对的形式存储下来,然后分配一个会话标识(SessionId)给客户端,这个会话标识一般保存在客户端 Cookie 中,之后每次该浏览器发送 HTTP 请求都会带上 Cookie 中的 SessionId 到服务器,服务器根据会话标识就可以将之前的状态信息与会话联系起来,从而实现会话保持。

优点:安全性高,因为状态信息保存在服务器端。

缺点:由于大型网站往往采用的是分布式服务器,浏览器发送的 HTTP 请求一般要先通过负载均衡器才能到达具体的后台服务器,倘若同一个浏览器两次 HTTP 请求分别落在不同的服务器上时,基于 Session 的方法就不能实现会话保持了。解决方法:采用中间件,例如 Redis,我们通过将 Session 的信息存储在 Redis 中,使得每个服务器都可以访问到之前的状态信息。

  1. 基于 Cookie 实现的会话保持
    客户端浏览器保存。服务器发送响应消息时,在 HTTP 响应头中设置 Set-Cookie 字段,用来存储客户端的状态信息。客户端解析出 HTTP 响应头中的字段信息,并根据其生命周期创建不同的 Cookie,这样一来每次浏览器发送 HTTP 请求的时候都会带上 Cookie 字段,从而实现状态保持。

优点:服务器不用保存状态信息, 减轻服务器存储压力,同时便于服务端做水平拓展。

缺点:该方式不够安全,因为状态信息存储在客户端,这意味着不能在会话中保存机密数据。除此之外,浏览器每次发起 HTTP 请求时都需要发送额外的 Cookie 到服务器端,会占用更多带宽

十. DNS 的作用和原理?
DNS将域名解析为ip地址
客户端(主机)把DNS查询报文发给代理商的本地DNS服务器,本地 DNS 服务器将该报文转发到根 DNS 服务器,根 DNS 服务器注意到查询的 IP 地址后缀为 edu 后向本地 DNS 服务器返回负责 edu 的顶级域名服务器的 IP 地址列表,该本地 DNS 服务器则再次向这些== 顶级域名服务器发送查询报文==,该顶级域名服务器注意到 mn.edu 的前缀,并用权威域名服务器的 IP 地址进行响应。最后找到了负责 def.mn.edu 的权威 DNS 服务器

十一. DNS用的什么传输协议?
DNS = UDP + 53 or TCP + 53

十二. URL(即统一资源定位符, Uniform Resource Locator)和URI
URI:定义资源是什么
URL:定义资源是什么,以及如何获取

十三. 状态码?
1XX:指示信息–表示请求正在处理
2XX:成功–表示请求已被成功处理完毕
3XX:重定向–要完成的请求需要进行附加操作
4XX:客户端错误–请求有语法错误或者请求无法实现,服务器无法处理请求
5XX:服务器端错误–服务器处理请求出现错误

十四. 状态码 301 和 302 的区别?
301 永久重定向30(2|3|7)临时重定向,语义和实现有略微区别;

十五. HTTP 异常状态码知道哪些?

  1. 400 语法错误(前端挨打)
  2. 403 拒绝访问
  3. 404 找不到资源
  4. 500 服务器错误(后端挨打,无法完成对请求的处理)
  5. 503 服务器宕机了(DevOps or IT 挨打)
  6. 505 服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本

十六.TCP如何保证有序
seq和小ack序列号保证(脑子里面想那幅图)

十七.TCP为什么四次挥手?为什么不是3次?
tcp是全双工通信,服务端和客服端都能发送和接收数据。tcp在断开连接时,需要服务端和客服端都确定对方将不再发送数据。(脑子里想那副图)

在客服端第1次挥手时,服务端可能还在发送数据。所以第2次挥手和第3次挥手不能合并。(和上面有联系)

十八. 为啥用udp?
TCP适合实时性要求不高,但要求内容要完整传输的应用。相比而言,UDP由于无连接、无重传确认,所以传输效率高、延时小,适合实时性要求高的应用

十九.udp如何实现可靠性传输?
tcp的可靠传输是在传输层实现的。udp可以在应用层实现可靠传输,把ack的确认机制、流量控制、拥塞控制、窗口设置等设置在传输层。

OS

一.详细聊聊进程和线程的区别?
进程,资源分配的最小单位
线程,调度的最小单位

进程的粒度大,体现在创建进程上下文切换开销。线程粒度小,在创建线程和上下文切换开销都较小。(进程资源最小单位,PCB、资源空间都大;同时这样也体现了上下文切换时候寄存器保护现场的内容)

linux进程之间通信用的方法和线程之间同步用的不一样。

进程中有一个或多个线程,是个包含关系。

二.线程的地址空间?
不太了解。知道线程栈这回事。

三.内存管理

  • 分区(内存碎片)
  • 分页(虚拟内存;逻辑地址,页表,物理地址;页面置换算法:FIFO先入先出、LRU等等)
  • 分段(解决分页没有逻辑内在,但又引入了分区管理的问题)
  • 段页管理

排序算法

一. Java 5亿整数大文件怎么排序?

1.分:按内存可承担的大小,将大文件分成一个一个的小文件,读入内存,在内存对该小文件进行排序,排序后放到磁盘。
2.和:外排序我们可以将这个“二”扩大到M。(内排序的归并排序是采用二路归并)。选择每个文件的第一个数读入内存(已经排序),找到min,写出磁盘。注意下一次选min需要先把写出的这个补齐了(所以要记录文件的索引,fd?)。

二. 排序算法?
冒泡排序:O(n2):基本有序情况下,冒泡最好,可以接近O(n)
简单选择排序:O(n2)
直接插入排序:O(n2)
希尔排序:O(nlogn)~O(n2)
堆排序:O(nlogn):最坏情况下,还能保证O(nlogn)
快速排序:O(nlogn):经过优化的快排,是最好的
归并排序:O(nlogn):最坏情况下,归并最好,还能保证O(nlogn),且稳定

参考文献

文中图片参考了B站视频、阿里开发者学院视频、其他知乎和CSDN博客等

你可能感兴趣的:(随笔记录,java,spring)