1. 键入一个网站,会发生什么?

图片

  • 解析URL:分析 URL 所需要使用的传输协议和请求的资源路径。如果输入的 URL 中的协议或者主机名不合法,将会把地址栏中输入的内容传递给搜索引擎。如果没有问题,浏览器会检查 URL 中是否出现了非法字符,则对非法字符进行转义后在进行下一过程。

  • 缓存判断:浏览器缓存 → 系统缓存(hosts 文件) → 路由器缓存 → ISP 的 DNS 缓存,如果其中某个缓存存在,直接返回服务器的IP地址。

  • DNS解析:如果缓存未命中,浏览器向本地 DNS 服务器发起请求,最终可能通过根域名服务器、顶级域名服务器(.com)、权威域名服务器逐级查询,直到获取目标域名的 IP 地址。

  • 获取MAC地址:当浏览器得到 IP 地址后,数据传输还需要知道目的主机 MAC 地址,因为应用层下发数据给传输层,TCP 协议会指定源端口号和目的端口号,然后下发给网络层。网络层会将本机地址作为源地址,获取的 IP 地址作为目的地址。然后将下发给数据链路层,数据链路层的发送需要加入通信双方的 MAC 地址,本机的 MAC 地址作为源 MAC 地址,目的 MAC 地址需要分情况处理。通过将 IP 地址与本机的子网掩码相结合,可以判断是否与请求主机在同一个子网里,如果在同一个子网里,可以使用 APR 协议获取到目的主机的 MAC 地址,如果不在一个子网里,那么请求应该转发给网关,由它代为转发,此时同样可以通过 ARP 协议来获取网关的 MAC 地址,此时目的主机的 MAC 地址应该为网关的地址。

  • 建立TCP连接:主机将使用目标 IP地址和目标MAC地址发送一个TCP SYN包,请求建立一个TCP连接,然后交给路由器转发,等路由器转到目标服务器后,服务器回复一个SYN-ACK包,确认连接请求。然后,主机发送一个ACK包,确认已收到服务器的确认,然后 TCP 连接建立完成。

  • HTTPS 的 TLS 四次握手:如果使用的是 HTTPS 协议,在通信前还存在 TLS 的四次握手。

  • 发送HTTP请求:连接建立后,浏览器会向服务器发送HTTP请求。请求中包含了用户需要获取的资源的信息,例如网页的URL、请求方法(GET、POST等)等。

  • 服务器处理请求并返回响应:服务器收到请求后,会根据请求的内容进行相应的处理。例如,如果是请求网页,服务器会读取相应的网页文件,并生成HTTP响应。

2. DNS域名怎么解析?

图片

  1. 客户端首先会发出一个 DNS 请求,问 www.server.com 的 IP 是啥,并发给本地 DNS 服务器(也就是客户端的 TCP/IP 设置中填写的 DNS 服务器地址)。

  2. 本地域名服务器收到客户端的请求后,如果缓存里的表格能找到 www.server.com,则它直接返回 IP 地址。如果没有,本地 DNS 会去问它的根域名服务器:“老大, 能告诉我 www.server.com 的 IP 地址吗?” 根域名服务器是最高层次的,它不直接用于域名解析,但能指明一条道路。

  3. 根 DNS 收到来自本地 DNS 的请求后,发现后置是 .com,说:“www.server.com 这个域名归 .com 区域管理”,我给你 .com 顶级域名服务器地址给你,你去问问它吧。”

  4. 本地 DNS 收到顶级域名服务器的地址后,发起请求问“老二, 你能告诉我 www.server.com 的 IP 地址吗?”

  5. 顶级域名服务器说:“我给你负责 www.server.com 区域的权威 DNS 服务器的地址,你去问它应该能问到”。

  6. 本地 DNS 于是转向问权威 DNS 服务器:“老三,www.server.com对应的IP是啥呀?” server.com 的权威 DNS 服务器,它是域名解析结果的原出处。为啥叫权威呢?就是我的域名我做主。

  7. 权威 DNS 服务器查询后将对应的 IP 地址 X.X.X.X 告诉本地 DNS。

  8. 本地 DNS 再将 IP 地址返回客户端,客户端和目标建立连接。

至此,我们完成了 DNS 的解析过程

3. HTTP状态码有哪些?对应什么含义?

图片

HTTP 状态码分为 5 大类

  • 1xx 类状态码属于提示信息,是协议处理中的一种中间状态,实际用到的比较少。

  • 2xx 类状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态。

  • 3xx 类状态码表示客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向

  • 4xx 类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。

  • 5xx 类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。

