20230903 复盘
八股总结
get 和 post 区别
- get 的请求参数放在请求 URL 中,一般用来请求资源,请求的资源会被浏览器缓存
- post 用来向服务器推送资源,参数放在 requestbody 中,可以提交的数据类型更多
为什么 post 比 get 慢
post 需要在 body 中放数据,所以需要在请求中声明 content type
post 需要先给浏览器发送消息进行确认,然后才会真正发数据
post 更加安全,不会被缓存,数据不会放在 URL 中
post 能够发送的数据更大,get 受到 URL 长度限制
post 用于修改或者写入数据,get 用于提交或者筛选
post 请求中的 requestbody 长度受到内存限制,会先把 post 的参数加载进内存,如果长度过大,会造成内存溢出
浏览器输入 url 到显示主页的全过程
- 首先输入 url
- 域名解析:浏览器首先解析 url 中的域名部分,查找域名对应的 ip 地址,如果浏览器中的缓存中存了对应的地址,就直接使用,否则会想 DNS 服务器发送查询请求,获得对应的 IP 地址
- 建立了 TCP 连接:使用解析得到的 ip 地址,浏览器与服务器之间建立 tcp 连接(三握四挥)
- 发送 http 请求:请求包含 get post 等请求类型,目标路径,请求头等信息
- 服务端处理请求:服务器接受请求之后,根据请求参数,找到对应的资源,或者处理逻辑
- 服务器响应:处理完请求之后,会生成 http resp 包括状态码等
- 客户端接受响应:根据响应头的内容,处理响应数据(显示网页,下载文件)
- 渲染页面:根据 html,css,js 等来渲染页面
- 显示页面:当所有的资源都下载完毕,以及渲染完成之后,用户就可以看到完整的页面,并与之交互
- 处理请求和交互内容,用户页面进行交互后,可能会触发 js 异步请求,重新走一遍流程
http 和 https 的区别
安全性
http 采用的是明文传输协议,数据在传输过程中不进行加密,容易受到网络窃听或者数据纂改,用户名密码等容易泄露
https 通过 tls,ssl 协议对通信进行加密,数据在传输过程中被加密,降低了窃听或者纂改的危险
加密过程
http 不进行加密
Https 服务器使用公钥加密敏感数据,只有有私钥的服务器才能解密数据,即使数据被截获,攻击者也无法轻易破解
端口号
Http 默认 80 https 403
证书
http 没证书
Https 使用数字证书来验证服务器省份,证书由受信任的第三方证书颁布机构(CA)签发,用于确认服务器是合法的,浏览器会验证证书的有效性,防止中间人攻击
SEO
使用 http 协议会被一些搜索引擎标记为不安全
使用 https 协议可以提升网站在搜索引擎的排名
性能
https 由于需要加密解密 性能稍弱于 http
浮点数为什么不精确
二进制表示
数字由二进制表示,有些数比如 0.1 在二进制表示可能是无限循环的,由于计算机储存空间是有限的,无法容纳无限数量的位
有限精度
常见的单精度浮点数 通常是 32 位 用 1 位来表示符号,用 8 位表示指数,23 位来表示小数部分,限制了浮点数的精度
线程和进程的区别
资源管理
进程:
是一个独立执行的环境,有独立的内存空间,数据以及系统资源,每一个进程有独立的地址空间,无法访问其他内存的地址,隔离性
线程:
线程是进程中的执行单元,多个线程共享一个进程的内存空间和资源,线程之间的切换开销较小,因为共享同一块内存区域,不同的线程之间可能会互相影响
CPU 调度
进程:os 会为每一个进程分配一个时间片,进程之间的切换涉及到上下文的切换,需要保存和回复进程的状态信息
线程:多个线程共享进程的上下文,线程的切换只需要保存线程的部分状态,开销较小
通信机制
- 进程: 进程之间通信相对复杂,通常需要使用操作系统提供的 IPC(进程间通信)机制,如管道、消息队列、信号量、共享内存等。这些机制的设计旨在确保进程间数据的正确传递和同步。
- 线程: 线程间通信较为简单,因为它们共享相同的内存空间。然而,这也导致了需要额外的同步机制来防止多个线程访问共享数据时产生竞态条件等问题。
TCPIP 网络四层模型
链路层
(数据链路层,物理层)
控制物理介质上面的数据传输,处理硬件相关的细节,比如帧的发送接受,校验
- 代表协议:ppp
网络层
处理数据在网络中的数据传输(寻址),分组传输,数据包转发等
- 代表协议:ip,icmp
传输层
负责数据传输的可靠性和流量控制,为应用层提供端到端的通信
- 代表协议:TCP, UDP
应用层
提供应用程序与网络通信的接口,负责处理用户数据,应用层面的通信等
- 代表协议:http,ftp,smtp
TCP UDP
Tcp 可靠传输原理
停止等待协议 :每发送完一个分组之后就停止发送,等待对方的确认,确认之后才会继续发送下一个
- 超时重传如果超过这个时间,就认定没有收到,就重新发送
连续 ARQ 协议(滑动窗口)
- 发送方每收到一个确认 就把发送窗口往后移动一位,,
- 累计确认:在收到几个分组之后,对按序到达的最后一个分组发送确认,这样就认为之前的全部收到了,但是如果出现差错 就会回退 n 帧 go back to n
- 对于未按序到达的分组怎么处理?
- 先临时存在接受窗口中,等到字节流所缺少的字节收到之后,再按序交付到上层
流量控制
- 利用滑动窗口进行流量控制
- 拥塞控制
- 慢开始
- 拥塞避免
- 快重传 ——> 比如接收方收到 1, 2, 但是直接收到了 4 这个时候直接回复 2 发送方会受到 4 个 2 三个重复确认,就立马重传 3
- 快恢复
三握
记忆( a:你在吗 b:我在,请说 a: 那我开始说了)
过程 a : SYN= 1 (同步)
过程 b: SYN= 1 ACK= 1
过程 a: ACK = 1 SYN = 1
q:为什么三握不能改成两握(为什么需要二次确认?)
a:防止已经失效的连接请求报文突然到达,如果只有两次握手的话,有可能一些连接的请求因为网络阻塞,突然到达,而服务端可能会以为是新的连接请求,就会发送确认,b 就会认为新的连接请求已经建立
此时,a 由于并没有二次确认,不会理会这个请求,b 会一直等待 a 的数据,浪费了 b 的资源
四挥
记忆(a:我挂电话了 b:我知道了 b:那你挂吧 a:挂了)
过程 a:FIN = 1
过程 b: ACK = 1
过程 b:FIN = 1 ACK=1
过程 a:ACK=1
中间 service 层可能会有消息没有发完需要先确认收到 消息发完了 再终止连接
arrayList 扩容机制
arrayList 是一个可变的数组的集合框架
首先先初始化数组,容量为 n
当加入的元素容量不够的时候,就会发生扩容
先准备一个更大的空间的数组(默认每次扩容增加的空间是原来空间的一半)
然后再把数据从原来的数组复制进新的数组
最后修改引用,销毁旧数组
重载和重写的区别
重载是指在同一个类中,定义多个方法,方法名一样,但是方法的参数列表(参数类型,个数)不同,目的是让一个方法能够处理不同类型的参数,提供更加灵活的调用方式
重写是在子类中重新定义父类已经定义好的方法,子类需要继承父类的方法,且重新实现这个方法的时候,子类使用的方法名,参数列表,返回值需要和父类完全一样,重写的目的是实现多态性,使用上转型对象的时候,调用重写的方法,使用的是子类的方法
区别:
重载是在一个类中定义多个方法,参数列表都不相同,重写是子类重新定义父类已经存在的方法,子类可以代替父类对象,并在运行时动态选择方法实现
concurrentHashmap 线程安全如何保障
使用分段锁来实现线程安全,将内部结构分成一系列的段,每一段相当于一个小的 hash 表,各个段之间独立的加锁,这样多个线程就可以在不同的段上进行操作,提高并发性能
在读的时候不需要加锁,写操作的时候,只是锁定对应的段,而不是整个 hash 表,减小了锁的粒度
Java 8 以后引入红黑树
红黑树原理
自平衡的二叉查找树
每一个节点要么是红色,黑色
叶子节点都是黑色
红色节点的叶子节点必须是黑色
o(log n)
使用链表
java8 之后引入红黑树
为什么使用红黑树?
查询效率比链表更加高效,链表是 n 红黑树是 logn
hashmap 扩容
hashmap 有两个参数 容量和负载因子
当元素数量达到负载因子的时候就会触发扩容操作、
创建一个更大的 hash 表,把旧元素复制进 hash 表中(遍历旧 hash 表,同时重新计算 hash 值,插入到新的表中)
- 扩容方式:将当前容量翻倍,并找到下一个 2 的幂作为新的容量,保持 hash 码的均匀分布
异常和错误的区别
异常是程序执行过程中的非正常情况 可以通过代码来捕获或者处理,可以分为两种类型
已检查异常 编译的时候被检查 需要进行明确的处理 比如 IOException SQLException 这些异常可以被捕获并进行适当处理
未检查异常 (运行时异常) 这些异常不会被显示捕获 比如除以 0 数组越界
- NullPointerExecption 空指针异常
- IllegelArgumentExecption 传递给方法的参数不合法
- ArrayIndexException 数组越界
错误是程序执行过程中遇到的严重问题,无法通过代码来处理(内存不足,虚拟机崩溃等)
Cookie 和 session 的区别
cookie 是采用在客户端保持状态的方案,session 是采用该在服务器端保持状态的方案
常见的状态码
200 成功
301 请求的资源永久移动到新的 url
302 请求的资源临时移动到 url http1 已被 303、307 替代
400 客户端请求语法错误
401 身份验证失败
403 权限不足
404 服务器不存在资源
500 服务器内部错误
ipv4 最多服务的主机
2^32 次方
地址耗尽
NAT 地址转换方案 多个主机共用一个公网 ip
虚拟内存
通过物理内存和磁盘内存结合,为每一个进程提供一个抽象的,似乎是连续的内存空间,进程都拥有自己独立的内存空间,不需要关心物理内存的分布和限制
虚拟内存的布局
- 代码段:存放程序可执行的代码,通常是只读
- 数据段:存放全部变量和静态变量,可读写
- 堆:用于动态分配内存,可读可写
- 栈:用于存放函数调用和局部变量,可读可写
- 内核空间:保留给操作系统内核使用,不可访问
寄存器,缓存,内存区别
寄存器
是 CPU 直接拿他里面寄存的东西进行计算操作的,是 CPU 的一部分。
缓存
为了避免数据多次从一个地方移动到另一个地方,从而在通过的中间划分出一个区域作为临时存放数据的地址,是一种数据中转的方式
内存
存放我们需要处理的一些数据,都会全部先放在内存中,然后等使用的时候,寄存器会从缓存或者内存中得到数据
创建 string 方式
- 字面量方式:String str = “hello”
- 构造函数方式:String str = new String(“hello”)
- 字符串拼接
- 从 stringBuilder, stringBuffer 拼接
什么时候用到字符串池
创建 String,会先检查,如果有就直接给引用,如果没有就先在串池中创建字符串。。。。