其中常见的具体状态码有:

  • 200:请求成功;

  • 301:永久重定向;302:临时重定向;

  • 404:无法找到此页面;405:请求的方法类型不支持;

  • 500:服务器内部出错。

4. TCP为什么是四次挥手而不是三次?

图片

具体过程:

  • 客户端主动调用关闭连接的函数,于是就会发送 FIN 报文,这个  FIN 报文代表客户端不会再发送数据了,进入 FIN_WAIT_1 状态;

  • 服务端收到了 FIN 报文,然后马上回复一个 ACK 确认报文,此时服务端进入 CLOSE_WAIT 状态。在收到 FIN 报文的时候,TCP 协议栈会为 FIN 包插入一个文件结束符 EOF 到接收缓冲区中,服务端应用程序可以通过 read 调用来感知这个 FIN 包,这个 EOF 会被放在已排队等候的其他已接收的数据之后,所以必须要得继续 read 接收缓冲区已接收的数据;

  • 接着,当服务端在 read 数据的时候,最后自然就会读到 EOF,接着 read() 就会返回 0,这时服务端应用程序如果有数据要发送的话,就发完数据后才调用关闭连接的函数,如果服务端应用程序没有数据要发送的话,可以直接调用关闭连接的函数,这时服务端就会发一个 FIN 包,这个  FIN 报文代表服务端不会再发送数据了,之后处于 LAST_ACK 状态;

  • 客户端接收到服务端的 FIN 包,并发送 ACK 确认包给服务端,此时客户端将进入 TIME_WAIT 状态;

  • 服务端收到 ACK 确认包后,就进入了最后的 CLOSE 状态;

  • 客户端经过 2MSL 时间之后,也进入 CLOSE 状态;

服务器收到客户端的 FIN 报文时,内核会马上回一个 ACK 应答报文,但是服务端应用程序可能还有数据要发送,所以并不能马上发送 FIN 报文,而是将发送 FIN 报文的控制权交给服务端应用程序

  • 如果服务端应用程序有数据要发送的话,就发完数据后,才调用关闭连接的函数;

  • 如果服务端应用程序没有数据要发送的话,可以直接调用关闭连接的函数,

从上面过程可知,是否要发送第三次挥手的控制权不在内核,而是在被动关闭方(上图的服务端)的应用程序,因为应用程序可能还有数据要发送,由应用程序决定什么时候调用关闭连接的函数,当调用了关闭连接的函数,内核就会发送 FIN 报文了,所以服务端的 ACK 和 FIN 一般都会分开发送。

5. 抽象类和接口有什么区别是什么?

两者的特点:

  • 抽象类用于描述类的共同特性和行为,可以有成员变量、构造方法和具体方法。适用于有明显继承关系的场景。

  • 接口用于定义行为规范,可以多实现,只能有常量和抽象方法(Java 8 以后可以有默认方法和静态方法)。适用于定义类的能力或功能。

两者的区别:

  • 实现方式:实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。

  • 方法方式:接口只有定义,不能有方法的实现,java 1.8中可以定义default方法体,而抽象类可以有定义与实现,方法可在抽象类中实现。

  • 访问修饰符:接口成员变量默认为public static final,必须赋初值,不能被修改;其所有的成员方法都是public、abstract的。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;抽象方法被abstract修饰,不能被private、static、synchronized和native等修饰,必须以分号结尾,不带花括号。

  • 变量:抽象类可以包含实例变量和静态变量,而接口只能包含常量(即静态常量)。

6. ConcurrentHashMap 和hashMap区别是什么?

JDK1.7版本:hashmap和concurrenthashmap的区别

  • 内存结构:hashmap采用数组 + 链表的结构。数组是 HashMap 的主体,链表则是为了解决哈希冲突而存在。当两个不同的键通过哈希函数计算出相同的数组索引时,它们会以链表的形式存储在该索引位置。**ConcurrentHashMap**采用分段锁机制,内部结构是一个 Segment 数组,每个 Segment 类似于一个小的 HashMap,它也有自己的数组和链表结构。

  • 线程安全性:hashmap是非线程安全的,在多线程环境下,如果多个线程同时对 HashMap 进行读写操作,可能会导致数据不一致、死循环等问题。**ConcurrentHashMap**是线程安全的。每个 Segment 都有自己的锁,不同的 Segment 可以被不同的线程同时访问,因此在多线程环境下可以提高并发性能,只有当多个线程同时访问同一个 Segment 时,才会发生锁竞争。

  • 性能:hashmap由于没有锁的开销,在单线程环境下性能较好。但在多线程环境下,为了保证线程安全,需要额外的同步机制,这会降低性能。**ConcurrentHashMap**通过分段锁机制,在多线程环境下可以实现更高的并发性能,不同的线程可以同时访问不同的 Segment,从而减少了锁竞争的可能性。

JDK1.8版本:hashmap和concurrenthashmap的区别

  • 内存结构:hashmap采用数组 + 链表 + 红黑树的结构,当链表长度超过一定阈值(默认为 8)时,链表会转换为红黑树,以提高查找效率。**ConcurrentHashMap**弃了分段锁机制,采用 CAS + synchronized 来保证线程安全,内部结构同样是数组 + 链表 + 红黑树。

  • 线程安全性:hashmap仍然是非线程安全的,多线程环境下的问题依然存在。**ConcurrentHashMap**通过 CAS 和 synchronized 保证线程安全。在插入元素时,首先会尝试使用 CAS 操作更新节点,如果 CAS 失败,则使用 synchronized 锁住当前节点,再进行插入操作。

  • 性能:hashmap在单线程环境下,由于红黑树的引入,当链表较长时查找效率会有所提高。**ConcurrentHashMap**在多线程环境下,由于摒弃了分段锁,减少了锁的粒度,进一步提高了并发性能。同时,红黑树的引入也提高了查找效率。

7. HashSet的底层实现是什么?

HashSet的底层实现基于HashMap

图片

具体来说,HashSet内部维护了一个HashMap实例,当你向HashSet中添加元素时,实际上是将该元素作为HashMapkey存储,而value则是一个固定的Object对象(PRESENT)。

这种实现方式利用了HashMap的特性:

  • HashMapkey不允许重复,这保证了HashSet中元素的唯一性

  • 基于哈希表的存储结构,使得HashSet具有 O (1) 平均时间复杂度的添加、删除和查找操作

  • 元素的存储顺序不保证,取决于哈希值的计算结果

8. MySQL有什么函数?

一、字符串函数

CONCAT(str1, str2, ...):连接多个字符串,返回一个合并后的字符串。

SELECT CONCAT('Hello', ' ', 'World') AS Greeting;

LENGTH(str):返回字符串的长度(字符数)。

SELECT LENGTH('Hello') AS StringLength;

SUBSTRING(str, pos, len):从指定位置开始,截取指定长度的子字符串。

SELECT SUBSTRING('Hello World', 1, 5) AS SubStr;

REPLACE(str, from_str, to_str):将字符串中的某部分替换为另一个字符串。

SELECT REPLACE('Hello World', 'World', 'MySQL') AS ReplacedStr;

二、数值函数

ABS(num):返回数字的绝对值。

SELECT ABS(-10) AS AbsoluteValue;

POWER(num, exponent):返回指定数字的指定幂次方。

SELECT POWER(2, 3) AS PowerValue;

三、日期和时间函数

NOW():返回当前日期和时间。

SELECT NOW() AS CurrentDateTime;

CURDATE():返回当前日期。

SELECT CURDATE() AS CurrentDate;

四、聚合函数

COUNT(column):计算指定列中的非NULL值的个数。

SELECT COUNT(*) AS RowCount FROM my_table;

SUM(column):计算指定列的总和。

SELECT SUM(price) AS TotalPrice FROM orders;

AVG(column):计算指定列的平均值。

SELECT AVG(price) AS AveragePrice FROM orders;

MAX(column):返回指定列的最大值。

SELECT MAX(price) AS MaxPrice FROM orders;

MIN(column):返回指定列的最小值。

SELECT MIN(price) AS MinPrice FROM orders;

9. SpringBoot启动的过程是怎样的?

SpringBoot是一个服务Spring框架的框架,能够简化配置文件,快速构建web应用,内置tomcat,无需打包部署,直接运行。 当我们启动一个SpringBoot应用的时候,都会用到如下的启动类:

@SpringBootApplication
public class Application {
     public static void main(String[] args) {
         SpringApplication.run(Application.class, args);
     }

只要加上@SpringBootApplication,然后执行run()方法,就可以启动一个应用程序,启动的流程如下:

  1. 首先从main找到run()方法,在执行run()方法之前new一个SpringApplication对象

  2. 进入run()方法,创建应用监听器SpringApplicationRunListeners开始监听

  3. 然后加载SpringBoot配置环境(ConfigurableEnvironment),然后把配置环境(Environment)加入监听对象中

  4. 然后加载应用上下文(ConfigurableApplicationContext),当做run方法的返回对象

  5. 最后创建Spring容器,refreshContext(context),实现starter自动化配置和bean的实例化等工作。

10. 项目

  • 选一个最有挑战的项目,遇到最有挑战的是什么,怎么解决的,怎么分析

11. 手撕算法

  • 压缩字符串

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