From 61ee24f6e2156fd5f53188eb9611c991aadd2c50 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 16 Jun 2020 18:08:27 +0800 Subject: [PATCH 01/27] =?UTF-8?q?Create=20=E6=8A=80=E6=9C=AF=E9=9D=A2?= =?UTF-8?q?=E8=AF=95=E9=9C=80=E8=A6=81=E6=8E=8C=E6=8F=A1=E7=9A=84=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E7=9F=A5=E8=AF=86=E6=95=B4=E7=90=86.MD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...45\350\257\206\346\225\264\347\220\206.MD" | 4557 +++++++++++++++++ 1 file changed, 4557 insertions(+) create mode 100644 "Interview/\346\212\200\346\234\257\351\235\242\350\257\225\351\234\200\350\246\201\346\216\214\346\217\241\347\232\204\345\237\272\347\241\200\347\237\245\350\257\206\346\225\264\347\220\206.MD" diff --git "a/Interview/\346\212\200\346\234\257\351\235\242\350\257\225\351\234\200\350\246\201\346\216\214\346\217\241\347\232\204\345\237\272\347\241\200\347\237\245\350\257\206\346\225\264\347\220\206.MD" "b/Interview/\346\212\200\346\234\257\351\235\242\350\257\225\351\234\200\350\246\201\346\216\214\346\217\241\347\232\204\345\237\272\347\241\200\347\237\245\350\257\206\346\225\264\347\220\206.MD" new file mode 100644 index 0000000..3496d07 --- /dev/null +++ "b/Interview/\346\212\200\346\234\257\351\235\242\350\257\225\351\234\200\350\246\201\346\216\214\346\217\241\347\232\204\345\237\272\347\241\200\347\237\245\350\257\206\346\225\264\347\220\206.MD" @@ -0,0 +1,4557 @@ + +技术面试知识点整理【目录】 +================= + +>**说明**: +> +>**1. 本文较长共整合27个专题,GitHub用户如果访问的比较慢的话,可以点击下面的链接进群下载本地PDF文件,或者继续在GitHub在线阅读** +> +>**2. [点击此处进群免费获取PDF版本](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +> +>**3. 目前还只同步部分内容至GitHub,后续会慢慢同步上来,可放心`Ctrl+D`** + +*** + + * [一、校招真题题解](#一校招真题题解) + * [前言](#前言) + * [1. 小米-小米Git](#1-小米-小米git) + * [2. 小米-懂二进制](#2-小米-懂二进制) + * [3. 小米-中国牛市](#3-小米-中国牛市) + * [4. 微软-LUCKY STRING](#4-微软-lucky-string) + * [5. 微软-Numeric Keypad](#5-微软-numeric-keypad) + * [6. 微软-Spring Outing](#6-微软-spring-outing) + * [7. 华为-最高分是多少](#7-华为-最高分是多少) + * [8. 华为-简单错误记录](#8-华为-简单错误记录) + * [9. 华为-扑克牌大小](#9-华为-扑克牌大小) + * [10. 去哪儿-二分查找](#10-去哪儿-二分查找) + * [11. 去哪儿-首个重复字符](#11-去哪儿-首个重复字符) + * [12. 去哪儿-寻找Coder](#12-去哪儿-寻找coder) + * [13. 美团-最大差值](#13-美团-最大差值) + * [14. 美团-棋子翻转](#14-美团-棋子翻转) + * [15. 美团-拜访](#15-美团-拜访) + * [16. 美团-直方图内最大矩形](#16-美团-直方图内最大矩形) + * [17. 美团-字符串计数](#17-美团-字符串计数) + * [18. 美团-平均年龄](#18-美团-平均年龄) + * [19. 百度-罪犯转移](#19-百度-罪犯转移) + * [20. 百度-裁减网格纸](#20-百度-裁减网格纸) + * [21. 百度-钓鱼比赛](#21-百度-钓鱼比赛) + * [22. 百度-蘑菇阵](#22-百度-蘑菇阵) + * [二、计算机网络](#二计算机网络) + * [(一)、概述](#一概述) + * [1. 网络的网络](#1-网络的网络) + * [2. ISP](#2-isp) + * [3. 互联网的组成](#3-互联网的组成) + * [4. 主机之间的通信方式](#4-主机之间的通信方式) + * [5. 电路交换与分组交换](#5-电路交换与分组交换) + * [⑴. 电路交换](#-电路交换) + * [⑵. 报文交换](#-报文交换) + * [⑶. 分组交换](#-分组交换) + * [6. 时延](#6-时延) + * [⑴. 发送时延](#-发送时延) + * [⑵. 传播时延](#-传播时延) + * [⑶. 处理时延](#-处理时延) + * [⑷. 排队时延](#-排队时延) + * [7. 计算机网络体系结构*](#7-计算机网络体系结构) + * [⑴. 七层协议](#-七层协议) + * [⑵. 五层协议](#-五层协议) + * [⑶. 数据在各层之间的传递过程](#-数据在各层之间的传递过程) + * [⑷. TCP/IP 体系结构](#-tcpip-体系结构) + * [(二)、物理层](#二物理层) + * [1. 通信方式](#1-通信方式) + * [2. 带通调制](#2-带通调制) + * [3. 信道复用技术](#3-信道复用技术) + * [⑴. 频分复用、时分复用](#-频分复用时分复用) + * [⑵. 统计时分复用](#-统计时分复用) + * [⑶. 波分复用](#-波分复用) + * [⑷. 码分复用](#-码分复用) + * [(三)、数据链路层](#三数据链路层) + * [1. 信道分类](#1-信道分类) + * [2. 三个基本问题](#2-三个基本问题) + * [⑴. 封装成帧](#-封装成帧) + * [⑵. 透明传输](#-透明传输) + * [⑶. 差错检测](#-差错检测) + * [3. 局域网](#3-局域网) + * [4. PPP 协议](#4-ppp-协议) + * [5. CSMA/CD 协议*](#5-csmacd-协议) + * [6. 扩展局域网*](#6-扩展局域网) + * [⑴. 在物理层进行扩展](#-在物理层进行扩展) + * [⑵. 在链路层进行扩展](#-在链路层进行扩展) + * [⑶. 虚拟局域网](#-虚拟局域网) + * [7. MAC 层*](#7-mac-层) + * [(四)、网络层](#四网络层) + * [1. 网际协议 IP 概述](#1-网际协议-ip-概述) + * [2. IP 数据报格式](#2-ip-数据报格式) + * [3. IP 地址编址方式](#3-ip-地址编址方式) + * [⑴. 分类](#-分类) + * [⑵. 子网划分](#-子网划分) + * [⑶. 无分类](#-无分类) + * [4. IP 地址和 MAC 地址](#4-ip-地址和-mac-地址) + * [5. 地址解析协议 ARP](#5-地址解析协议-arp) + * [6. 路由器的结构](#6-路由器的结构) + * [7. 路由器分组转发流程](#7-路由器分组转发流程) + * [8. 路由选择协议](#8-路由选择协议) + * [⑴. 内部网关协议 RIP](#-内部网关协议-rip) + * [⑵. 内部网关协议 OSPF](#-内部网关协议-ospf) + * [⑶. 外部网关协议 BGP](#-外部网关协议-bgp) + * [9. 网际控制报文协议 ICMP](#9-网际控制报文协议-icmp) + * [10. 分组网间探测 PING](#10-分组网间探测-ping) + * [11. 虚拟专用网 VPN](#11-虚拟专用网-vpn) + * [12. 网络地址转换 NAT](#12-网络地址转换-nat) + * [(五)、传输层](#五传输层) + * [1. UDP 和 TCP 的特点](#1-udp-和-tcp-的特点) + * [2. UDP 首部格式](#2-udp-首部格式) + * [3. TCP 首部格式](#3-tcp-首部格式) + * [4. TCP 的三次握手](#4-tcp-的三次握手) + * [5. TCP 的四次挥手](#5-tcp-的四次挥手) + * [6. TCP 滑动窗口](#6-tcp-滑动窗口) + * [7. TCP 可靠传输](#7-tcp-可靠传输) + * [8. TCP 流量控制](#8-tcp-流量控制) + * [9. TCP 拥塞控制](#9-tcp-拥塞控制) + * [10. 慢开始与拥塞避免](#10-慢开始与拥塞避免) + * [11. 快重传与快恢复](#11-快重传与快恢复) + * [(六)、应用层](#六应用层) + * [1. 域名系统 DNS](#1-域名系统-dns) + * [⑴. 层次结构](#-层次结构) + * [⑵. 解析过程](#-解析过程) + * [2. 文件传输协议 FTP](#2-文件传输协议-ftp) + * [3. 远程终端协议 TELNET](#3-远程终端协议-telnet) + * [4. 电子邮件协议](#4-电子邮件协议) + * [⑴. POP3](#-pop3) + * [⑵. IMAP](#-imap) + * [⑶. SMTP](#-smtp) + * [5. 动态主机配置协议 DHCP](#5-动态主机配置协议-dhcp) + * [6. 点对点传输 P2P](#6-点对点传输-p2p) + * [7. Web 页面请求过程](#7-web-页面请求过程) + * [⑴. DHCP 配置主机信息](#-dhcp-配置主机信息) + * [⑵. ARP 解析 MAC 地址](#-arp-解析-mac-地址) + * [⑶. DNS 解析域名](#-dns-解析域名) + * [⑷. HTTP 请求页面](#-http-请求页面) + * [8. 常用端口](#8-常用端口) + * [三、HTTP](#三http) + * [(一)、基本概念](#一基本概念) + * [1. Web 基础](#1-web-基础) + * [2. URL](#2-url) + * [3. 请求和响应报文](#3-请求和响应报文) + * [⑴. 请求报文](#-请求报文) + * [⑵. 响应报文](#-响应报文) + * [(二)、HTTP方法](#二http方法) + * [1. GET](#1-get) + * [2. POST](#2-post) + * [3. HEAD](#3-head) + * [4. PUT](#4-put) + * [5. PATCH](#5-patch) + * [6. DELETE](#6-delete) + * [7. OPTIONS](#7-options) + * [8. CONNECT](#8-connect) + * [9. TRACE](#9-trace) + * [(三)、HTTP状态码](#三http状态码) + * [1. 2XX 成功](#1-2xx-成功) + * [2. 3XX 重定向](#2-3xx-重定向) + * [3. 4XX 客户端错误](#3-4xx-客户端错误) + * [4. 5XX 服务器错误](#4-5xx-服务器错误) + * [(四)、HTTP首都](#四http首都) + * [1. 通用首部字段](#1-通用首部字段) + * [2. 请求首部字段](#2-请求首部字段) + * [3. 响应首部字段](#3-响应首部字段) + * [4. 实体首部字段](#4-实体首部字段) + * [(五)、具体应用](#五具体应用) + * [1. Cookie](#1-cookie) + * [⑴. 创建过程](#-创建过程) + * [⑵. Set-Cookie](#-set-cookie) + * [⑶. Session 和 Cookie 区别](#-session-和-cookie-区别) + * [⑷. 浏览器禁用 Cookie 的情况](#-浏览器禁用-cookie-的情况) + * [⑸. 使用 Cookie 实现用户名和密码的自动填写](#-使用-cookie-实现用户名和密码的自动填写) + * [2. 缓存](#2-缓存) + * [⑴. 优点](#-优点) + * [⑵. 实现方法](#-实现方法) + * [⑶. Cache-Control 字段](#-cache-control-字段) + * [⑷. no-cache 指令](#-no-cache-指令) + * [⑸. no-store 指令](#-no-store-指令) + * [⑹. max-age 指令](#-max-age-指令) + * [3. 持久连接](#3-持久连接) + * [4. 编码](#4-编码) + * [5. 分块传输](#5-分块传输) + * [6. 多部分对象集合](#6-多部分对象集合) + * [7. 范围请求](#7-范围请求) + * [8. 内容协商](#8-内容协商) + * [9. 虚拟主机](#9-虚拟主机) + * [10. 通信数据转发](#10-通信数据转发) + * [⑴. 代理](#-代理) + * [⑵. 网关](#-网关) + * [⑶. 隧道](#-隧道) + * [(六)、HTPPs](#六htpps) + * [1. 加密](#1-加密) + * [⑴. 对称密钥](#-对称密钥) + * [⑵. 公开密钥](#-公开密钥) + * [⑶. HTTPs 采用的加密方式](#-https-采用的加密方式) + * [2. 认证](#2-认证) + * [3. 完整性](#3-完整性) + * [(七)、Web攻击技术](#七web攻击技术) + * [1. 攻击模式](#1-攻击模式) + * [⑴. 主动攻击](#-主动攻击) + * [⑵. 被动攻击](#-被动攻击) + * [2. 跨站脚本攻击](#2-跨站脚本攻击) + * [⑴. 概念](#-概念) + * [⑵. 危害](#-危害) + * [⑶. 防范手段](#-防范手段) + * [3. SQL 注入攻击](#3-sql-注入攻击) + * [⑴. 概念](#-概念-1) + * [⑵. 攻击原理](#-攻击原理) + * [⑶. 危害](#-危害-1) + * [⑷. 防范手段](#-防范手段-1) + * [4. 跨站点请求伪造](#4-跨站点请求伪造) + * [⑴. 概念](#-概念-2) + * [⑵. 防范手段](#-防范手段-2) + * [5. 拒绝服务攻击](#5-拒绝服务攻击) + * [⑴. 概念](#-概念-3) + * [(八)、各版本比较](#八各版本比较) + * [1. HTTP/1.0 与 HTTP/1.1 的区别](#1-http10-与-http11-的区别) + * [2. HTTP/1.1 与 HTTP/2.0 的区别](#2-http11-与-http20-的区别) + * [⑴. 多路复用](#-多路复用) + * [⑵. 首部压缩](#-首部压缩) + * [⑶. 服务端推送](#-服务端推送) + * [⑷. 二进制格式](#-二进制格式) + * [四、操作系统](#四操作系统) + * [(一)、概述](#一概述-1) + * [1. 操作系统基本特征](#1-操作系统基本特征) + * [⑴. 并发](#-并发) + * [⑵. 共享](#-共享) + * [⑶. 虚拟](#-虚拟) + * [⑷. 异步](#-异步) + * [2. 操作系统基本功能](#2-操作系统基本功能) + * [⑴. 进程管理](#-进程管理) + * [⑵. 内存管理](#-内存管理) + * [⑶. 文件管理](#-文件管理) + * [⑷. 设备管理](#-设备管理) + * [3. 系统调用](#3-系统调用) + * [4. 大内核和微内核](#4-大内核和微内核) + * [⑴. 大内核](#-大内核) + * [⑵. 微内核](#-微内核) + * [5. 中断分类](#5-中断分类) + * [6. 外中断](#6-外中断) + * [7. 异常](#7-异常) + * [8. 陷入](#8-陷入) + * [(二)、进程管理](#二进程管理) + * [1. 进程与线程](#1-进程与线程) + * [⑴. 进程](#-进程) + * [⑵. 线程](#-线程) + * [⑶. 区别](#-区别) + * [2. 进程状态的切换](#2-进程状态的切换) + * [3. 调度算法](#3-调度算法) + * [⑴. 批处理系统中的调度](#-批处理系统中的调度) + * [①. 先来先服务](#-先来先服务) + * [②. 短作业优先](#-短作业优先) + * [③. 最短剩余时间优先](#-最短剩余时间优先) + * [⑵. 交互式系统中的调度](#-交互式系统中的调度) + * [①. 优先级调度](#-优先级调度) + * [②. 时间片轮转](#-时间片轮转) + * [③. 多级反馈队列](#-多级反馈队列) + * [⑶. 实时系统中的调度](#-实时系统中的调度) + * [4. 进程同步](#4-进程同步) + * [⑴. 临界区](#-临界区) + * [⑵. 同步与互斥](#-同步与互斥) + * [⑶. 信号量](#-信号量) + * [⑷. 管程](#-管程) + * [5. 经典同步问题](#5-经典同步问题) + * [⑴. 读者-写者问题](#-读者-写者问题) + * [⑵. 哲学家进餐问题](#-哲学家进餐问题) + * [6. 进程通信](#6-进程通信) + * [⑴. 进程同步与进程通信的区别](#-进程同步与进程通信的区别) + * [⑵. 进程通信方式](#-进程通信方式) + * [①. 消息传递](#-消息传递) + * [②. 共享内存](#-共享内存) + * [(三)、死锁](#三死锁) + * [1. 死锁的必要条件](#1-死锁的必要条件) + * [2. 死锁的处理方法](#2-死锁的处理方法) + * [⑴. 鸵鸟策略](#-鸵鸟策略) + * [⑵. 死锁检测与死锁恢复](#-死锁检测与死锁恢复) + * [⑶. 死锁预防](#-死锁预防) + * [⑷. 死锁避免](#-死锁避免) + * [(四)、内存管理](#四内存管理) + * [1. 虚拟内存](#1-虚拟内存) + * [2. 分页与分段](#2-分页与分段) + * [⑴. 分页](#-分页) + * [⑵. 分段](#-分段) + * [⑶. 段页式](#-段页式) + * [⑷. 分页与分段区别](#-分页与分段区别) + * [3. 分页系统地址映射](#3-分页系统地址映射) + * [4. 页面置换算法](#4-页面置换算法) + * [⑴. 最佳](#-最佳) + * [⑵. 先进先出](#-先进先出) + * [⑶. 最近最久未使用](#-最近最久未使用) + * [⑷. 时钟](#-时钟) + * [(五)、设备管理](#五设备管理) + * [1. 磁盘调度算法](#1-磁盘调度算法) + * [⑴. 先来先服务](#-先来先服务-1) + * [⑵. 最短寻道时间优先](#-最短寻道时间优先) + * [⑶. 扫描算法](#-扫描算法) + * [⑷. 循环扫描算法](#-循环扫描算法) + * [(六)、链接](#六链接) + * [1. 编译系统](#1-编译系统) + * [2. 目标文件](#2-目标文件) + * [3. 静态链接](#3-静态链接) + * [4. 动态链接](#4-动态链接) + * [五、Linux](#五linux) + * [(一)、常用操作以及概念](#一常用操作以及概念) + * [1. 求助](#1-求助) + * [⑴. help](#-help) + * [⑵. man](#-man) + * [⑶. info](#-info) + * [2. 关机](#2-关机) + * [⑴. sync](#-sync) + * [⑵. shutdown](#-shutdown) + * [⑶. 其它关机指令](#-其它关机指令) + * [3. PATH](#3-path) + * [4. 运行等级](#4-运行等级) + * [5. sudo](#5-sudo) + * [6. GNU](#6-gnu) + * [7. 包管理工具](#7-包管理工具) + * [8. 发行版](#8-发行版) + * [9. VIM 三个模式](#9-vim-三个模式) + * [(二)、分区](#二分区) + * [1. 磁盘的文件名](#1-磁盘的文件名) + * [2. 分区表](#2-分区表) + * [⑴. MBR](#-mbr) + * [⑵. GPT](#-gpt) + * [3. 开机检测程序](#3-开机检测程序) + * [⑴. BIOS](#-bios) + * [⑵. UEFI](#-uefi) + * [4. 挂载](#4-挂载) + * [(三)、文件](#三文件) + * [1. 文件权限概念](#1-文件权限概念) + * [2. 文件属性以及权限的修改](#2-文件属性以及权限的修改) + * [⑴. 修改文件所属群组](#-修改文件所属群组) + * [⑵. 修改文件拥有者](#-修改文件拥有者) + * [⑶. 修改权限](#-修改权限) + * [3. 目录的权限](#3-目录的权限) + * [4. 文件默认权限](#4-文件默认权限) + * [5. 目录配置](#5-目录配置) + * [6. 文件时间](#6-文件时间) + * [7. 文件与目录的基本操作](#7-文件与目录的基本操作) + * [⑴. ls](#-ls) + * [⑵. cp](#-cp) + * [⑶. rm](#-rm) + * [⑷. mv](#-mv) + * [8. 获取文件内容](#8-获取文件内容) + * [⑴. cat](#-cat) + * [⑵. tac](#-tac) + * [⑶. more](#-more) + * [⑷. less](#-less) + * [⑸. head](#-head) + * [⑹. tail](#-tail) + * [⑺. od](#-od) + * [⑻. touch](#-touch) + * [9. 指令与文件搜索](#9-指令与文件搜索) + * [⑴. which](#-which) + * [⑵. whereis](#-whereis) + * [⑶. locate](#-locate) + * [⑷. find](#-find) + * [(四)、磁盘与文件系统](#四磁盘与文件系统) + * [1. 文件系统的组成](#1-文件系统的组成) + * [2. inode](#2-inode) + * [3. 目录的 inode 与 block](#3-目录的-inode-与-block) + * [. 实体链接与符号链接](#-实体链接与符号链接) + * [⑴. 实体链接](#-实体链接) + * [⑵. 符号链接](#-符号链接) + * [(五)、压缩与打包](#五压缩与打包) + * [1. 压缩](#1-压缩) + * [⑴. gzip](#-gzip) + * [⑵. bzip2](#-bzip2) + * [⑶. xz](#-xz) + * [2. 打包](#2-打包) + * [(六)、Bash](#六bash) + * [1. 特性](#1-特性) + * [2. 变量操作](#2-变量操作) + * [3. 指令搜索顺序](#3-指令搜索顺序) + * [4. 数据流重定向](#4-数据流重定向) + * [(七)、管线指令](#七管线指令) + * [1. 提取指令](#1-提取指令) + * [2. 排序指令](#2-排序指令) + * [3. 双向输出重定向](#3-双向输出重定向) + * [4. 字符转换指令](#4-字符转换指令) + * [5. 分区指令](#5-分区指令) + * [(八)、正则表达式](#八正则表达式) + * [1. grep](#1-grep) + * [2. printf](#2-printf) + * [3. awk](#3-awk) + * [(九)、进程管理](#九进程管理) + * [1. 查看进程](#1-查看进程) + * [⑴. ps](#-ps) + * [⑵. top](#-top) + * [⑶. pstree](#-pstree) + * [⑷. netstat](#-netstat) + * [2. 进程状态](#2-进程状态) + * [3. SIGCHILD](#3-sigchild) + * [4. 孤儿进程和僵死进程](#4-孤儿进程和僵死进程) + * [⑴. 孤儿进程](#-孤儿进程) + * [⑵. 僵死进程](#-僵死进程) + * [(十)、I/O复用](#十io复用) + * [1. 概念理解](#1-概念理解) + * [2. I/O 模型](#2-io-模型) + * [⑴. 同步-阻塞](#-同步-阻塞) + * [⑵. 同步-非阻塞](#-同步-非阻塞) + * [⑶. 异步-阻塞](#-异步-阻塞) + * [⑷. 异步-非阻塞](#-异步-非阻塞) + * [3. select poll epoll](#3-select-poll-epoll) + * [⑴. select](#-select) + * [⑵. poll](#-poll) + * [⑶. epoll](#-epoll) + * [4. select 和 poll 比较](#4-select-和-poll-比较) + * [⑴. 功能](#-功能) + * [⑵. 速度](#-速度) + * [⑶. 可移植性](#-可移植性) + * [5. eopll 工作模式](#5-eopll-工作模式) + * [⑴. LT 模式](#-lt-模式) + * [⑵. ET 模式](#-et-模式) + * [6. select poll epoll 应用场景](#6-select-poll-epoll-应用场景) + * [⑴. select 应用场景](#-select-应用场景) + * [⑵. poll 应用场景](#-poll-应用场景) + * [⑶. epoll 应用场景](#-epoll-应用场景) + * [⑷. 性能对比](#-性能对比) + * [六、算法](#六算法) + * [(一)、算法分析](#一算法分析) + * [1. 数学模型](#1-数学模型) + * [⑴. 近似](#-近似) + * [⑵. 增长数量级](#-增长数量级) + * [⑶. 内循环](#-内循环) + * [⑷. 成本模型](#-成本模型) + * [2. ThreeSum](#2-threesum) + * [3. 倍率实验](#3-倍率实验) + * [4. 注意事项](#4-注意事项) + * [⑴. 大常数](#-大常数) + * [⑵. 缓存](#-缓存) + * [⑶. 对最坏情况下的性能的保证](#-对最坏情况下的性能的保证) + * [⑷. 随机化算法](#-随机化算法) + * [⑸. 均摊分析](#-均摊分析) + * [(二)、栈和队列](#二栈和队列) + * [1. 栈](#1-栈) + * [2. 队列](#2-队列) + * [(三)、union-find](#三union-find) + * [1. quick-find](#1-quick-find) + * [2. quick-union](#2-quick-union) + * [3. 加权 quick-union](#3-加权-quick-union) + * [4. 路径压缩的加权 quick-union](#4-路径压缩的加权-quick-union) + * [5. 各种 union-find 算法的比较](#5-各种-union-find-算法的比较) + * [(四)、排序](#四排序) + * [1. 选择排序](#1-选择排序) + * [2. 插入排序](#2-插入排序) + * [3. 希尔排序](#3-希尔排序) + * [4. 归并排序](#4-归并排序) + * [⑴. 归并方法](#-归并方法) + * [⑵. 自顶向下归并排序](#-自顶向下归并排序) + * [⑶. 自底向上归并排序](#-自底向上归并排序) + * [5. 快速排序](#5-快速排序) + * [⑴. 基本算法](#-基本算法) + * [⑵. 切分](#-切分) + * [⑶. 性能分析](#-性能分析) + * [⑷. 算法改进](#-算法改进) + * [6. 优先队列](#6-优先队列) + * [⑴. 堆](#-堆) + * [⑵. 上浮和下沉](#-上浮和下沉) + * [⑶. 插入元素](#-插入元素) + * [⑷. 删除最大元素](#-删除最大元素) + * [⑸. 堆排序](#-堆排序) + * [⑹. 分析](#-分析) + * [7. 应用](#7-应用) + * [⑴. 排序算法的比较](#-排序算法的比较) + * [⑵. Java 的排序算法实现](#-java-的排序算法实现) + * [⑶. 基于切分的快速选择算法](#-基于切分的快速选择算法) + * [(五)、查找](#五查找) + * [1. 符号表](#1-符号表) + * [⑴. 无序符号表](#-无序符号表) + * [⑵. 有序符号表](#-有序符号表) + * [⑶. 二分查找实现有序符号表](#-二分查找实现有序符号表) + * [2. 二叉查找树](#2-二叉查找树) + * [⑴. get()](#-get) + * [⑵. put()](#-put) + * [⑶. 分析](#-分析-1) + * [⑷. floor()](#-floor) + * [⑸. rank()](#-rank) + * [⑹. min()](#-min) + * [⑺. deleteMin()](#-deletemin) + * [⑻. delete()](#-delete) + * [⑼. keys()](#-keys) + * [⑽. 性能分析](#-性能分析-1) + * [3. 2-3 查找树](#3-2-3-查找树) + * [⑴. 插入操作](#-插入操作) + * [⑵. 性质](#-性质) + * [3. 红黑二叉查找树](#3-红黑二叉查找树) + * [⑴. 左旋转](#-左旋转) + * [⑵. 右旋转](#-右旋转) + * [⑶. 颜色转换](#-颜色转换) + * [⑷. 插入](#-插入) + * [⑸. 删除最小键](#-删除最小键) + * [⑹. 分析](#-分析-2) + * [4. 散列表](#4-散列表) + * [⑴. 散列函数](#-散列函数) + * [⑵. 基于拉链法的散列表](#-基于拉链法的散列表) + * [⑶. 基于线性探测法的散列表](#-基于线性探测法的散列表) + * [5. 应用](#5-应用) + * [⑴. 各种符号表实现的比较](#-各种符号表实现的比较) + * [⑵. Java 的符号表实现](#-java-的符号表实现) + * [⑶. 集合类型](#-集合类型) + * [⑷. 稀疏向量乘法](#-稀疏向量乘法) + * [七、剑指 Offer 题解](#七剑指-offer-题解) + * [1. 前言](#1-前言) + * [2. 实现 Singleton](#2-实现-singleton) + * [3. 数组中重复的数字](#3-数组中重复的数字) + * [4. 二维数组中的查找](#4-二维数组中的查找) + * [5. 替换空格](#5-替换空格) + * [6. 从尾到头打印链表](#6-从尾到头打印链表) + * [7. 重建二叉树](#7-重建二叉树) + * [8. 二叉树的下一个结点](#8-二叉树的下一个结点) + * [9. 用两个栈实现队列](#9-用两个栈实现队列) + * [10.1 斐波那契数列](#101-斐波那契数列) + * [10.2 跳台阶](#102-跳台阶) + * [10.3 变态跳台阶](#103-变态跳台阶) + * [10.4 矩形覆盖](#104-矩形覆盖) + * [11. 旋转数组的最小数字](#11-旋转数组的最小数字) + * [12. 矩阵中的路径](#12-矩阵中的路径) + * [13. 机器人的运动范围](#13-机器人的运动范围) + * [14. 剪绳子](#14-剪绳子) + * [15. 二进制中 1 的个数](#15-二进制中-1-的个数) + * [16. 数值的整数次方](#16-数值的整数次方) + * [17. 打印从 1 到最大的 n 位数](#17-打印从-1-到最大的-n-位数) + * [18.1 在 O(1) 时间内删除链表节点](#181-在-o1-时间内删除链表节点) + * [18.2 删除链表中重复的结点](#182-删除链表中重复的结点) + * [19. 正则表达式匹配](#19-正则表达式匹配) + * [20. 表示数值的字符串](#20-表示数值的字符串) + * [21. 调整数组顺序使奇数位于偶数前面](#21-调整数组顺序使奇数位于偶数前面) + * [22. 链表中倒数第 K 个结点](#22-链表中倒数第-k-个结点) + * [23. 链表中环的入口结点](#23-链表中环的入口结点) + * [24. 反转链表](#24-反转链表) + * [25. 合并两个排序的链表](#25-合并两个排序的链表) + * [26. 树的子结构](#26-树的子结构) + * [27. 二叉树的镜像](#27-二叉树的镜像) + * [28. 对称的二叉树](#28-对称的二叉树) + * [29. 顺时针打印矩阵](#29-顺时针打印矩阵) + * [30. 包含 min 函数的栈](#30-包含-min-函数的栈) + * [31. 栈的压入、弹出序列](#31-栈的压入弹出序列) + * [32.1 从上往下打印二叉树](#321-从上往下打印二叉树) + * [32.2 把二叉树打印成多行](#322-把二叉树打印成多行) + * [32.3 按之字形顺序打印二叉树](#323-按之字形顺序打印二叉树) + * [33. 二叉搜索树的后序遍历序列](#33-二叉搜索树的后序遍历序列) + * [34. 二叉树中和为某一值的路径](#34-二叉树中和为某一值的路径) + * [35. 复杂链表的复制](#35-复杂链表的复制) + * [36. 二叉搜索树与双向链表](#36-二叉搜索树与双向链表) + * [37. 序列化二叉树](#37-序列化二叉树) + * [38. 字符串的排列](#38-字符串的排列) + * [39. 数组中出现次数超过一半的数字](#39-数组中出现次数超过一半的数字) + * [40. 最小的 K 个数](#40-最小的-k-个数) + * [41.1 数据流中的中位数](#411-数据流中的中位数) + * [41.2 字符流中第一个不重复的字符](#412-字符流中第一个不重复的字符) + * [42. 连续子数组的最大和](#42-连续子数组的最大和) + * [43. 从 1 到 n 整数中 1 出现的次数](#43-从-1-到-n-整数中-1-出现的次数) + * [44. 数字序列中的某一位数字](#44-数字序列中的某一位数字) + * [45. 把数组排成最小的数](#45-把数组排成最小的数) + * [46. 把数字翻译成字符串](#46-把数字翻译成字符串) + * [47. 礼物的最大价值](#47-礼物的最大价值) + * [48. 最长不含重复字符的子字符串](#48-最长不含重复字符的子字符串) + * [49. 丑数](#49-丑数) + * [50. 第一个只出现一次的字符位置](#50-第一个只出现一次的字符位置) + * [51. 数组中的逆序对](#51-数组中的逆序对) + * [52. 两个链表的第一个公共结点](#52-两个链表的第一个公共结点) + * [53 数字在排序数组中出现的次数](#53-数字在排序数组中出现的次数) + * [54. 二叉搜索树的第 K 个结点](#54-二叉搜索树的第-k-个结点) + * [55.1 二叉树的深度](#551-二叉树的深度) + * [55.2 平衡二叉树](#552-平衡二叉树) + * [56. 数组中只出现一次的数字](#56-数组中只出现一次的数字) + * [57.1 和为 S 的两个数字](#571-和为-s-的两个数字) + * [57.2 和为 S 的连续正数序列](#572-和为-s-的连续正数序列) + * [58.1 翻转单词顺序列](#581-翻转单词顺序列) + * [58.2 左旋转字符串](#582-左旋转字符串) + * [59. 滑动窗口的最大值](#59-滑动窗口的最大值) + * [60. n 个骰子的点数](#60-n-个骰子的点数) + * [61. 扑克牌顺子](#61-扑克牌顺子) + * [62. 圆圈中最后剩下的数](#62-圆圈中最后剩下的数) + * [63. 股票的最大利润](#63-股票的最大利润) + * [64. 求 1 2 3 … n](#64-求-123n) + * [65. 不用加减乘除做加法](#65-不用加减乘除做加法) + * [66. 构建乘积数组](#66-构建乘积数组) + * [67. 把字符串转换成整数](#67-把字符串转换成整数) + * [68. 树中两个节点的最低公共祖先](#68-树中两个节点的最低公共祖先) + * [八、Leetcode 题解](#八leetcode-题解) + * [(一)、算法思想](#一算法思想) + * [1. 二分查找](#1-二分查找) + * [2. 贪心思想](#2-贪心思想) + * [3. 双指针](#3-双指针) + * [4. 排序](#4-排序) + * [⑴. 快速选择](#-快速选择) + * [⑵. 堆排序](#-堆排序-1) + * [⑶. 桶排序](#-桶排序) + * [5. 搜索](#5-搜索) + * [⑴. BFS](#-bfs) + * [⑵. DFS](#-dfs) + * [⑶. Backtracking](#-backtracking) + * [6. 分治](#6-分治) + * [7. 动态规划](#7-动态规划) + * [⑴. 斐波那契数列](#-斐波那契数列) + * [⑵. 最长递增子序列](#-最长递增子序列) + * [⑶. 最长公共子系列](#-最长公共子系列) + * [⑷. 0-1 背包](#-0-1-背包) + * [⑸. 数组区间](#-数组区间) + * [⑹. 字符串编辑](#-字符串编辑) + * [⑺. 分割整数](#-分割整数) + * [⑻. 矩阵路径](#-矩阵路径) + * [⑼. 其它问题](#-其它问题) + * [8. 数学](#8-数学) + * [⑴. 素数](#-素数) + * [⑵. 最大公约数](#-最大公约数) + * [⑶. 进制转换](#-进制转换) + * [⑷. 阶乘](#-阶乘) + * [⑸. 字符串加法减法](#-字符串加法减法) + * [⑹. 相遇问题](#-相遇问题) + * [⑺. 多数投票问题](#-多数投票问题) + * [⑻. 其它](#-其它) + * [(二)、数据结构](#二数据结构) + * [1. 栈和队列](#1-栈和队列) + * [2. 哈希表](#2-哈希表) + * [3. 字符串](#3-字符串) + * [4. 数组与矩阵](#4-数组与矩阵) + * [⑴. 1-n 分布](#-1-n-分布) + * [⑵. 有序矩阵](#-有序矩阵) + * [5. 链表](#5-链表) + * [6. 树](#6-树) + * [⑴. 递归](#-递归) + * [⑵. 层次遍历](#-层次遍历) + * [⑶. 前中后序遍历](#-前中后序遍历) + * [⑷. BST](#-bst) + * [⑸. Trie](#-trie) + * [7. 图](#7-图) + * [8. 位运算](#8-位运算) + * [九、设计模式](#九设计模式) + * [(一)、前言](#一前言) + * [(二)、设计模式概念](#二设计模式概念) + * [(三)、单例模式](#三单例模式) + * [1. 意图](#1-意图) + * [2. 类图](#2-类图) + * [3. 使用场景](#3-使用场景) + * [4. JDK 的使用](#4-jdk-的使用) + * [5. 实现](#5-实现) + * [⑴. 懒汉式-线程不安全](#-懒汉式-线程不安全) + * [⑵. 懒汉式-线程安全](#-懒汉式-线程安全) + * [⑶. 饿汉式-线程安全](#-饿汉式-线程安全) + * [⑷. 双重校验锁-线程安全](#-双重校验锁-线程安全) + * [(四)、简单工厂](#四简单工厂) + * [1. 意图](#1-意图-1) + * [2. 类图](#2-类图-1) + * [3. 实现](#3-实现) + * [(五)、工厂方法模式](#五工厂方法模式) + * [1. 意图](#1-意图-2) + * [2. 类图](#2-类图-2) + * [3. 实现](#3-实现-1) + * [(六)、抽象工厂模式](#六抽象工厂模式) + * [1. 意图](#1-意图-3) + * [2. 类图](#2-类图-3) + * [3. 代码实现](#3-代码实现) + * [十、面向对象思想](#十面向对象思想) + * [(一)、设计原则](#一设计原则) + * [1. S.O.L.I.D](#1-solid) + * [⑴. 单一责任原则](#-单一责任原则) + * [⑵. 开放封闭原则](#-开放封闭原则) + * [⑶. 里氏替换原则](#-里氏替换原则) + * [⑷. 接口分离原则](#-接口分离原则) + * [⑸. 依赖倒置原则](#-依赖倒置原则) + * [2. 其他常见原则](#2-其他常见原则) + * [⑴. 迪米特法则](#-迪米特法则) + * [⑵. 合成复用原则](#-合成复用原则) + * [⑶. 共同封闭原则](#-共同封闭原则) + * [⑷. 稳定抽象原则](#-稳定抽象原则) + * [⑸. 稳定依赖原则](#-稳定依赖原则) + * [(二)、三大特性](#二三大特性) + * [1. 封装](#1-封装) + * [2. 继承](#2-继承) + * [3. 多态](#3-多态) + * [(三)、UML](#三uml) + * [1. 类图](#1-类图) + * [⑴. 继承相关](#-继承相关) + * [⑵. 泛化关系 (Generalize)](#-泛化关系-generalize) + * [⑶. 实现关系 (Realize)](#-实现关系-realize) + * [⑷. 整体和部分](#-整体和部分) + * [⑸. 聚合关系 (Aggregation)](#-聚合关系-aggregation) + * [⑹. 组合关系 (Composition)](#-组合关系-composition) + * [⑺. 相互联系](#-相互联系) + * [⑻. 关联关系 (Association)](#-关联关系-association) + * [⑼. 依赖关系 (Dependency)](#-依赖关系-dependency) + * [2. 时序图](#2-时序图) + * [⑴. 定义](#-定义) + * [⑵. 赤壁之战时序图](#-赤壁之战时序图) + * [⑶. 活动图、时序图之间的关系](#-活动图时序图之间的关系) + * [⑷. 类图与时序图的关系](#-类图与时序图的关系) + * [⑸. 时序图的组成](#-时序图的组成) + * [⑹. 对象](#-对象) + * [⑺. 生命线](#-生命线) + * [⑻. 消息](#-消息) + * [⑼. 激活](#-激活) + * [十一、数据库系统原理](#十一数据库系统原理) + * [(一)、事务](#一事务) + * [1. 概念](#1-概念) + * [2. 四大特性](#2-四大特性) + * [(二)、并发一致性问题](#二并发一致性问题) + * [1. 问题](#1-问题) + * [⑴. 丢失修改](#-丢失修改) + * [⑵. 读脏数据](#-读脏数据) + * [⑶. 不可重复读](#-不可重复读) + * [⑷. 幻影读](#-幻影读) + * [2. 解决方法](#2-解决方法) + * [(三)、封锁](#三封锁) + * [1. 封锁粒度](#1-封锁粒度) + * [2. 封锁类型](#2-封锁类型) + * [⑴. 排它锁与共享锁](#-排它锁与共享锁) + * [⑵. 意向锁](#-意向锁) + * [3. 封锁协议](#3-封锁协议) + * [⑴. 三级封锁协议](#-三级封锁协议) + * [⑵. 两段锁协议](#-两段锁协议) + * [(四)、隔离级别](#四隔离级别) + * [(五)、多版本并发控制](#五多版本并发控制) + * [1. 版本号](#1-版本号) + * [2. Undo 日志](#2-undo-日志) + * [3. 实现过程](#3-实现过程) + * [⑴. SELECT](#-select-1) + * [⑵. INSERT](#-insert) + * [⑶. DELETE](#-delete-1) + * [⑷. UPDATE](#-update) + * [4. 快照读与当前读](#4-快照读与当前读) + * [(六)、Next-Key Locks](#六next-key-locks) + * [1. Record Locks](#1-record-locks) + * [2. Grap Locks](#2-grap-locks) + * [3. Next-Key Locks](#3-next-key-locks) + * [(七)、关系数据库设计理论](#七关系数据库设计理论) + * [1. 函数依赖](#1-函数依赖) + * [2. 异常](#2-异常) + * [3. 范式](#3-范式) + * [⑴. 第一范式 (1NF)](#-第一范式-1nf) + * [⑵. 第二范式 (2NF)](#-第二范式-2nf) + * [⑶. 第三范式 (3NF)](#-第三范式-3nf) + * [⑷. BC 范式(BCNF)](#-bc-范式bcnf) + * [(八)、数据库系统概述](#八数据库系统概述) + * [1. 基本术语](#1-基本术语) + * [⑴. 数据模型](#-数据模型) + * [⑵. 数据库系统](#-数据库系统) + * [2. 数据库的三层模式和两层映像](#2-数据库的三层模式和两层映像) + * [⑴. 外模式](#-外模式) + * [⑵. 模式](#-模式) + * [⑶. 内模式](#-内模式) + * [⑷. 外模式/模式映像](#-外模式模式映像) + * [⑸. 模式/内模式映像](#-模式内模式映像) + * [(九)、关系数据库建模](#九关系数据库建模) + * [1. ER 图](#1-er-图) + * [⑴. 实体的三种联系](#-实体的三种联系) + * [⑵. 表示出现多次的关系](#-表示出现多次的关系) + * [⑶. 联系的多向性](#-联系的多向性) + * [⑷. 表示子类](#-表示子类) + * [(十)、约束](#十约束) + * [1. 键码](#1-键码) + * [2. 单值约束](#2-单值约束) + * [3. 引用完整性约束](#3-引用完整性约束) + * [4. 域约束](#4-域约束) + * [5. 一般约束](#5-一般约束) + * [十二、SQL](#十二sql) + * [(一)、基础](#一基础) + * [(二)、创建表](#二创建表) + * [(三)、修改表](#三修改表) + * [(四)、插入](#四插入) + * [(五)、更新](#五更新) + * [(六)、删除](#六删除) + * [(七)、查询](#七查询) + * [1. DISTINCT](#1-distinct) + * [2. LIMIT](#2-limit) + * [(八)、排序](#八排序) + * [(九)、过滤](#九过滤) + * [(十)、通配符](#十通配符) + * [(十一)、计算字段](#十一计算字段) + * [(十二)、函数](#十二函数) + * [1. 文本处理](#1-文本处理) + * [2. 日期和时间处理](#2-日期和时间处理) + * [3. 数值处理](#3-数值处理) + * [4. 汇总](#4-汇总) + * [(十三)、分组](#十三分组) + * [(十四)、子查询](#十四子查询) + * [(十五)、连接](#十五连接) + * [1. 内连接](#1-内连接) + * [2. 自连接](#2-自连接) + * [3. 自然连接](#3-自然连接) + * [4. 外连接](#4-外连接) + * [(十六)、组合查询](#十六组合查询) + * [(十七)、视图](#十七视图) + * [(十八)、存储过程](#十八存储过程) + * [1. 使用存储过程的好处](#1-使用存储过程的好处) + * [2. 创建存储过程](#2-创建存储过程) + * [(十九)、游标](#十九游标) + * [(二十)、触发器](#二十触发器) + * [(二十一)、事务处理](#二十一事务处理) + * [(二十二)、字符集](#二十二字符集) + * [(二十三)、权限管理](#二十三权限管理) + * [1. 创建账户](#1-创建账户) + * [2. 修改账户名](#2-修改账户名) + * [3. 删除账户](#3-删除账户) + * [4. 查看权限](#4-查看权限) + * [5. 授予权限](#5-授予权限) + * [6. 删除权限](#6-删除权限) + * [7. 更改密码](#7-更改密码) + * [十三、MySQL](#十三mysql) + * [(一)、储存引擎](#一储存引擎) + * [1. InnoDB](#1-innodb) + * [2. MyISAM](#2-myisam) + * [3. 比较](#3-比较) + * [(二)、数据类型](#二数据类型) + * [1. 整型](#1-整型) + * [2. 浮点数](#2-浮点数) + * [3. 字符串](#3-字符串-1) + * [4. 时间和日期](#4-时间和日期) + * [⑴. DATATIME](#-datatime) + * [⑵. TIMESTAMP](#-timestamp) + * [(三)、索引](#三索引) + * [1. 索引分类](#1-索引分类) + * [⑴. B Tree 索引](#-btree-索引) + * [⑵. 哈希索引](#-哈希索引) + * [⑶. 空间索引(R-Tree)](#-空间索引r-tree) + * [⑷. 全文索引](#-全文索引) + * [2. 索引的优点](#2-索引的优点) + * [3. 索引优化](#3-索引优化) + * [⑴. 独立的列](#-独立的列) + * [⑵. 前缀索引](#-前缀索引) + * [⑶. 多列索引](#-多列索引) + * [⑷. 索引列的顺序](#-索引列的顺序) + * [⑸. 聚簇索引](#-聚簇索引) + * [⑹. 覆盖索引](#-覆盖索引) + * [4. B-Tree 和 B Tree 原理](#4-b-tree-和-btree-原理) + * [⑴. B-Tree](#-b-tree) + * [⑵. B Tree](#-btree) + * [⑶. 带有顺序访问指针的 B Tree](#-带有顺序访问指针的-btree) + * [⑷. 为什么使用 B-Tree 和 B Tree](#-为什么使用-b-tree-和-btree) + * [(四)、查询性能优化](#四查询性能优化) + * [1. Explain](#1-explain) + * [2. 减少返回的列](#2-减少返回的列) + * [3. 减少返回的行](#3-减少返回的行) + * [4. 拆分大的 DELETE 或 INSERT 语句](#4-拆分大的-delete-或-insert-语句) + * [(五)、切分](#五切分) + * [1. 垂直切分](#1-垂直切分) + * [2. 水平切分](#2-水平切分) + * [3. 切分的选择](#3-切分的选择) + * [4. 存在的问题](#4-存在的问题) + * [⑴. 事务问题](#-事务问题) + * [⑵. 跨库跨表连接问题](#-跨库跨表连接问题) + * [⑶. 额外的数据管理负担和数据运算压力](#-额外的数据管理负担和数据运算压力) + * [(六)、数据转移和故障恢复](#六数据转移和故障恢复) + * [1. 提升备库或切换角色](#1-提升备库或切换角色) + * [2. 虚拟 IP 地址和 IP 托管](#2-虚拟-ip-地址和-ip-托管) + * [3. 中间件解决方案](#3-中间件解决方案) + * [4. 在应用中处理故障转移](#4-在应用中处理故障转移) + * [十四、Redis](#十四redis) + * [(一)、Redis是什么](#一redis是什么) + * [(二)、五种基本类型](#二五种基本类型) + * [1. STRING](#1-string) + * [2. LIST](#2-list) + * [3. SET](#3-set) + * [4. HASH](#4-hash) + * [5. ZSET](#5-zset) + * [(三)、键的过期时间](#三键的过期时间) + * [(四)、发布与订阅](#四发布与订阅) + * [(五)、事务](#五事务) + * [(六)、持久化](#六持久化) + * [1. 快照持久化](#1-快照持久化) + * [2. AOF 持久化](#2-aof-持久化) + * [(七)、复制](#七复制) + * [1. 从服务器连接主服务器的过程](#1-从服务器连接主服务器的过程) + * [2. 主从链](#2-主从链) + * [(八)、处理故障](#八处理故障) + * [(九)、分片](#九分片) + * [1. 客户端分片](#1-客户端分片) + * [2. 代理分片](#2-代理分片) + * [3. 服务器分片](#3-服务器分片) + * [(十)、事件](#十事件) + * [1. 事件类型](#1-事件类型) + * [⑴. 文件事件](#-文件事件) + * [⑵. 时间事件](#-时间事件) + * [2. 事件的调度与执行](#2-事件的调度与执行) + * [(十一)、Redis 与 Memcached 的区别](#十一redis-与-memcached-的区别) + * [1. 数据类型](#1-数据类型) + * [2. 数据持久化](#2-数据持久化) + * [3. 分布式](#3-分布式) + * [4. 内存管理机制](#4-内存管理机制) + * [(十二)、Redis 适用场景](#十二redis-适用场景) + * [1. 缓存](#1-缓存) + * [2. 消息队列](#2-消息队列) + * [3. 计数器](#3-计数器) + * [4. 好友关系](#4-好友关系) + * [(十三)、数据淘汰策略](#十三数据淘汰策略) + * [(十四)、一个简单的论坛系统分析](#十四一个简单的论坛系统分析) + * [1. 文章信息](#1-文章信息) + * [2. 点赞功能](#2-点赞功能) + * [3. 对文章进行排序](#3-对文章进行排序) + * [十五、Java 虚拟机](#十五java-虚拟机) + * [(一)、运行时数据区域](#一运行时数据区域) + * [1. 程序计数器](#1-程序计数器) + * [2. Java 虚拟机栈](#2-java-虚拟机栈) + * [3. 本地方法栈](#3-本地方法栈) + * [4. Java 堆](#4-java-堆) + * [5. 方法区](#5-方法区) + * [6. 运行时常量池](#6-运行时常量池) + * [7. 直接内存](#7-直接内存) + * [(二)、垃圾收集](#二垃圾收集) + * [1. 判断一个对象是否可回收](#1-判断一个对象是否可回收) + * [⑴. 引用计数](#-引用计数) + * [⑵. 可达性](#-可达性) + * [⑶. 引用类型](#-引用类型) + * [⑷. 方法区的回收](#-方法区的回收) + * [⑸. finalize()](#-finalize) + * [2. 垃圾收集算法](#2-垃圾收集算法) + * [⑴. 标记 - 清除](#-标记---清除) + * [⑵. 复制](#-复制) + * [⑶. 标记 - 整理](#-标记---整理) + * [⑷. 分代收集](#-分代收集) + * [3. 垃圾收集器](#3-垃圾收集器) + * [⑴. Serial 收集器](#-serial-收集器) + * [⑵. ParNew 收集器](#-parnew-收集器) + * [⑶. Parallel Scavenge 收集器](#-parallel-scavenge-收集器) + * [⑷. Serial Old 收集器](#-serial-old-收集器) + * [⑸. Parallel Old 收集器](#-parallel-old-收集器) + * [⑹. CMS 收集器](#-cms-收集器) + * [⑺. G1 收集器](#-g1-收集器) + * [⑻. 七种垃圾收集器的比较](#-七种垃圾收集器的比较) + * [4. 内存分配与回收策略](#4-内存分配与回收策略) + * [⑴. 优先在 Eden 分配](#-优先在-eden-分配) + * [⑵. 大对象直接进入老年代](#-大对象直接进入老年代) + * [⑶. 长期存活的对象进入老年代](#-长期存活的对象进入老年代) + * [⑷. 动态对象年龄判定](#-动态对象年龄判定) + * [⑸. 空间分配担保](#-空间分配担保) + * [5. Full GC 的触发条件](#5-full-gc-的触发条件) + * [⑴. 调用 System.gc()](#-调用-systemgc) + * [⑵. 老年代空间不足](#-老年代空间不足) + * [⑶. 空间分配担保失败](#-空间分配担保失败) + * [⑷. JDK 1.7 及以前的永久代空间不足](#-jdk-17-及以前的永久代空间不足) + * [⑸. Concurrent Mode Failure](#-concurrent-mode-failure) + * [(三)、类加载机制](#三类加载机制) + * [1. 类的生命周期](#1-类的生命周期) + * [2. 类初始化时机](#2-类初始化时机) + * [3. 类加载过程](#3-类加载过程) + * [⑴. 加载](#-加载) + * [⑵. 验证](#-验证) + * [⑶. 准备](#-准备) + * [⑷. 解析](#-解析) + * [⑸. 初始化](#-初始化) + * [4. 类加载器](#4-类加载器) + * [⑴. 类与类加载器](#-类与类加载器) + * [⑵. 类加载器分类](#-类加载器分类) + * [⑶. 双亲委派模型](#-双亲委派模型) + * [(四)、JVM参数](#四jvm参数) + * [1. GC 优化配置](#1-gc-优化配置) + * [2. GC 类型设置](#2-gc-类型设置) + * [十六、Java 并发](#十六java-并发) + * [(一)、使用线程](#一使用线程) + * [1. 实现 Runnable 接口](#1-实现-runnable-接口) + * [2. 实现 Callable 接口](#2-实现-callable-接口) + * [3. 继承 Thread 类](#3-继承-thread-类) + * [4. 实现接口 VS 继承 Thread](#4-实现接口-vs-继承-thread) + * [(二)、基础线程机制](#二基础线程机制) + * [1. sleep()](#1-sleep) + * [2. yield()](#2-yield) + * [3. join()](#3-join) + * [4. deamon](#4-deamon) + * [(三)、结束线程](#三结束线程) + * [1. 阻塞](#1-阻塞) + * [2. 中断](#2-中断) + * [(四)、线程之间的协作](#四线程之间的协作) + * [1. 同步与通信的概念理解](#1-同步与通信的概念理解) + * [2. 线程同步](#2-线程同步) + * [⑴. synchronized](#-synchronized) + * [⑵. ReentrantLock](#-reentrantlock) + * [3. 线程通信](#3-线程通信) + * [⑴. wait() notify() notifyAll()](#-wait-notify-notifyall) + * [⑵. BlockingQueue](#-blockingqueue) + * [(五)、线程状态转换](#五线程状态转换) + * [(六)、Executor](#六executor) + * [(七)、内存模型](#七内存模型) + * [1. 主内存与工作内存](#1-主内存与工作内存) + * [2. 内存模型三大特性](#2-内存模型三大特性) + * [⑴. 原子性](#-原子性) + * [⑵. 可见性](#-可见性) + * [⑶. 有序性](#-有序性) + * [3. 先行发生原则](#3-先行发生原则) + * [⑴. 单一线程原则](#-单一线程原则) + * [⑵. 管程锁定规则](#-管程锁定规则) + * [⑶. volatile 变量规则](#-volatile-变量规则) + * [⑷. 线程启动规则](#-线程启动规则) + * [⑸. 线程加入规则](#-线程加入规则) + * [⑹. 线程中断规则](#-线程中断规则) + * [⑺. 对象终结规则](#-对象终结规则) + * [⑻. 传递性](#-传递性) + * [(八)、线程安全](#八线程安全) + * [1. 线程安全分类](#1-线程安全分类) + * [⑴. 不可变](#-不可变) + * [⑵. 绝对线程安全](#-绝对线程安全) + * [⑶. 相对线程安全](#-相对线程安全) + * [⑷. 线程兼容](#-线程兼容) + * [⑸. 线程对立](#-线程对立) + * [2. 线程安全的实现方法](#2-线程安全的实现方法) + * [⑴. 互斥同步](#-互斥同步) + * [⑵. 非阻塞同步](#-非阻塞同步) + * [⑶. 无同步方案](#-无同步方案) + * [3. 锁优化](#3-锁优化) + * [⑴. 自旋锁与自适应自旋](#-自旋锁与自适应自旋) + * [⑵. 锁消除](#-锁消除) + * [⑶. 锁粗化](#-锁粗化) + * [⑷. 轻量级锁](#-轻量级锁) + * [⑸. 偏向锁](#-偏向锁) + * [(九)、多线程开发良好的实践](#九多线程开发良好的实践) + * [十七、Java 容器](#十七java-容器) + * [(一)、概览](#一概览) + * [1. Collection](#1-collection) + * [⑴. Set](#-set) + * [⑵. List](#-list) + * [⑶. Queue](#-queue) + * [2. Map](#2-map) + * [(二)、容器中的设计模式](#二容器中的设计模式) + * [1. 迭代器模式](#1-迭代器模式) + * [2. 适配器模式](#2-适配器模式) + * [(三)、散列](#三散列) + * [(四)、源码分析](#四源码分析) + * [1. ArrayList](#1-arraylist) + * [⑴. 概览](#-概览) + * [⑵. Fail-Fast](#-fail-fast) + * [⑶. 和 Vector 的区别](#-和-vector-的区别) + * [⑷. 和 LinkedList 的区别](#-和-linkedlist-的区别) + * [2. Vector](#2-vector) + * [3. LinkedList](#3-linkedlist) + * [4. TreeMap](#4-treemap) + * [5. HashMap](#5-hashmap) + * [⑴. 存储结构](#-存储结构) + * [⑵. 拉链法的工作原理](#-拉链法的工作原理) + * [⑶. 链表转红黑树](#-链表转红黑树) + * [⑷. 扩容](#-扩容) + * [⑸. 确定桶下标](#-确定桶下标) + * [⑹. 扩容-重新计算桶下标](#-扩容-重新计算桶下标) + * [⑺. 扩容-计算数组容量](#-扩容-计算数组容量) + * [⑻. null 值](#-null-值) + * [⑼. 与 HashTable 的区别](#-与-hashtable-的区别) + * [6. LinkedHashMap](#6-linkedhashmap) + * [7. ConcurrentHashMap - JDK 1.7](#7-concurrenthashmap---jdk-17) + * [⑴. 存储结构](#-存储结构-1) + * [⑵. HashEntery 的不可变性](#-hashentery-的不可变性) + * [⑶. Volatile 变量](#-volatile-变量) + * [⑷. 小结](#-小结) + * [8. ConcurrentHashMap - JDK 1.8](#8-concurrenthashmap---jdk-18) + * [十八、Java IO](#十八java-io) + * [(一)、概览](#一概览-1) + * [(二)、磁盘操作](#二磁盘操作) + * [(三)、字节操作](#三字节操作) + * [(四)、字符操作](#四字符操作) + * [(五)、对象操作](#五对象操作) + * [(六)、网络操作](#六网络操作) + * [1. InetAddress](#1-inetaddress) + * [2. URL](#2-url-1) + * [3. Sockets](#3-sockets) + * [4. Datagram](#4-datagram) + * [(七)、NIO](#七nio) + * [1. 流与块](#1-流与块) + * [2. 通道与缓冲区](#2-通道与缓冲区) + * [⑴. 通道](#-通道) + * [⑵. 缓冲区](#-缓冲区) + * [3. 缓冲区状态变量](#3-缓冲区状态变量) + * [4. 文件 NIO 实例](#4-文件-nio-实例) + * [5. 套接字 NIO 实例](#5-套接字-nio-实例) + * [⑴. ServerSocketChannel](#-serversocketchannel) + * [⑵. Selectors](#-selectors) + * [⑶. 主循环](#-主循环) + * [⑷. 监听新连接](#-监听新连接) + * [⑸. 接受新的连接](#-接受新的连接) + * [⑹. 删除处理过的 SelectionKey](#-删除处理过的-selectionkey) + * [⑺. 传入的 I/O](#-传入的-io) + * [6. 内存映射文件](#6-内存映射文件) + * [7. 对比](#7-对比) + * [十九、Java 基础](#十九java-基础) + * [(一)、关键字](#一关键字) + * [1. final](#1-final) + * [2. static](#2-static) + * [(二)、Object 通用方法](#二object-通用方法) + * [1. 概览](#1-概览) + * [2. clone()](#2-clone) + * [3. equals()](#3-equals) + * [(三)、继承](#三继承) + * [1. 访问权限](#1-访问权限) + * [2. 抽象类与接口](#2-抽象类与接口) + * [3. super](#3-super) + * [4. 重载与重写](#4-重载与重写) + * [(四)、String](#四string) + * [1. String, StringBuffer and StringBuilder](#1-string-stringbuffer-and-stringbuilder) + * [2. String 不可变的原因](#2-string-不可变的原因) + * [3. String.intern()](#3-stringintern) + * [(五)、基本类型与运算](#五基本类型与运算) + * [1. 包装类型](#1-包装类型) + * [2. switch](#2-switch) + * [(六)、反射](#六反射) + * [(七)、异常](#七异常) + * [(八)、泛型](#八泛型) + * [(九)、注解](#九注解) + * [(十)、特性](#十特性) + * [1. 面向对象三大特性](#1-面向对象三大特性) + * [2. Java 各版本的新特性](#2-java-各版本的新特性) + * [3. Java 与 C 的区别](#3-java-与-c-的区别) + * [4. JRE or JDK](#4-jre-or-jdk) + * [二十、JDK 中的设计模式](#二十jdk-中的设计模式) + * [(一)、创建型](#一创建型) + * [1. 单例模式](#1-单例模式) + * [2. 简单工厂模式](#2-简单工厂模式) + * [3. 工厂方法模式](#3-工厂方法模式) + * [4. 抽象工厂](#4-抽象工厂) + * [5. 生成器模式](#5-生成器模式) + * [6. 原型模式](#6-原型模式) + * [(二)、行为型](#二行为型) + * [1. 责任链](#1-责任链) + * [2. 命令模式](#2-命令模式) + * [3. 解释器模式](#3-解释器模式) + * [4. 迭代器](#4-迭代器) + * [5. 中间人模式](#5-中间人模式) + * [6. 备忘录模式](#6-备忘录模式) + * [7. 观察者模式](#7-观察者模式) + * [8. 策略模式](#8-策略模式) + * [9. 模板方法](#9-模板方法) + * [10. 访问者模式](#10-访问者模式) + * [11. 空对象模式](#11-空对象模式) + * [(三)、结构型](#三结构型) + * [1. 适配器](#1-适配器) + * [2. 桥接模式](#2-桥接模式) + * [3. 组合模式](#3-组合模式) + * [4. 装饰者模式](#4-装饰者模式) + * [5. 蝇量模式](#5-蝇量模式) + * [6. 动态代理](#6-动态代理) + * [二十一、分布式基础](#二十一分布式基础) + * [(一)、基本概念](#一基本概念-1) + * [1. 异常](#1-异常) + * [⑴. 服务器宕机](#-服务器宕机) + * [⑵. 网络异常](#-网络异常) + * [⑶. 磁盘故障](#-磁盘故障) + * [2. 超时](#2-超时) + * [3. 衡量指标](#3-衡量指标) + * [⑴. 性能](#-性能) + * [⑵. 可用性](#-可用性) + * [⑶. 一致性](#-一致性) + * [⑷. 可扩展性](#-可扩展性) + * [(二)、数据分布](#二数据分布) + * [1. 哈希分布](#1-哈希分布) + * [2. 顺序分布](#2-顺序分布) + * [(三)、负载均衡](#三负载均衡) + * [(四)、复制](#四复制) + * [1. 强同步复制协议](#1-强同步复制协议) + * [2. 异步复制协议](#2-异步复制协议) + * [(五)、CAP](#五cap) + * [(六)、BASE](#六base) + * [1. 基本可用](#1-基本可用) + * [2. 软状态](#2-软状态) + * [3. 最终一致性](#3-最终一致性) + * [(七)、容错](#七容错) + * [1. 故障检测](#1-故障检测) + * [2. 故障恢复](#2-故障恢复) + * [(八)、CDN 架构](#八cdn-架构) + * [二十二、一致性协议](#二十二一致性协议) + * [(一)、两阶段提交协议](#一两阶段提交协议) + * [1. 运行过程](#1-运行过程) + * [2. 存在的问题](#2-存在的问题) + * [(二)、Paxos 协议](#二paxos-协议) + * [1. 执行过程](#1-执行过程) + * [2. 约束条件](#2-约束条件) + * [⑴. 正确性](#-正确性) + * [⑵. 可终止性](#-可终止性) + * [(三)、Raft 协议](#三raft-协议) + * [1. 单个 Candidate 的竞选](#1-单个-candidate-的竞选) + * [2. 多个 Candidate 竞选](#2-多个-candidate-竞选) + * [3. 日志复制](#3-日志复制) + * [(四)、拜占庭将军问题](#四拜占庭将军问题) + * [二十三、分布式问题分析](#二十三分布式问题分析) + * [(一)、谈谈业务中使用分布式的场景](#一谈谈业务中使用分布式的场景) + * [(二)、分布式事务](#二分布式事务) + * [1. 产生原因](#1-产生原因) + * [2. 应用场景](#2-应用场景) + * [3. 解决方案](#3-解决方案) + * [⑴. 两阶段提交协议](#-两阶段提交协议) + * [⑵. 消息中间件](#-消息中间件) + * [①. 消息处理模型](#-消息处理模型) + * [②. 消息的可靠性](#-消息的可靠性) + * [(三)、负载均衡的算法与实现](#三负载均衡的算法与实现) + * [1. 算法](#1-算法) + * [⑴. 轮询(Round Robin)](#-轮询round-robin) + * [⑵. 加权轮询(Weighted Round Robbin)](#-加权轮询weighted-round-robbin) + * [⑶. 最少连接(least Connections)](#-最少连接least-connections) + * [⑷. 加权最小连接(Weighted LeastConnection)](#-加权最小连接weighted-leastconnection) + * [⑸. 随机算法(Random)](#-随机算法random) + * [2. 实现](#2-实现) + * [⑴. HTTP 重定向](#-http-重定向) + * [⑵. DNS 重定向](#-dns-重定向) + * [⑶. 修改 MAC 地址](#-修改-mac-地址) + * [⑷. 修改 IP 地址](#-修改-ip-地址) + * [⑸. 代理自动配置](#-代理自动配置) + * [(四)、分布式锁](#四分布式锁) + * [1. 使用场景](#1-使用场景) + * [2. 实现方式](#2-实现方式) + * [⑴. 数据库分布式锁](#-数据库分布式锁) + * [⑵. Redis 分布式锁](#-redis-分布式锁) + * [⑶. Zookeeper 分布式锁](#-zookeeper-分布式锁) + * [(五)、分布式 Session](#五分布式-session) + * [1. Sticky Sessions](#1-sticky-sessions) + * [2. Session Replication](#2-session-replication) + * [3. Persistent DataStore](#3-persistent-datastore) + * [4. In-Memory DataStore](#4-in-memory-datastore) + * [(六)、分库与分表带来的分布式困境与应对之策](#六分库与分表带来的分布式困境与应对之策) + * [1. 事务问题](#1-事务问题) + * [2. 查询问题](#2-查询问题) + * [3. ID 唯一性](#3-id-唯一性) + * [二十四、Git](#二十四git) + * [1. 学习资料](#1-学习资料) + * [2. 集中式与分布式](#2-集中式与分布式) + * [3. Git 的中心服务器](#3-git-的中心服务器) + * [4. Git 工作流](#4-git-工作流) + * [5. 分支实现](#5-分支实现) + * [6. 冲突](#6-冲突) + * [7. Fast forward](#7-fast-forward) + * [8. 分支管理策略](#8-分支管理策略) + * [9. 储藏(Stashing)](#9-储藏stashing) + * [10. SSH 传输设置](#10-ssh-传输设置) + * [11. .gitignore 文件](#11-gitignore-文件) + * [12. Git 命令一览](#12-git-命令一览) + * [二十五、正则表达式](#二十五正则表达式) + * [(一)、概述](#一概述-2) + * [(二)、匹配单个字符](#二匹配单个字符) + * [(三)、匹配一组字符](#三匹配一组字符) + * [(四)、使用元字符](#四使用元字符) + * [1. 匹配空白字符](#1-匹配空白字符) + * [2. 匹配特定的字符类别](#2-匹配特定的字符类别) + * [⑴. 数字元字符](#-数字元字符) + * [⑵. 字母数字元字符](#-字母数字元字符) + * [⑶. 空白字符元字符](#-空白字符元字符) + * [(五)、重复匹配](#五重复匹配) + * [(六)、位置匹配](#六位置匹配) + * [1. 单词边界](#1-单词边界) + * [2. 字符串边界](#2-字符串边界) + * [(七)、使用子表达式](#七使用子表达式) + * [(八)、回溯引用](#八回溯引用) + * [1. 替换](#1-替换) + * [2. 大小写转换](#2-大小写转换) + * [(九)、前后查找](#九前后查找) + * [(十)、嵌入条件](#十嵌入条件) + * [1. 回溯引用条件](#1-回溯引用条件) + * [2. 前后查找条件](#2-前后查找条件) + * [二十六、重构](#二十六重构) + * [(一)、第一个案例](#一第一个案例) + * [(二)、重构原则](#二重构原则) + * [1. 定义](#1-定义) + * [2. 为何重构](#2-为何重构) + * [3. 三次法则](#3-三次法则) + * [4. 间接层与重构](#4-间接层与重构) + * [5. 修改接口](#5-修改接口) + * [6. 何时不该重构](#6-何时不该重构) + * [7. 重构与设计](#7-重构与设计) + * [8. 重构与性能](#8-重构与性能) + * [(三)、代码的坏味道](#三代码的坏味道) + * [1. 重复代码](#1-重复代码) + * [2. 过长函数](#2-过长函数) + * [3. 过大的类](#3-过大的类) + * [4. 过长的参数列表](#4-过长的参数列表) + * [5. 发散式变化](#5-发散式变化) + * [6. 散弹式修改](#6-散弹式修改) + * [7. 依恋情结](#7-依恋情结) + * [8. 数据泥团](#8-数据泥团) + * [9. 基本类型偏执](#9-基本类型偏执) + * [10. switch 惊悚现身](#10-switch-惊悚现身) + * [11. 平行继承体系](#11-平行继承体系) + * [12. 冗余类](#12-冗余类) + * [13. 夸夸其谈未来性](#13-夸夸其谈未来性) + * [14. 令人迷惑的暂时字段](#14-令人迷惑的暂时字段) + * [15. 过度耦合的消息链](#15-过度耦合的消息链) + * [16. 中间人](#16-中间人) + * [17. 狎昵关系](#17-狎昵关系) + * [18. 异曲同工的类](#18-异曲同工的类) + * [19. 不完美的类库](#19-不完美的类库) + * [20. 幼稚的数据类](#20-幼稚的数据类) + * [21. 被拒绝的馈赠](#21-被拒绝的馈赠) + * [22. 过多的注释](#22-过多的注释) + * [(四)、构筑测试体系](#四构筑测试体系) + * [(五)、重新组织函数](#五重新组织函数) + * [1. 提炼函数](#1-提炼函数) + * [2. 内联函数](#2-内联函数) + * [3. 内联临时变量](#3-内联临时变量) + * [4. 以查询取代临时变量](#4-以查询取代临时变量) + * [5. 引起解释变量](#5-引起解释变量) + * [6. 分解临时变量](#6-分解临时变量) + * [7. 移除对参数的赋值](#7-移除对参数的赋值) + * [8. 以函数对象取代函数](#8-以函数对象取代函数) + * [9. 替换算法](#9-替换算法) + * [(六)、在对象之间搬移特性](#六在对象之间搬移特性) + * [1. 搬移函数](#1-搬移函数) + * [2. 搬移字段](#2-搬移字段) + * [3. 提炼类](#3-提炼类) + * [4. 将类内联化](#4-将类内联化) + * [5. 隐藏委托关系](#5-隐藏委托关系) + * [6. 移除中间人](#6-移除中间人) + * [7. 引入外加函数](#7-引入外加函数) + * [8. 引入本地扩展](#8-引入本地扩展) + * [(七)、重新组织数据](#七重新组织数据) + * [1. 自封装字段](#1-自封装字段) + * [2. 以对象取代数据值](#2-以对象取代数据值) + * [3. 将值对象改成引用对象](#3-将值对象改成引用对象) + * [4. 将引用对象改为值对象](#4-将引用对象改为值对象) + * [5. 以对象取代数组](#5-以对象取代数组) + * [6. 赋值被监视数据](#6-赋值被监视数据) + * [7. 将单向关联改为双向关联](#7-将单向关联改为双向关联) + * [8. 将双向关联改为单向关联](#8-将双向关联改为单向关联) + * [9. 以字面常量取代魔法数](#9-以字面常量取代魔法数) + * [10. 封装字段](#10-封装字段) + * [11. 封装集合](#11-封装集合) + * [12. 以数据类取代记录](#12-以数据类取代记录) + * [13. 以类取代类型码](#13-以类取代类型码) + * [14. 以子类取代类型码](#14-以子类取代类型码) + * [15. 以 State/Strategy 取代类型码](#15-以-statestrategy-取代类型码) + * [16. 以字段取代子类](#16-以字段取代子类) + * [(八)、简化条件表达式](#八简化条件表达式) + * [1. 分解条件表达式](#1-分解条件表达式) + * [2. 合并条件表达式](#2-合并条件表达式) + * [3. 合并重复的条件片段](#3-合并重复的条件片段) + * [4. 移除控制标记](#4-移除控制标记) + * [5. 以卫语句取代嵌套条件表达式](#5-以卫语句取代嵌套条件表达式) + * [6. 以多态取代条件表达式](#6-以多态取代条件表达式) + * [7. 引入 Null 对象](#7-引入-null-对象) + * [8. 引入断言](#8-引入断言) + * [(九)、简化函数调用](#九简化函数调用) + * [1. 函数改名](#1-函数改名) + * [2. 添加参数](#2-添加参数) + * [3. 移除参数](#3-移除参数) + * [4. 将查询函数和修改函数分离](#4-将查询函数和修改函数分离) + * [5. 令函数携带参数](#5-令函数携带参数) + * [6. 以明确函数取代参数](#6-以明确函数取代参数) + * [7. 保持对象完整](#7-保持对象完整) + * [8. 以函数取代参数](#8-以函数取代参数) + * [9. 引入参数对象](#9-引入参数对象) + * [10. 移除设值函数](#10-移除设值函数) + * [11. 隐藏函数](#11-隐藏函数) + * [12. 以工厂函数取代构造函数](#12-以工厂函数取代构造函数) + * [13. 封装向下转型](#13-封装向下转型) + * [14. 以异常取代错误码](#14-以异常取代错误码) + * [15. 以测试取代异常](#15-以测试取代异常) + * [(十)、处理概括关系](#十处理概括关系) + * [1. 字段上移](#1-字段上移) + * [2. 函数上移](#2-函数上移) + * [3. 构造函数本体上移](#3-构造函数本体上移) + * [4. 函数下移](#4-函数下移) + * [5. 字段下移](#5-字段下移) + * [6. 提炼子类](#6-提炼子类) + * [7. 提炼超类](#7-提炼超类) + * [8. 提炼接口](#8-提炼接口) + * [9. 折叠继承体系](#9-折叠继承体系) + * [10. 塑造模板函数](#10-塑造模板函数) + * [11. 以委托取代继承](#11-以委托取代继承) + * [12. 以继承取代委托](#12-以继承取代委托) + * [二十七、代码可读性](#二十七代码可读性) + * [1. 可读性的重要性](#1-可读性的重要性) + * [2. 用名字表达代码含义](#2-用名字表达代码含义) + * [3. 名字不能带来歧义](#3-名字不能带来歧义) + * [4. 良好的代码风格](#4-良好的代码风格) + * [5. 编写注释](#5-编写注释) + * [6. 如何编写注释](#6-如何编写注释) + * [7. 提高控制流的可读性](#7-提高控制流的可读性) + * [8. 拆分长表达式](#8-拆分长表达式) + * [9. 变量与可读性](#9-变量与可读性) + * [10. 抽取函数](#10-抽取函数) + * [11. 一次只做一件事](#11-一次只做一件事) + * [12. 用自然语言表述代码](#12-用自然语言表述代码) + * [13. 减少代码量](#13-减少代码量) +*** +# 一、校招真题题解 +## 前言 +省略的代码: +``` +import java.util.*; +``` +``` +public class Solution { +} +``` +``` +public class Main { + public static void main(String[] args) { + Scanner in = new Scanner(System.in); + while (in.hasNext()) { + } + } +} +``` +## 1. 小米-小米Git +- 重建多叉树 +- 使用 LCA +``` +private class TreeNode { + int id; + List childs = new ArrayList<>(); + TreeNode(int id) { + this.id = id; + } +} +public int getSplitNode(String[] matrix, int indexA, int indexB) { + int n = matrix.length; + boolean[][] linked = new boolean[n][n]; // 重建邻接矩阵 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + linked[i][j] = matrix[i].charAt(j) == '1'; + } + } + TreeNode tree = constructTree(linked, 0); + TreeNode ancestor = LCA(tree, new TreeNode(indexA), new TreeNode(indexB)); + return ancestor.id; +} +private TreeNode constructTree(boolean[][] linked, int root) { + TreeNode tree = new TreeNode(root); + for (int i = 0; i < linked[root].length; i++) { + if (linked[root][i]) { + linked[i][root] = false; // 因为题目给的邻接矩阵是双向的,在这里需要把它转为单向的 + tree.childs.add(constructTree(links, i)); + } + } + return tree; +} +private TreeNode LCA(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || root.id == p.id || root.id == q.id) return root; + TreeNode ancestor = null; + int cnt = 0; + for (int i = 0; i < root.childs.size(); i++) { + TreeNode tmp = LCA(root.childs.get(i), p, q); + if (tmp != null) { + ancestor = tmp; + cnt++; + } + } + return cnt == 2 ? root : ancestor; +} +``` +## 2. 小米-懂二进制 +对两个数进行异或,结果的二进制表示为 1 的那一位就是两个数不同的位。 +``` +public int countBitDiff(int m, int n) { + return Integer.bitCount(m ^ n); +} +``` +## 3. 小米-中国牛市 +背包问题,可以设一个大小为 2 的背包。 + +状态转移方程如下: +``` +dp[i, j] = max(dp[i, j-1], prices[j] - prices[jj] + dp[i-1, jj]) { jj in range of [0, j-1] } = max(dp[i, j-1], prices[j] + max(dp[i-1, jj] - prices[jj])) +``` +``` +public int calculateMax(int[] prices) { + int n = prices.length; + int[][] dp = new int[3][n]; + for (int i = 1; i <= 2; i++) { + int localMax = dp[i - 1][0] - prices[0]; + for (int j = 1; j < n; j++) { + dp[i][j] = Math.max(dp[i][j - 1], prices[j] + localMax); + localMax = Math.max(localMax, dp[i - 1][j] - prices[j]); + } + } + return dp[2][n - 1]; +} +``` +## 4. 微软-LUCKY STRING +- 斐波那契数列可以预计算; +- 从头到尾遍历字符串的过程,每一轮循环都使用一个 Set 来保存从 i 到 j 出现的字符,并且 Set 保证了字符都不同,因此 Set 的大小就是不同字符的个数。 +``` +Set fibSet = new HashSet<>(Arrays.asList(1, 2, 3, 5, 8, 13, 21, 34, 55, 89)); +Scanner in = new Scanner(System.in); +String str = in.nextLine(); +int n = str.length(); +Set ret = new HashSet<>(); +for (int i = 0; i < n; i++) { + Set set = new HashSet<>(); + for (int j = i; j < n; j++) { + set.add(str.charAt(j)); + int cnt = set.size(); + if (fibSet.contains(cnt)) { + ret.add(str.substring(i, j + 1)); + } + } +} +String[] arr = ret.toArray(new String[ret.size()]); +Arrays.sort(arr); +for (String s : arr) { + System.out.println(s); +} +``` +## 5. 微软-Numeric Keypad +``` +private static int[][] canReach = { + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // 0 + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 1 + {1, 0, 1, 1, 0, 1, 1, 0, 1, 1}, // 2 + {0, 0, 0, 1, 0, 0, 1, 0, 0, 1}, // 3 + {1, 0, 0, 0, 1, 1, 1, 1, 1, 1}, // 4 + {1, 0, 0, 0, 0, 1, 1, 0, 1, 1}, // 5 + {0, 0, 0, 0, 0, 0, 1, 0, 0, 1}, // 6 + {1, 0, 0, 0, 0, 0, 0, 1, 1, 1}, // 7 + {1, 0, 0, 0, 0, 0, 0, 0, 1, 1}, // 8 + {0, 0, 0, 0, 0, 0, 0, 0, 0, 1} // 9 +}; +private static boolean isLegal(char[] chars, int idx) { + if (idx >= chars.length || idx < 0) return true; + int cur = chars[idx] - '0'; + int next = chars[idx + 1] - '0'; + return canReach[cur][next] == 1; +} +public static void main(String[] args) { + Scanner in = new Scanner(System.in); + int T = Integer.valueOf(in.nextLine()); + for (int i = 0; i < T; i++) { + String line = in.nextLine(); + char[] chars = line.toCharArray(); + for (int j = 0; j < chars.length - 1; j++) { + while (!isLegal(chars, j)) { + if (--chars[j + 1] < '0') { + chars[j--]--; + } + for (int k = j + 2; k < chars.length; k++) { + chars[k] = '9'; + } + } + } + System.out.println(new String(chars)); + } +} +``` +## 6. 微软-Spring Outing +下面以 N = 3,K = 4 来进行讨论。 + +初始时,令第 0 个地方成为待定地点,也就是呆在家里。 + +从第 4 个地点开始投票,每个人只需要比较第 4 个地方和第 0 个地方的优先级,里,如果超过半数的人选择了第 4 个地方,那么更新第 4 个地方成为待定地点。 + +从后往前不断重复以上步骤,不断更新待定地点,直到所有地方都已经投票。 + +上面的讨论中,先令第 0 个地点成为待定地点,是因为这样的话第 4 个地点就只需要和这个地点进行比较,而不用考虑其它情况。如果最开始先令第 1 个地点成为待定地点,那么在对第 2 个地点进行投票时,每个人不仅要考虑第 2 个地点与第 1 个地点的优先级,也要考虑与其后投票地点的优先级。 +``` +int N = in.nextInt(); +int K = in.nextInt(); +int[][] votes = new int[N][K + 1]; +for (int i = 0; i < N; i++) { + for (int j = 0; j < K + 1; j++) { + int place = in.nextInt(); + votes[i][place] = j; + } +} +int ret = 0; +for (int place = K; place > 0; place--) { + int cnt = 0; + for (int i = 0; i < N; i++) { + if (votes[i][place] < votes[i][ret]) { + cnt++; + } + } + if (cnt > N / 2) { + ret = place; + } +} +System.out.println(ret == 0 ? "otaku" : ret); +``` +## 7. 华为-最高分是多少 +``` +int N = in.nextInt(); +int M = in.nextInt(); +int[] scores = new int[N]; +for (int i = 0; i < N; i++) { + scores[i] = in.nextInt(); +} +for (int i = 0; i < M; i++) { + String str = in.next(); + if (str.equals("U")) { + int id = in.nextInt() - 1; + int newScore = in.nextInt(); + scores[id] = newScore; + } else { + int idBegin = in.nextInt() - 1; + int idEnd = in.nextInt() - 1; + int ret = 0; + if (idBegin > idEnd) { + int t = idBegin; + idBegin = idEnd; + idEnd = t; + } + for (int j = idBegin; j <= idEnd; j++) { + ret = Math.max(ret, scores[j]); + } + System.out.println(ret); + } +} +``` +## 8. 华为-简单错误记录 +``` +HashMap map = new LinkedHashMap<>(); +while (in.hasNextLine()) { + String s = in.nextLine(); + String key = s.substring(s.lastIndexOf('\\') + 1); + map.put(key, map.containsKey(key) ? map.get(key) + 1 : 1); +} +List> list = new LinkedList<>(map.entrySet()); +Collections.sort(list, (o1, o2) -> o2.getValue() - o1.getValue()); +for (int i = 0; i < 8 && i < list.size(); i++) { + String[] token = list.get(i).getKey().split(" "); + String filename = token[0]; + String line = token[1]; + if (filename.length() > 16) filename = filename.substring(filename.length() - 16); + System.out.println(filename + " " + line + " " + list.get(i).getValue()); +} +``` +## 9. 华为-扑克牌大小 +``` +public class Main { + private Map map = new HashMap<>(); + public Main() { + map.put("3", 0); + map.put("4", 1); + map.put("5", 2); + map.put("6", 3); + map.put("7", 4); + map.put("8", 5); + map.put("9", 6); + map.put("10", 7); + map.put("J", 8); + map.put("Q", 9); + map.put("K", 10); + map.put("A", 11); + map.put("2", 12); + map.put("joker", 13); + map.put("JOKER ", 14); + } + private String play(String s1, String s2) { + String[] token1 = s1.split(" "); + String[] token2 = s2.split(" "); + CardType type1 = computeCardType(token1); + CardType type2 = computeCardType(token2); + if (type1 == CardType.DoubleJoker) return s1; + if (type2 == CardType.DoubleJoker) return s2; + if (type1 == CardType.Bomb && type2 != CardType.Bomb) return s1; + if (type2 == CardType.Bomb && type1 != CardType.Bomb) return s2; + if (type1 != type2 || token1.length != token2.length) return "ERROR"; + for (int i = 0; i < token1.length; i++) { + int val1 = map.get(token1[i]); + int val2 = map.get(token2[i]); + if (val1 != val2) return val1 > val2 ? s1 : s2; + } + return "ERROR"; + } + private CardType computeCardType(String[] token) { + boolean hasjoker = false, hasJOKER = false; + for (int i = 0; i < token.length; i++) { + if (token[i].equals("joker")) hasjoker = true; + else if (token[i].equals("JOKER")) hasJOKER = true; + } + if (hasjoker && hasJOKER) return CardType.DoubleJoker; + int maxContinueLen = 1; + int curContinueLen = 1; + String curValue = token[0]; + for (int i = 1; i < token.length; i++) { + if (token[i].equals(curValue)) curContinueLen++; + else { + curContinueLen = 1; + curValue = token[i]; + } + maxContinueLen = Math.max(maxContinueLen, curContinueLen); + } + if (maxContinueLen == 4) return CardType.Bomb; + if (maxContinueLen == 3) return CardType.Triple; + if (maxContinueLen == 2) return CardType.Double; + boolean isStraight = true; + for (int i = 1; i < token.length; i++) { + if (map.get(token[i]) - map.get(token[i - 1]) != 1) { + isStraight = false; + break; + } + } + if (isStraight && token.length == 5) return CardType.Straight; + return CardType.Sigal; + } + private enum CardType { + DoubleJoker, Bomb, Sigal, Double, Triple, Straight; + } + public static void main(String[] args) { + Main main = new Main(); + Scanner in = new Scanner(System.in); + while (in.hasNextLine()) { + String s = in.nextLine(); + String[] token = s.split("-"); + System.out.println(main.play(token[0], token[1])); + } + } +} +``` +## 10. 去哪儿-二分查找 +对于有重复元素的有序数组,二分查找需要注意以下要点: + +- if (val <= A[m]) h = m; +- 因为 h 的赋值为 m 而不是 m - 1,因此 while 循环的条件也就为 l < h。(如果是 m - 1 循环条件为 l <= h) +``` +public int getPos(int[] A, int n, int val) { + int l = 0, h = n - 1; + while (l < h) { + int m = l + (h - l) / 2; + if (val <= A[m]) h = m; + else l = m + 1; + } + return A[h] == val ? h : -1; +} +``` +## 11. 去哪儿-首个重复字符 +``` +public char findFirstRepeat(String A, int n) { + boolean[] hasAppear = new boolean[256]; + for (int i = 0; i < n; i++) { + char c = A.charAt(i); + if(hasAppear[c]) return c; + hasAppear[c] = true; + } + return ' '; +} +``` +## 12. 去哪儿-寻找Coder +``` +public String[] findCoder(String[] A, int n) { + List> list = new ArrayList<>(); + for (String s : A) { + int cnt = 0; + String t = s.toLowerCase(); + int idx = -1; + while (true) { + idx = t.indexOf("coder", idx + 1); + if (idx == -1) break; + cnt++; + } + if (cnt != 0) { + list.add(new Pair<>(s, cnt)); + } + } + Collections.sort(list, (o1, o2) -> (o2.getValue() - o1.getValue())); + String[] ret = new String[list.size()]; + for (int i = 0; i < list.size(); i++) { + ret[i] = list.get(i).getKey(); + } + return ret; +} +// 牛客网无法导入 javafx.util.Pair,这里就自己实现一下 Pair 类 +private class Pair { + T t; + K k; + Pair(T t, K k) { + this.t = t; + this.k = k; + } + T getKey() { + return t; + } + K getValue() { + return k; + } +} +``` +## 13. 美团-最大差值 +贪心策略。 +``` +public int getDis(int[] A, int n) { + int max = 0; + int soFarMin = A[0]; + for (int i = 1; i < n; i++) { + if(soFarMin > A[i]) soFarMin = A[i]; + else max = Math.max(max, A[i]- soFarMin); + } + return max; +} +``` +## 14. 美团-棋子翻转 +``` +public int[][] flipChess(int[][] A, int[][] f) { + int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + for (int[] ff : f) { + for (int[] dd : direction) { + int r = ff[0] + dd[0] - 1, c = ff[1] + dd[1] - 1; + if(r < 0 || r > 3 || c < 0 || c > 3) continue; + A[r][c] ^= 1; + } + } + return A; +} +``` +## 15. 美团-拜访 +``` +private Set paths; +private List curPath; +public int countPath(int[][] map, int n, int m) { + paths = new HashSet<>(); + curPath = new ArrayList<>(); + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + if (map[i][j] == 1) { + map[i][j] = -1; + int[][] leftRightDirection = {{1, 0}, {-1, 0}}; + int[][] topDownDirection = {{0, 1}, {0, -1}}; + for (int[] lr : leftRightDirection) { + for (int[] td : topDownDirection) { + int[][] directions = {lr, td}; + backtracking(map, n, m, i, j, directions); + } + } + return paths.size(); + } + } + } + return 0; +} +private void backtracking(int[][] map, int n, int m, int r, int c, int[][] directions) { + if (map[r][c] == 2) { + String path = ""; + for (int num : curPath) { + path += num; + } + paths.add(path); + return; + } + for (int i = 0; i < directions.length; i++) { + int nextR = r + directions[i][0]; + int nextC = c + directions[i][1]; + if (nextR < 0 || nextR >= n || nextC < 0 || nextC >= m || map[nextR][nextC] == -1) continue; + map[nextR][nextC] = map[nextR][nextC] == 2 ? 2 : -1; + curPath.add(nextR); + curPath.add(nextC); + backtracking(map, n, m, nextR, nextC, directions); + curPath.remove(curPath.size() - 1); + curPath.remove(curPath.size() - 1); + map[nextR][nextC] = map[nextR][nextC] == 2 ? 2 : 0; + } +} +``` +## 16. 美团-直方图内最大矩形 +``` +public int countArea(int[] A, int n) { + int max = 0; + for (int i = 0; i < n; i++) { + int min = A[i]; + for (int j = i; j < n; j++) { + min = Math.min(min, A[j]); + max = Math.max(max, min * (j - i + 1)); + } + } + return max; +} +``` +## 17. 美团-字符串计数 +字符串都是小写字符,可以把字符串当成是 26 进制。但是字典序的比较和普通的整数比较不同,是从左往右进行比较,例如 “ac” 和 “abc”,字典序的比较结果为 “ac” > “abc”,如果按照整数方法比较,因为 “abc” 是三位数,显然更大。 + +由于两个字符串的长度可能不想等,在 s1 空白部分和 s2 对应部分进行比较时,应该把 s1 的空白部分看成是 ‘a’ 字符进行填充的。 + +还有一点要注意的是,s1 到 s2 长度为 leni 的字符串个数只比较前面 i 个字符。例如 ‘aaa’ 和 ‘bbb’ ,长度为 2 的个数为 ‘aa’ 到 ‘bb’ 的字符串个数,不需要考虑后面部分的字符。 + +在统计个数时,从 len1 开始一直遍历到最大合法长度,每次循环都统计长度为 i 的子字符串个数。 +``` +String s1 = in.next(); +String s2 = in.next(); +int len1 = in.nextInt(); +int len2 = in.nextInt(); +int len = Math.min(s2.length(), len2); +int[] subtractArr = new int[len]; +for (int i = 0; i < len; i++) { + char c1 = i < s1.length() ? s1.charAt(i) : 'a'; + char c2 = s2.charAt(i); + subtractArr[i] = c2 - c1; +} +int ret = 0; +for (int i = len1; i <= len; i++) { + for (int j = 0; j < i; j++) { + ret += subtractArr[j] * Math.pow(26, i - j - 1); + } +} +System.out.println(ret - 1); +``` +## 18. 美团-平均年龄 +``` +int W = in.nextInt(); +double Y = in.nextDouble(); +double x = in.nextDouble(); +int N = in.nextInt(); +while (N-- > 0) { + Y++; // 老员工每年年龄都要加 1 + Y += (21 - Y) * x; +} +System.out.println((int) Math.ceil(Y)); +``` +## 19. 百度-罪犯转移 +部分和问题,将每次求的部分和缓存起来。 +``` +int n = in.nextInt(); +int t = in.nextInt(); +int c = in.nextInt(); +int[] values = new int[n]; +for (int i = 0; i < n; i++) { + values[i] = in.nextInt(); +} +int cnt = 0; +int totalValue = 0; +for (int s = 0, e = c - 1; e < n; s++, e++) { + if (s == 0) { + for (int j = 0; j < c; j++) totalValue += values[j]; + } else { + totalValue = totalValue - values[s - 1] + values[e]; + } + if (totalValue <= t) cnt++; +} +System.out.println(cnt); +``` +## 20. 百度-裁减网格纸 +``` +int n = in.nextInt(); +int minX, minY, maxX, maxY; +minX = minY = Integer.MAX_VALUE; +maxX = maxY = Integer.MIN_VALUE; +for (int i = 0; i < n; i++) { + int x = in.nextInt(); + int y = in.nextInt(); + minX = Math.min(minX, x); + minY = Math.min(minY, y); + maxX = Math.max(maxX, x); + maxY = Math.max(maxY, y); +} +System.out.println((int) Math.pow(Math.max(maxX - minX, maxY - minY), 2)); +``` +## 21. 百度-钓鱼比赛 +P ( 至少钓一条鱼 ) = 1 - P ( 一条也钓不到 ) + +坑:读取概率矩阵的时候,需要一行一行进行读取,而不能直接用 in.nextDouble()。 +``` +public static void main(String[] args) { + Scanner in = new Scanner(System.in); + while (in.hasNext()) { + int n = in.nextInt(); + int m = in.nextInt(); + int x = in.nextInt(); + int y = in.nextInt(); + int t = in.nextInt(); + in.nextLine(); // 坑 + double pcc = 0.0; + double sum = 0.0; + for (int i = 1; i <= n; i++) { + String[] token = in.nextLine().split(" "); // 坑 + for (int j = 1; j <= m; j++) { + double p = Double.parseDouble(token[j - 1]); + // double p = in.nextDouble(); + sum += p; + if (i == x && j == y) { + pcc = p; + } + } + } + double pss = sum / (n * m); + pcc = computePOfIRT(pcc, t); + pss = computePOfIRT(pss, t); + System.out.println(pcc > pss ? "cc" : pss > pcc ? "ss" : "equal"); + System.out.printf("%.2f\n", Math.max(pcc, pss)); + } +} +// compute probability of independent repeated trials +private static double computePOfIRT(double p, int t) { + return 1 - Math.pow((1 - p), t); +} +``` +## 22. 百度-蘑菇阵 +这题用回溯会超时,需要用 DP。 + +dp[i][j] 表示到达 (i,j) 位置不会触碰蘑菇的概率。对于 N*M 矩阵,如果 i == N || j == M,那么 (i,j) 只能有一个移动方向;其它情况下能有两个移动方向。 + +考虑以下矩阵,其中第 3 行和第 3 列只能往一个方向移动,而其它位置可以有两个方向移动。 +``` +int N = in.nextInt(); +int M = in.nextInt(); +int K = in.nextInt(); +boolean[][] mushroom = new boolean[N][M]; +while (K-- > 0) { + int x = in.nextInt(); + int y = in.nextInt(); + mushroom[x - 1][y - 1] = true; +} +double[][] dp = new double[N][M]; +dp[0][0] = 1; +for (int i = 0; i < N; i++) { + for (int j = 0; j < M; j++) { + if (mushroom[i][j]) dp[i][j] = 0; + else { + double cur = dp[i][j]; + if (i == N - 1 && j == M - 1) break; + if (i == N - 1) dp[i][j + 1] += cur; + else if (j == M - 1) dp[i + 1][j] += cur; + else { + dp[i][j + 1] += cur / 2; + dp[i + 1][j] += cur / 2; + } + } + } +} +System.out.printf("%.2f\n", dp[N - 1][M - 1]); +``` + +# 二、计算机网络 + +## (一)、概述 + +### 1. 网络的网络 + +### 2. ISP + +### 3. 互联网的组成 + +### 4. 主机之间的通信方式 + +### 5. 电路交换与分组交换 + +#### ⑴. 电路交换 + +#### ⑵. 报文交换 + +#### ⑶. 分组交换 + +### 6. 时延 + +#### ⑴. 发送时延 + +#### ⑵. 传播时延 + +#### ⑶. 处理时延 + +#### ⑷. 排队时延 + +### 7. 计算机网络体系结构* + +#### ⑴. 七层协议 + +#### ⑵. 五层协议 + +#### ⑶. 数据在各层之间的传递过程 + +#### ⑷. TCP/IP 体系结构 + +## (二)、物理层 + +### 1. 通信方式 + +### 2. 带通调制 + +### 3. 信道复用技术 + +#### ⑴. 频分复用、时分复用 + +#### ⑵. 统计时分复用 + +#### ⑶. 波分复用 + +#### ⑷. 码分复用 + +## (三)、数据链路层 + +### 1. 信道分类 + +### 2. 三个基本问题 + +#### ⑴. 封装成帧 + +#### ⑵. 透明传输 + +#### ⑶. 差错检测 + +### 3. 局域网 + +### 4. PPP 协议 + +### 5. CSMA/CD 协议* + +### 6. 扩展局域网* + +#### ⑴. 在物理层进行扩展 + +#### ⑵. 在链路层进行扩展 + +#### ⑶. 虚拟局域网 + +### 7. MAC 层* + +## (四)、网络层 + +### 1. 网际协议 IP 概述 + +### 2. IP 数据报格式 + +### 3. IP 地址编址方式 + +#### ⑴. 分类 + +#### ⑵. 子网划分 + +#### ⑶. 无分类 + +### 4. IP 地址和 MAC 地址 + +### 5. 地址解析协议 ARP + +### 6. 路由器的结构 + +### 7. 路由器分组转发流程 + +### 8. 路由选择协议 + +#### ⑴. 内部网关协议 RIP + +#### ⑵. 内部网关协议 OSPF + +#### ⑶. 外部网关协议 BGP + +### 9. 网际控制报文协议 ICMP + +### 10. 分组网间探测 PING + +### 11. 虚拟专用网 VPN + +### 12. 网络地址转换 NAT + +## (五)、传输层 + +### 1. UDP 和 TCP 的特点 + +### 2. UDP 首部格式 + +### 3. TCP 首部格式 + +### 4. TCP 的三次握手 + +### 5. TCP 的四次挥手 + +### 6. TCP 滑动窗口 + +### 7. TCP 可靠传输 + +### 8. TCP 流量控制 + +### 9. TCP 拥塞控制 + +### 10. 慢开始与拥塞避免 + +### 11. 快重传与快恢复 + +## (六)、应用层 + +### 1. 域名系统 DNS + +#### ⑴. 层次结构 + +#### ⑵. 解析过程 + +### 2. 文件传输协议 FTP + +### 3. 远程终端协议 TELNET + +### 4. 电子邮件协议 + +#### ⑴. POP3 + +#### ⑵. IMAP + +#### ⑶. SMTP + +### 5. 动态主机配置协议 DHCP + +### 6. 点对点传输 P2P + +### 7. Web 页面请求过程 + +#### ⑴. DHCP 配置主机信息 + +#### ⑵. ARP 解析 MAC 地址 + +#### ⑶. DNS 解析域名 + +#### ⑷. HTTP 请求页面 + +### 8. 常用端口 + +# 三、HTTP + +## (一)、基本概念 + +### 1. Web 基础 + +### 2. URL + +### 3. 请求和响应报文 + +#### ⑴. 请求报文 + +#### ⑵. 响应报文 + +## (二)、HTTP方法 + +### 1. GET + +### 2. POST + +### 3. HEAD + +### 4. PUT + +### 5. PATCH + +### 6. DELETE + +### 7. OPTIONS + +### 8. CONNECT + +### 9. TRACE + +## (三)、HTTP状态码 + +### 1. 2XX 成功 + +### 2. 3XX 重定向 + +### 3. 4XX 客户端错误 + +### 4. 5XX 服务器错误 + +## (四)、HTTP首都 + +### 1. 通用首部字段 + +### 2. 请求首部字段 + +### 3. 响应首部字段 + +### 4. 实体首部字段 + +## (五)、具体应用 + +### 1. Cookie + +#### ⑴. 创建过程 + +#### ⑵. Set-Cookie + +#### ⑶. Session 和 Cookie 区别 + +#### ⑷. 浏览器禁用 Cookie 的情况 + +#### ⑸. 使用 Cookie 实现用户名和密码的自动填写 + +### 2. 缓存 + +#### ⑴. 优点 + +#### ⑵. 实现方法 + +#### ⑶. Cache-Control 字段 + +#### ⑷. no-cache 指令 + +#### ⑸. no-store 指令 + +#### ⑹. max-age 指令 + +### 3. 持久连接 + +### 4. 编码 + +### 5. 分块传输 + +### 6. 多部分对象集合 + +### 7. 范围请求 + +### 8. 内容协商 + +### 9. 虚拟主机 + +### 10. 通信数据转发 + +#### ⑴. 代理 + +#### ⑵. 网关 + +#### ⑶. 隧道 + +## (六)、HTPPs + +### 1. 加密 + +#### ⑴. 对称密钥 + +#### ⑵. 公开密钥 + +#### ⑶. HTTPs 采用的加密方式 + +### 2. 认证 + +### 3. 完整性 + +## (七)、Web攻击技术 + +### 1. 攻击模式 + +#### ⑴. 主动攻击 + +#### ⑵. 被动攻击 + +### 2. 跨站脚本攻击 + +#### ⑴. 概念 + +#### ⑵. 危害 + +#### ⑶. 防范手段 + +### 3. SQL 注入攻击 + +#### ⑴. 概念 + +#### ⑵. 攻击原理 + +#### ⑶. 危害 + +#### ⑷. 防范手段 + +### 4. 跨站点请求伪造 + +#### ⑴. 概念 + +#### ⑵. 防范手段 + +### 5. 拒绝服务攻击 + +#### ⑴. 概念 + +## (八)、各版本比较 + +### 1. HTTP/1.0 与 HTTP/1.1 的区别 + +### 2. HTTP/1.1 与 HTTP/2.0 的区别 + +#### ⑴. 多路复用 + +#### ⑵. 首部压缩 + +#### ⑶. 服务端推送 + +#### ⑷. 二进制格式 + +# 四、操作系统 + +## (一)、概述 + +### 1. 操作系统基本特征 + +#### ⑴. 并发 + +#### ⑵. 共享 + +#### ⑶. 虚拟 + +#### ⑷. 异步 + +### 2. 操作系统基本功能 + +#### ⑴. 进程管理 + +#### ⑵. 内存管理 + +#### ⑶. 文件管理 + +#### ⑷. 设备管理 + +### 3. 系统调用 + +### 4. 大内核和微内核 + +#### ⑴. 大内核 + +#### ⑵. 微内核 + +### 5. 中断分类 + +### 6. 外中断 + +### 7. 异常 + +### 8. 陷入 + +## (二)、进程管理 + +### 1. 进程与线程 + +#### ⑴. 进程 + +#### ⑵. 线程 + +#### ⑶. 区别 + +### 2. 进程状态的切换 + +### 3. 调度算法 + +#### ⑴. 批处理系统中的调度 + +##### ①. 先来先服务 + +##### ②. 短作业优先 + +##### ③. 最短剩余时间优先 + +#### ⑵. 交互式系统中的调度 + +##### ①. 优先级调度 + +##### ②. 时间片轮转 + +##### ③. 多级反馈队列 + +#### ⑶. 实时系统中的调度 + +### 4. 进程同步 + +#### ⑴. 临界区 + +#### ⑵. 同步与互斥 + +#### ⑶. 信号量 + +#### ⑷. 管程 + +### 5. 经典同步问题 + +#### ⑴. 读者-写者问题 + +#### ⑵. 哲学家进餐问题 + +### 6. 进程通信 + +#### ⑴. 进程同步与进程通信的区别 + +#### ⑵. 进程通信方式 + +##### ①. 消息传递 + +##### ②. 共享内存 + +## (三)、死锁 + +### 1. 死锁的必要条件 + +### 2. 死锁的处理方法 + +#### ⑴. 鸵鸟策略 + +#### ⑵. 死锁检测与死锁恢复 + +#### ⑶. 死锁预防 + +#### ⑷. 死锁避免 + +## (四)、内存管理 + +### 1. 虚拟内存 + +### 2. 分页与分段 + +#### ⑴. 分页 + +#### ⑵. 分段 + +#### ⑶. 段页式 + +#### ⑷. 分页与分段区别 + +### 3. 分页系统地址映射 + +### 4. 页面置换算法 + +#### ⑴. 最佳 + +#### ⑵. 先进先出 + +#### ⑶. 最近最久未使用 + +#### ⑷. 时钟 + +## (五)、设备管理 + +### 1. 磁盘调度算法 + +#### ⑴. 先来先服务 + +#### ⑵. 最短寻道时间优先 + +#### ⑶. 扫描算法 + +#### ⑷. 循环扫描算法 + +## (六)、链接 + +### 1. 编译系统 + +### 2. 目标文件 + +### 3. 静态链接 + +### 4. 动态链接 + +# 五、Linux + +## (一)、常用操作以及概念 + +### 1. 求助 + +#### ⑴. help + +#### ⑵. man + +#### ⑶. info + +### 2. 关机 + +#### ⑴. sync + +#### ⑵. shutdown + +#### ⑶. 其它关机指令 + +### 3. PATH + +### 4. 运行等级 + +### 5. sudo + +### 6. GNU + +### 7. 包管理工具 + +### 8. 发行版 + +### 9. VIM 三个模式 + +## (二)、分区 + +### 1. 磁盘的文件名 + +### 2. 分区表 + +#### ⑴. MBR + +#### ⑵. GPT + +### 3. 开机检测程序 + +#### ⑴. BIOS + +#### ⑵. UEFI + +### 4. 挂载 + +## (三)、文件 + +### 1. 文件权限概念 + +### 2. 文件属性以及权限的修改 + +#### ⑴. 修改文件所属群组 + +#### ⑵. 修改文件拥有者 + +#### ⑶. 修改权限 + +### 3. 目录的权限 + +### 4. 文件默认权限 + +### 5. 目录配置 + +### 6. 文件时间 + +### 7. 文件与目录的基本操作 + +#### ⑴. ls + +#### ⑵. cp + +#### ⑶. rm + +#### ⑷. mv + +### 8. 获取文件内容 + +#### ⑴. cat + +#### ⑵. tac + +#### ⑶. more + +#### ⑷. less + +#### ⑸. head + +#### ⑹. tail + +#### ⑺. od + +#### ⑻. touch + +### 9. 指令与文件搜索 + +#### ⑴. which + +#### ⑵. whereis + +#### ⑶. locate + +#### ⑷. find + +## (四)、磁盘与文件系统 + +### 1. 文件系统的组成 + +### 2. inode + +### 3. 目录的 inode 与 block + +### . 实体链接与符号链接 + +#### ⑴. 实体链接 + +#### ⑵. 符号链接 + +## (五)、压缩与打包 + +### 1. 压缩 + +#### ⑴. gzip + +#### ⑵. bzip2 + +#### ⑶. xz + +### 2. 打包 + +## (六)、Bash + +### 1. 特性 + +### 2. 变量操作 + +### 3. 指令搜索顺序 + +### 4. 数据流重定向 + +## (七)、管线指令 + +### 1. 提取指令 + +### 2. 排序指令 + +### 3. 双向输出重定向 + +### 4. 字符转换指令 + +### 5. 分区指令 + +## (八)、正则表达式 + +### 1. grep + +### 2. printf + +### 3. awk + +## (九)、进程管理 + +### 1. 查看进程 + +#### ⑴. ps + +#### ⑵. top + +#### ⑶. pstree + +#### ⑷. netstat + +### 2. 进程状态 + +### 3. SIGCHILD + +### 4. 孤儿进程和僵死进程 + +#### ⑴. 孤儿进程 + +#### ⑵. 僵死进程 + +## (十)、I/O复用 + +### 1. 概念理解 + +### 2. I/O 模型 + +#### ⑴. 同步-阻塞 + +#### ⑵. 同步-非阻塞 + +#### ⑶. 异步-阻塞 + +#### ⑷. 异步-非阻塞 + +### 3. select poll epoll + +#### ⑴. select + +#### ⑵. poll + +#### ⑶. epoll + +### 4. select 和 poll 比较 + +#### ⑴. 功能 + +#### ⑵. 速度 + +#### ⑶. 可移植性 + +### 5. eopll 工作模式 + +#### ⑴. LT 模式 + +#### ⑵. ET 模式 + +### 6. select poll epoll 应用场景 + +#### ⑴. select 应用场景 + +#### ⑵. poll 应用场景 + +#### ⑶. epoll 应用场景 + +#### ⑷. 性能对比 + +# 六、算法 + +## (一)、算法分析 + +### 1. 数学模型 + +#### ⑴. 近似 + +#### ⑵. 增长数量级 + +#### ⑶. 内循环 + +#### ⑷. 成本模型 + +### 2. ThreeSum + +### 3. 倍率实验 + +### 4. 注意事项 + +#### ⑴. 大常数 + +#### ⑵. 缓存 + +#### ⑶. 对最坏情况下的性能的保证 + +#### ⑷. 随机化算法 + +#### ⑸. 均摊分析 + +## (二)、栈和队列 + +### 1. 栈 + +### 2. 队列 + +## (三)、union-find + +### 1. quick-find + +### 2. quick-union + +### 3. 加权 quick-union + +### 4. 路径压缩的加权 quick-union + +### 5. 各种 union-find 算法的比较 + +## (四)、排序 + +### 1. 选择排序 + +### 2. 插入排序 + +### 3. 希尔排序 + +### 4. 归并排序 + +#### ⑴. 归并方法 + +#### ⑵. 自顶向下归并排序 + +#### ⑶. 自底向上归并排序 + +### 5. 快速排序 + +#### ⑴. 基本算法 + +#### ⑵. 切分 + +#### ⑶. 性能分析 + +#### ⑷. 算法改进 + +### 6. 优先队列 + +#### ⑴. 堆 + +#### ⑵. 上浮和下沉 + +#### ⑶. 插入元素 + +#### ⑷. 删除最大元素 + +#### ⑸. 堆排序 + +#### ⑹. 分析 + +### 7. 应用 + +#### ⑴. 排序算法的比较 + +#### ⑵. Java 的排序算法实现 + +#### ⑶. 基于切分的快速选择算法 + +## (五)、查找 + +### 1. 符号表 + +#### ⑴. 无序符号表 + +#### ⑵. 有序符号表 + +#### ⑶. 二分查找实现有序符号表 + +### 2. 二叉查找树 + +#### ⑴. get() + +#### ⑵. put() + +#### ⑶. 分析 + +#### ⑷. floor() + +#### ⑸. rank() + +#### ⑹. min() + +#### ⑺. deleteMin() + +#### ⑻. delete() + +#### ⑼. keys() + +#### ⑽. 性能分析 + +### 3. 2-3 查找树 + +#### ⑴. 插入操作 + +#### ⑵. 性质 + +### 3. 红黑二叉查找树 + +#### ⑴. 左旋转 + +#### ⑵. 右旋转 + +#### ⑶. 颜色转换 + +#### ⑷. 插入 + +#### ⑸. 删除最小键 + +#### ⑹. 分析 + +### 4. 散列表 + +#### ⑴. 散列函数 + +#### ⑵. 基于拉链法的散列表 + +#### ⑶. 基于线性探测法的散列表 + +### 5. 应用 + +#### ⑴. 各种符号表实现的比较 + +#### ⑵. Java 的符号表实现 + +#### ⑶. 集合类型 + +#### ⑷. 稀疏向量乘法 + +# 七、剑指 Offer 题解 + +## 1. 前言 +## 2. 实现 Singleton +## 3. 数组中重复的数字 +## 4. 二维数组中的查找 +## 5. 替换空格 +## 6. 从尾到头打印链表 +## 7. 重建二叉树 +## 8. 二叉树的下一个结点 +## 9. 用两个栈实现队列 +## 10.1 斐波那契数列 +## 10.2 跳台阶 +## 10.3 变态跳台阶 +## 10.4 矩形覆盖 +## 11. 旋转数组的最小数字 +## 12. 矩阵中的路径 +## 13. 机器人的运动范围 +## 14. 剪绳子 +## 15. 二进制中 1 的个数 +## 16. 数值的整数次方 +## 17. 打印从 1 到最大的 n 位数 +## 18.1 在 O(1) 时间内删除链表节点 +## 18.2 删除链表中重复的结点 +## 19. 正则表达式匹配 +## 20. 表示数值的字符串 +## 21. 调整数组顺序使奇数位于偶数前面 +## 22. 链表中倒数第 K 个结点 +## 23. 链表中环的入口结点 +## 24. 反转链表 +## 25. 合并两个排序的链表 +## 26. 树的子结构 +## 27. 二叉树的镜像 +## 28. 对称的二叉树 +## 29. 顺时针打印矩阵 +## 30. 包含 min 函数的栈 +## 31. 栈的压入、弹出序列 +## 32.1 从上往下打印二叉树 +## 32.2 把二叉树打印成多行 +## 32.3 按之字形顺序打印二叉树 +## 33. 二叉搜索树的后序遍历序列 +## 34. 二叉树中和为某一值的路径 +## 35. 复杂链表的复制 +## 36. 二叉搜索树与双向链表 +## 37. 序列化二叉树 +## 38. 字符串的排列 +## 39. 数组中出现次数超过一半的数字 +## 40. 最小的 K 个数 +## 41.1 数据流中的中位数 +## 41.2 字符流中第一个不重复的字符 +## 42. 连续子数组的最大和 +## 43. 从 1 到 n 整数中 1 出现的次数 +## 44. 数字序列中的某一位数字 +## 45. 把数组排成最小的数 +## 46. 把数字翻译成字符串 +## 47. 礼物的最大价值 +## 48. 最长不含重复字符的子字符串 +## 49. 丑数 +## 50. 第一个只出现一次的字符位置 +## 51. 数组中的逆序对 +## 52. 两个链表的第一个公共结点 +## 53 数字在排序数组中出现的次数 +## 54. 二叉搜索树的第 K 个结点 +## 55.1 二叉树的深度 +## 55.2 平衡二叉树 +## 56. 数组中只出现一次的数字 +## 57.1 和为 S 的两个数字 +## 57.2 和为 S 的连续正数序列 +## 58.1 翻转单词顺序列 +## 58.2 左旋转字符串 +## 59. 滑动窗口的最大值 +## 60. n 个骰子的点数 +## 61. 扑克牌顺子 +## 62. 圆圈中最后剩下的数 +## 63. 股票的最大利润 +## 64. 求 1+2+3+…+n +## 65. 不用加减乘除做加法 +## 66. 构建乘积数组 +## 67. 把字符串转换成整数 +## 68. 树中两个节点的最低公共祖先 + +# 八、Leetcode 题解 + +## (一)、算法思想 + +### 1. 二分查找 + +### 2. 贪心思想 + +### 3. 双指针 + +### 4. 排序 + +#### ⑴. 快速选择 + +#### ⑵. 堆排序 + +#### ⑶. 桶排序 + +### 5. 搜索 + +#### ⑴. BFS + +#### ⑵. DFS + +#### ⑶. Backtracking + +### 6. 分治 + +### 7. 动态规划 + +#### ⑴. 斐波那契数列 + +#### ⑵. 最长递增子序列 + +#### ⑶. 最长公共子系列 + +#### ⑷. 0-1 背包 + +#### ⑸. 数组区间 + +#### ⑹. 字符串编辑 + +#### ⑺. 分割整数 + +#### ⑻. 矩阵路径 + +#### ⑼. 其它问题 + +### 8. 数学 + +#### ⑴. 素数 + +#### ⑵. 最大公约数 + +#### ⑶. 进制转换 + +#### ⑷. 阶乘 + +#### ⑸. 字符串加法减法 + +#### ⑹. 相遇问题 + +#### ⑺. 多数投票问题 + +#### ⑻. 其它 + +## (二)、数据结构 + +### 1. 栈和队列 + +### 2. 哈希表 + +### 3. 字符串 + +### 4. 数组与矩阵 + +#### ⑴. 1-n 分布 + +#### ⑵. 有序矩阵 + +### 5. 链表 + +### 6. 树 + +#### ⑴. 递归 + +#### ⑵. 层次遍历 + +#### ⑶. 前中后序遍历 + +#### ⑷. BST + +#### ⑸. Trie + +### 7. 图 + +### 8. 位运算 + +# 九、设计模式 + +## (一)、前言 + +## (二)、设计模式概念 + +## (三)、单例模式 + +### 1. 意图 + +### 2. 类图 + +### 3. 使用场景 + +### 4. JDK 的使用 + +### 5. 实现 + +#### ⑴. 懒汉式-线程不安全 + +#### ⑵. 懒汉式-线程安全 + +#### ⑶. 饿汉式-线程安全 + +#### ⑷. 双重校验锁-线程安全 + +## (四)、简单工厂 + +### 1. 意图 + +### 2. 类图 + +### 3. 实现 + +## (五)、工厂方法模式 + +### 1. 意图 + +### 2. 类图 + +### 3. 实现 + +## (六)、抽象工厂模式 + +### 1. 意图 + +### 2. 类图 + +### 3. 代码实现 + +# 十、面向对象思想 + +## (一)、设计原则 + +### 1. S.O.L.I.D + +#### ⑴. 单一责任原则 + +#### ⑵. 开放封闭原则 + +#### ⑶. 里氏替换原则 + +#### ⑷. 接口分离原则 + +#### ⑸. 依赖倒置原则 + +### 2. 其他常见原则 + +#### ⑴. 迪米特法则 + +#### ⑵. 合成复用原则 + +#### ⑶. 共同封闭原则 + +#### ⑷. 稳定抽象原则 + +#### ⑸. 稳定依赖原则 + +## (二)、三大特性 + +### 1. 封装 + +### 2. 继承 + +### 3. 多态 + +## (三)、UML + +### 1. 类图 + +#### ⑴. 继承相关 + +#### ⑵. 泛化关系 (Generalize) + +#### ⑶. 实现关系 (Realize) + +#### ⑷. 整体和部分 + +#### ⑸. 聚合关系 (Aggregation) + +#### ⑹. 组合关系 (Composition) + +#### ⑺. 相互联系 + +#### ⑻. 关联关系 (Association) + +#### ⑼. 依赖关系 (Dependency) + +### 2. 时序图 + +#### ⑴. 定义 + +#### ⑵. 赤壁之战时序图 + +#### ⑶. 活动图、时序图之间的关系 + +#### ⑷. 类图与时序图的关系 + +#### ⑸. 时序图的组成 + +#### ⑹. 对象 + +#### ⑺. 生命线 + +#### ⑻. 消息 + +#### ⑼. 激活 + +# 十一、数据库系统原理 + +## (一)、事务 + +### 1. 概念 + +### 2. 四大特性 + +. 原子性(Atomicity) + +. 一致性(Consistency) + +. 隔离性(Isolation) + +. 持久性(Durability) + +## (二)、并发一致性问题 + +### 1. 问题 + +#### ⑴. 丢失修改 + +#### ⑵. 读脏数据 + +#### ⑶. 不可重复读 + +#### ⑷. 幻影读 + +### 2. 解决方法 + +## (三)、封锁 + +### 1. 封锁粒度 + +### 2. 封锁类型 + +#### ⑴. 排它锁与共享锁 + +#### ⑵. 意向锁 + +### 3. 封锁协议 + +#### ⑴. 三级封锁协议 + +#### ⑵. 两段锁协议 + +## (四)、隔离级别 + +## (五)、多版本并发控制 + +### 1. 版本号 + +### 2. Undo 日志 + +### 3. 实现过程 + +#### ⑴. SELECT + +#### ⑵. INSERT + +#### ⑶. DELETE + +#### ⑷. UPDATE + +### 4. 快照读与当前读 + +## (六)、Next-Key Locks + +### 1. Record Locks + +### 2. Grap Locks + +### 3. Next-Key Locks + +## (七)、关系数据库设计理论 + +### 1. 函数依赖 + +### 2. 异常 + +### 3. 范式 + +#### ⑴. 第一范式 (1NF) + +#### ⑵. 第二范式 (2NF) + +#### ⑶. 第三范式 (3NF) + +#### ⑷. BC 范式(BCNF) + +## (八)、数据库系统概述 + +### 1. 基本术语 + +#### ⑴. 数据模型 + +#### ⑵. 数据库系统 + +### 2. 数据库的三层模式和两层映像 + +#### ⑴. 外模式 + +#### ⑵. 模式 + +#### ⑶. 内模式 + +#### ⑷. 外模式/模式映像 + +#### ⑸. 模式/内模式映像 + +## (九)、关系数据库建模 + +### 1. ER 图 + +#### ⑴. 实体的三种联系 + +#### ⑵. 表示出现多次的关系 + +#### ⑶. 联系的多向性 + +#### ⑷. 表示子类 + +## (十)、约束 + +### 1. 键码 + +### 2. 单值约束 + +### 3. 引用完整性约束 + +### 4. 域约束 + +### 5. 一般约束 + +# 十二、SQL + +## (一)、基础 + +## (二)、创建表 + +## (三)、修改表 + +## (四)、插入 + +## (五)、更新 + +## (六)、删除 + +## (七)、查询 + +### 1. DISTINCT + +### 2. LIMIT + +## (八)、排序 + +## (九)、过滤 + +## (十)、通配符 + +## (十一)、计算字段 + +## (十二)、函数 + +### 1. 文本处理 + +### 2. 日期和时间处理 + +### 3. 数值处理 + +### 4. 汇总 + +## (十三)、分组 + +## (十四)、子查询 + +## (十五)、连接 + +### 1. 内连接 + +### 2. 自连接 + +### 3. 自然连接 + +### 4. 外连接 + +## (十六)、组合查询 + +## (十七)、视图 + +## (十八)、存储过程 + +### 1. 使用存储过程的好处 + +### 2. 创建存储过程 + +## (十九)、游标 + +## (二十)、触发器 + +## (二十一)、事务处理 + +## (二十二)、字符集 + +## (二十三)、权限管理 + +### 1. 创建账户 + +### 2. 修改账户名 + +### 3. 删除账户 + +### 4. 查看权限 + +### 5. 授予权限 + +### 6. 删除权限 + +### 7. 更改密码 + +# 十三、MySQL + +## (一)、储存引擎 + +### 1. InnoDB + +### 2. MyISAM + +### 3. 比较 + +## (二)、数据类型 + +### 1. 整型 + +### 2. 浮点数 + +### 3. 字符串 + +### 4. 时间和日期 + +#### ⑴. DATATIME + +#### ⑵. TIMESTAMP + +## (三)、索引 + +### 1. 索引分类 + +#### ⑴. B+Tree 索引 + +#### ⑵. 哈希索引 + +#### ⑶. 空间索引(R-Tree) + +#### ⑷. 全文索引 + +### 2. 索引的优点 + +### 3. 索引优化 + +#### ⑴. 独立的列 + +#### ⑵. 前缀索引 + +#### ⑶. 多列索引 + +#### ⑷. 索引列的顺序 + +#### ⑸. 聚簇索引 + +#### ⑹. 覆盖索引 + +### 4. B-Tree 和 B+Tree 原理 + +#### ⑴. B-Tree + +#### ⑵. B+Tree + +#### ⑶. 带有顺序访问指针的 B+Tree + +#### ⑷. 为什么使用 B-Tree 和 B+Tree + +## (四)、查询性能优化 + +### 1. Explain + +### 2. 减少返回的列 + +### 3. 减少返回的行 + +### 4. 拆分大的 DELETE 或 INSERT 语句 + +## (五)、切分 + +### 1. 垂直切分 + +### 2. 水平切分 + +### 3. 切分的选择 + +### 4. 存在的问题 + +#### ⑴. 事务问题 + +#### ⑵. 跨库跨表连接问题 + +#### ⑶. 额外的数据管理负担和数据运算压力 + +## (六)、数据转移和故障恢复 + +### 1. 提升备库或切换角色 + +### 2. 虚拟 IP 地址和 IP 托管 + +### 3. 中间件解决方案 + +### 4. 在应用中处理故障转移 + +# 十四、Redis + +## (一)、Redis是什么 + +## (二)、五种基本类型 + +### 1. STRING + +### 2. LIST + +### 3. SET + +### 4. HASH + +### 5. ZSET + +## (三)、键的过期时间 + +## (四)、发布与订阅 + +## (五)、事务 + +## (六)、持久化 + +### 1. 快照持久化 + +### 2. AOF 持久化 + +## (七)、复制 + +### 1. 从服务器连接主服务器的过程 + +### 2. 主从链 + +## (八)、处理故障 + +## (九)、分片 + +### 1. 客户端分片 + +### 2. 代理分片 + +### 3. 服务器分片 + +## (十)、事件 + +### 1. 事件类型 + +#### ⑴. 文件事件 + +#### ⑵. 时间事件 + +### 2. 事件的调度与执行 + +## (十一)、Redis 与 Memcached 的区别 + +### 1. 数据类型 + +### 2. 数据持久化 + +### 3. 分布式 + +### 4. 内存管理机制 + +## (十二)、Redis 适用场景 + +### 1. 缓存 + +### 2. 消息队列 + +### 3. 计数器 + +### 4. 好友关系 + +## (十三)、数据淘汰策略 + +## (十四)、一个简单的论坛系统分析 + +### 1. 文章信息 + +### 2. 点赞功能 + +### 3. 对文章进行排序 + +# 十五、Java 虚拟机 + +## (一)、运行时数据区域 + +### 1. 程序计数器 + +### 2. Java 虚拟机栈 + +### 3. 本地方法栈 + +### 4. Java 堆 + +### 5. 方法区 + +### 6. 运行时常量池 + +### 7. 直接内存 + +## (二)、垃圾收集 + +### 1. 判断一个对象是否可回收 + +#### ⑴. 引用计数 + +#### ⑵. 可达性 + +#### ⑶. 引用类型 + +#### ⑷. 方法区的回收 + +#### ⑸. finalize() + +### 2. 垃圾收集算法 + +#### ⑴. 标记 - 清除 + +#### ⑵. 复制 + +#### ⑶. 标记 - 整理 + +#### ⑷. 分代收集 + +### 3. 垃圾收集器 + +#### ⑴. Serial 收集器 + +#### ⑵. ParNew 收集器 + +#### ⑶. Parallel Scavenge 收集器 + +#### ⑷. Serial Old 收集器 + +#### ⑸. Parallel Old 收集器 + +#### ⑹. CMS 收集器 + +#### ⑺. G1 收集器 + +#### ⑻. 七种垃圾收集器的比较 + +### 4. 内存分配与回收策略 + +#### ⑴. 优先在 Eden 分配 + +#### ⑵. 大对象直接进入老年代 + +#### ⑶. 长期存活的对象进入老年代 + +#### ⑷. 动态对象年龄判定 + +#### ⑸. 空间分配担保 + +### 5. Full GC 的触发条件 + +#### ⑴. 调用 System.gc() + +#### ⑵. 老年代空间不足 + +#### ⑶. 空间分配担保失败 + +#### ⑷. JDK 1.7 及以前的永久代空间不足 + +#### ⑸. Concurrent Mode Failure + +## (三)、类加载机制 + +### 1. 类的生命周期 + +### 2. 类初始化时机 + +### 3. 类加载过程 + +#### ⑴. 加载 + +#### ⑵. 验证 + +#### ⑶. 准备 + +#### ⑷. 解析 + +#### ⑸. 初始化 + +### 4. 类加载器 + +#### ⑴. 类与类加载器 + +#### ⑵. 类加载器分类 + +#### ⑶. 双亲委派模型 + +## (四)、JVM参数 + +### 1. GC 优化配置 + +### 2. GC 类型设置 + +# 十六、Java 并发 + +## (一)、使用线程 + +### 1. 实现 Runnable 接口 + +### 2. 实现 Callable 接口 + +### 3. 继承 Thread 类 + +### 4. 实现接口 VS 继承 Thread + +## (二)、基础线程机制 + +### 1. sleep() + +### 2. yield() + +### 3. join() + +### 4. deamon + +## (三)、结束线程 + +### 1. 阻塞 + +### 2. 中断 + +## (四)、线程之间的协作 + +### 1. 同步与通信的概念理解 + +### 2. 线程同步 + +#### ⑴. synchronized + +#### ⑵. ReentrantLock + +### 3. 线程通信 + +#### ⑴. wait() notify() notifyAll() + +#### ⑵. BlockingQueue + +## (五)、线程状态转换 + +## (六)、Executor + +## (七)、内存模型 + +### 1. 主内存与工作内存 + +### 2. 内存模型三大特性 + +#### ⑴. 原子性 + +#### ⑵. 可见性 + +#### ⑶. 有序性 + +### 3. 先行发生原则 + +#### ⑴. 单一线程原则 + +#### ⑵. 管程锁定规则 + +#### ⑶. volatile 变量规则 + +#### ⑷. 线程启动规则 + +#### ⑸. 线程加入规则 + +#### ⑹. 线程中断规则 + +#### ⑺. 对象终结规则 + +#### ⑻. 传递性 + +## (八)、线程安全 + +### 1. 线程安全分类 + +#### ⑴. 不可变 + +#### ⑵. 绝对线程安全 + +#### ⑶. 相对线程安全 + +#### ⑷. 线程兼容 + +#### ⑸. 线程对立 + +### 2. 线程安全的实现方法 + +#### ⑴. 互斥同步 + +#### ⑵. 非阻塞同步 + +#### ⑶. 无同步方案 + +### 3. 锁优化 + +#### ⑴. 自旋锁与自适应自旋 + +#### ⑵. 锁消除 + +#### ⑶. 锁粗化 + +#### ⑷. 轻量级锁 + +#### ⑸. 偏向锁 + +## (九)、多线程开发良好的实践 + +# 十七、Java 容器 + +## (一)、概览 + +### 1. Collection + +#### ⑴. Set + +#### ⑵. List + +#### ⑶. Queue + +### 2. Map + +## (二)、容器中的设计模式 + +### 1. 迭代器模式 + +### 2. 适配器模式 + +## (三)、散列 + +## (四)、源码分析 + +### 1. ArrayList + +#### ⑴. 概览 + +#### ⑵. Fail-Fast + +#### ⑶. 和 Vector 的区别 + +#### ⑷. 和 LinkedList 的区别 + +### 2. Vector + +### 3. LinkedList + +### 4. TreeMap + +### 5. HashMap + +#### ⑴. 存储结构 + +#### ⑵. 拉链法的工作原理 + +#### ⑶. 链表转红黑树 + +#### ⑷. 扩容 + +#### ⑸. 确定桶下标 + +#### ⑹. 扩容-重新计算桶下标 + +#### ⑺. 扩容-计算数组容量 + +#### ⑻. null 值 + +#### ⑼. 与 HashTable 的区别 + +### 6. LinkedHashMap + +### 7. ConcurrentHashMap - JDK 1.7 + +#### ⑴. 存储结构 + +#### ⑵. HashEntery 的不可变性 + +#### ⑶. Volatile 变量 + +#### ⑷. 小结 + +### 8. ConcurrentHashMap - JDK 1.8 + +# 十八、Java IO + +## (一)、概览 + +## (二)、磁盘操作 + +## (三)、字节操作 + +## (四)、字符操作 + +## (五)、对象操作 + +## (六)、网络操作 + +### 1. InetAddress + +### 2. URL + +### 3. Sockets + +### 4. Datagram + +## (七)、NIO + +### 1. 流与块 + +### 2. 通道与缓冲区 + +#### ⑴. 通道 + +#### ⑵. 缓冲区 + +### 3. 缓冲区状态变量 + +### 4. 文件 NIO 实例 + +### 5. 套接字 NIO 实例 + +#### ⑴. ServerSocketChannel + +#### ⑵. Selectors + +#### ⑶. 主循环 + +#### ⑷. 监听新连接 + +#### ⑸. 接受新的连接 + +#### ⑹. 删除处理过的 SelectionKey + +#### ⑺. 传入的 I/O + +### 6. 内存映射文件 + +### 7. 对比 + +# 十九、Java 基础 + +## (一)、关键字 + +### 1. final + +### 2. static + +## (二)、Object 通用方法 + +### 1. 概览 + +### 2. clone() + +### 3. equals() + +## (三)、继承 + +### 1. 访问权限 + +### 2. 抽象类与接口 + +### 3. super + +### 4. 重载与重写 + +## (四)、String + +### 1. String, StringBuffer and StringBuilder + +### 2. String 不可变的原因 + +### 3. String.intern() + +## (五)、基本类型与运算 + +### 1. 包装类型 + +### 2. switch + +## (六)、反射 + +## (七)、异常 + +## (八)、泛型 + +## (九)、注解 + +## (十)、特性 + +### 1. 面向对象三大特性 + +### 2. Java 各版本的新特性 + +### 3. Java 与 C++ 的区别 + +### 4. JRE or JDK + +# 二十、JDK 中的设计模式 + +## (一)、创建型 + +### 1. 单例模式 + +### 2. 简单工厂模式 + +### 3. 工厂方法模式 + +### 4. 抽象工厂 + +### 5. 生成器模式 + +### 6. 原型模式 + +## (二)、行为型 + +### 1. 责任链 + +### 2. 命令模式 + +### 3. 解释器模式 + +### 4. 迭代器 + +### 5. 中间人模式 + +### 6. 备忘录模式 + +### 7. 观察者模式 + +### 8. 策略模式 + +### 9. 模板方法 + +### 10. 访问者模式 + +### 11. 空对象模式 + +## (三)、结构型 + +### 1. 适配器 + +### 2. 桥接模式 + +### 3. 组合模式 + +### 4. 装饰者模式 + +### 5. 蝇量模式 + +### 6. 动态代理 + +# 二十一、分布式基础 + +## (一)、基本概念 + +### 1. 异常 + +#### ⑴. 服务器宕机 + +#### ⑵. 网络异常 + +#### ⑶. 磁盘故障 + +### 2. 超时 + +### 3. 衡量指标 + +#### ⑴. 性能 + +#### ⑵. 可用性 + +#### ⑶. 一致性 + +#### ⑷. 可扩展性 + +## (二)、数据分布 + +### 1. 哈希分布 + +### 2. 顺序分布 + +## (三)、负载均衡 + +## (四)、复制 + +### 1. 强同步复制协议 + +### 2. 异步复制协议 + +## (五)、CAP + +## (六)、BASE + +### 1. 基本可用 + +### 2. 软状态 + +### 3. 最终一致性 + +## (七)、容错 + +### 1. 故障检测 + +### 2. 故障恢复 + +## (八)、CDN 架构 + +# 二十二、一致性协议 + +## (一)、两阶段提交协议 + +### 1. 运行过程 + +### 2. 存在的问题 + +## (二)、Paxos 协议 + +### 1. 执行过程 + +### 2. 约束条件 + +#### ⑴. 正确性 + +#### ⑵. 可终止性 + +## (三)、Raft 协议 + +### 1. 单个 Candidate 的竞选 + +### 2. 多个 Candidate 竞选 + +### 3. 日志复制 + +## (四)、拜占庭将军问题 + +# 二十三、分布式问题分析 + +## (一)、谈谈业务中使用分布式的场景 + +## (二)、分布式事务 + +### 1. 产生原因 + +### 2. 应用场景 + +### 3. 解决方案 + +#### ⑴. 两阶段提交协议 + +#### ⑵. 消息中间件 + +##### ①. 消息处理模型 + +##### ②. 消息的可靠性 + +## (三)、负载均衡的算法与实现 + +### 1. 算法 + +#### ⑴. 轮询(Round Robin) + +#### ⑵. 加权轮询(Weighted Round Robbin) + +#### ⑶. 最少连接(least Connections) + +#### ⑷. 加权最小连接(Weighted LeastConnection) + +#### ⑸. 随机算法(Random) + +### 2. 实现 + +#### ⑴. HTTP 重定向 + +#### ⑵. DNS 重定向 + +#### ⑶. 修改 MAC 地址 + +#### ⑷. 修改 IP 地址 + +#### ⑸. 代理自动配置 + +## (四)、分布式锁 + +### 1. 使用场景 + +### 2. 实现方式 + +#### ⑴. 数据库分布式锁 + +#### ⑵. Redis 分布式锁 + +#### ⑶. Zookeeper 分布式锁 + +## (五)、分布式 Session + +### 1. Sticky Sessions + +### 2. Session Replication + +### 3. Persistent DataStore + +### 4. In-Memory DataStore + +## (六)、分库与分表带来的分布式困境与应对之策 + +### 1. 事务问题 + +### 2. 查询问题 + +### 3. ID 唯一性 + +# 二十四、Git + +## 1. 学习资料 + +## 2. 集中式与分布式 + +## 3. Git 的中心服务器 + +## 4. Git 工作流 + +## 5. 分支实现 + +## 6. 冲突 + +## 7. Fast forward + +## 8. 分支管理策略 + +## 9. 储藏(Stashing) + +## 10. SSH 传输设置 + +## 11. .gitignore 文件 + +## 12. Git 命令一览 + +# 二十五、正则表达式 + +## (一)、概述 + +## (二)、匹配单个字符 + +## (三)、匹配一组字符 + +## (四)、使用元字符 + +### 1. 匹配空白字符 + +### 2. 匹配特定的字符类别 + +#### ⑴. 数字元字符 + +#### ⑵. 字母数字元字符 + +#### ⑶. 空白字符元字符 + +## (五)、重复匹配 + +## (六)、位置匹配 + +### 1. 单词边界 + +### 2. 字符串边界 + +## (七)、使用子表达式 + +## (八)、回溯引用 + +### 1. 替换 + +### 2. 大小写转换 + +## (九)、前后查找 + +## (十)、嵌入条件 + +### 1. 回溯引用条件 + +### 2. 前后查找条件 + +# 二十六、重构 + +## (一)、第一个案例 + +## (二)、重构原则 + +### 1. 定义 + +### 2. 为何重构 + +### 3. 三次法则 + +### 4. 间接层与重构 + +### 5. 修改接口 + +### 6. 何时不该重构 + +### 7. 重构与设计 + +### 8. 重构与性能 + +## (三)、代码的坏味道 + +### 1. 重复代码 + +### 2. 过长函数 + +### 3. 过大的类 + +### 4. 过长的参数列表 + +### 5. 发散式变化 + +### 6. 散弹式修改 + +### 7. 依恋情结 + +### 8. 数据泥团 + +### 9. 基本类型偏执 + +### 10. switch 惊悚现身 + +### 11. 平行继承体系 + +### 12. 冗余类 + +### 13. 夸夸其谈未来性 + +### 14. 令人迷惑的暂时字段 + +### 15. 过度耦合的消息链 + +### 16. 中间人 + +### 17. 狎昵关系 + +### 18. 异曲同工的类 + +### 19. 不完美的类库 + +### 20. 幼稚的数据类 + +### 21. 被拒绝的馈赠 + +### 22. 过多的注释 + +## (四)、构筑测试体系 + +## (五)、重新组织函数 + +### 1. 提炼函数 + +### 2. 内联函数 + +### 3. 内联临时变量 + +### 4. 以查询取代临时变量 + +### 5. 引起解释变量 + +### 6. 分解临时变量 + +### 7. 移除对参数的赋值 + +### 8. 以函数对象取代函数 + +### 9. 替换算法 + +## (六)、在对象之间搬移特性 + +### 1. 搬移函数 + +### 2. 搬移字段 + +### 3. 提炼类 + +### 4. 将类内联化 + +### 5. 隐藏委托关系 + +### 6. 移除中间人 + +### 7. 引入外加函数 + +### 8. 引入本地扩展 + +## (七)、重新组织数据 + +### 1. 自封装字段 + +### 2. 以对象取代数据值 + +### 3. 将值对象改成引用对象 + +### 4. 将引用对象改为值对象 + +### 5. 以对象取代数组 + +### 6. 赋值被监视数据 + +### 7. 将单向关联改为双向关联 + +### 8. 将双向关联改为单向关联 + +### 9. 以字面常量取代魔法数 + +### 10. 封装字段 + +### 11. 封装集合 + +### 12. 以数据类取代记录 + +### 13. 以类取代类型码 + +### 14. 以子类取代类型码 + +### 15. 以 State/Strategy 取代类型码 + +### 16. 以字段取代子类 + +## (八)、简化条件表达式 + +### 1. 分解条件表达式 + +### 2. 合并条件表达式 + +### 3. 合并重复的条件片段 + +### 4. 移除控制标记 + +### 5. 以卫语句取代嵌套条件表达式 + +### 6. 以多态取代条件表达式 + +### 7. 引入 Null 对象 + +### 8. 引入断言 + +## (九)、简化函数调用 + +### 1. 函数改名 + +### 2. 添加参数 + +### 3. 移除参数 + +### 4. 将查询函数和修改函数分离 + +### 5. 令函数携带参数 + +### 6. 以明确函数取代参数 + +### 7. 保持对象完整 + +### 8. 以函数取代参数 + +### 9. 引入参数对象 + +### 10. 移除设值函数 + +### 11. 隐藏函数 + +### 12. 以工厂函数取代构造函数 + +### 13. 封装向下转型 + +### 14. 以异常取代错误码 + +### 15. 以测试取代异常 + +## (十)、处理概括关系 + +### 1. 字段上移 + +### 2. 函数上移 + +### 3. 构造函数本体上移 + +### 4. 函数下移 + +### 5. 字段下移 + +### 6. 提炼子类 + +### 7. 提炼超类 + +### 8. 提炼接口 + +### 9. 折叠继承体系 + +### 10. 塑造模板函数 + +### 11. 以委托取代继承 + +### 12. 以继承取代委托 + +# 二十七、代码可读性 + +## 1. 可读性的重要性 + +## 2. 用名字表达代码含义 + +## 3. 名字不能带来歧义 + +## 4. 良好的代码风格 + +## 5. 编写注释 + +## 6. 如何编写注释 + +## 7. 提高控制流的可读性 + +## 8. 拆分长表达式 + +## 9. 变量与可读性 + +## 10. 抽取函数 + +## 11. 一次只做一件事 + +## 12. 用自然语言表述代码 + +## 13. 减少代码量 From 79a22ea029829c93cf7b8ee568ff804ce21bd06e Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 16 Jun 2020 18:09:57 +0800 Subject: [PATCH 02/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 782a78b..4b51573 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ - [消息中间件MQ与RabbitMQ面试题](https://github.com/Java-Super-Air/JavaStudy/blob/master/Interview/%E6%B6%88%E6%81%AF%E4%B8%AD%E9%97%B4%E4%BB%B6MQ%E4%B8%8ERabbitMQ%E9%9D%A2%E8%AF%95%E9%A2%98.md) - **面试攻略** - + - [技术面试需要掌握的基础知识整理](https://github.com/Java-Super-Air/JavaStudy/blob/master/Interview/%E6%8A%80%E6%9C%AF%E9%9D%A2%E8%AF%95%E9%9C%80%E8%A6%81%E6%8E%8C%E6%8F%A1%E7%9A%84%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E6%95%B4%E7%90%86.MD) - [面试前的准备](https://github.com/Java-Super-Air/JavaStudy/blob/master/Interview/%E7%A8%8B%E5%BA%8F%E5%91%98%E9%9D%A2%E8%AF%95%E6%94%BB%E7%95%A5%EF%BC%9A%E9%9D%A2%E8%AF%95%E5%89%8D%E7%9A%84%E5%87%86%E5%A4%87.md) - [面试中的技巧](https://github.com/Java-Super-Air/JavaStudy/blob/master/Interview/%E7%A8%8B%E5%BA%8F%E5%91%98%E9%9D%A2%E8%AF%95%E6%94%BB%E7%95%A5%EF%BC%9A%E9%9D%A2%E8%AF%95%E4%B8%AD%E7%9A%84%E6%8A%80%E5%B7%A7.md) From d9e61aa31ffd556a6e40aa22e1191909078b43c8 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 16 Jun 2020 20:34:34 +0800 Subject: [PATCH 03/27] =?UTF-8?q?Update=20=E6=8A=80=E6=9C=AF=E9=9D=A2?= =?UTF-8?q?=E8=AF=95=E9=9C=80=E8=A6=81=E6=8E=8C=E6=8F=A1=E7=9A=84=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E7=9F=A5=E8=AF=86=E6=95=B4=E7=90=86.MD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...45\350\257\206\346\225\264\347\220\206.MD" | 583 +++++++++++++++++- 1 file changed, 582 insertions(+), 1 deletion(-) diff --git "a/Interview/\346\212\200\346\234\257\351\235\242\350\257\225\351\234\200\350\246\201\346\216\214\346\217\241\347\232\204\345\237\272\347\241\200\347\237\245\350\257\206\346\225\264\347\220\206.MD" "b/Interview/\346\212\200\346\234\257\351\235\242\350\257\225\351\234\200\350\246\201\346\216\214\346\217\241\347\232\204\345\237\272\347\241\200\347\237\245\350\257\206\346\225\264\347\220\206.MD" index 3496d07..20fe2b7 100644 --- "a/Interview/\346\212\200\346\234\257\351\235\242\350\257\225\351\234\200\350\246\201\346\216\214\346\217\241\347\232\204\345\237\272\347\241\200\347\237\245\350\257\206\346\225\264\347\220\206.MD" +++ "b/Interview/\346\212\200\346\234\257\351\235\242\350\257\225\351\234\200\350\246\201\346\216\214\346\217\241\347\232\204\345\237\272\347\241\200\347\237\245\350\257\206\346\225\264\347\220\206.MD" @@ -1,4 +1,3 @@ - 技术面试知识点整理【目录】 ================= @@ -1995,182 +1994,764 @@ System.out.printf("%.2f\n", dp[N - 1][M - 1]); ### 1. 网络的网络 +网络把主机连接起来,而互联网是把多种不同的网络连接起来,因此互联网是网络的网络。 + +![](https://upload-images.jianshu.io/upload_images/21105806-3e9befc93843a932.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 2. ISP +互联网服务提供商 ISP 可以从互联网管理机构获得许多 IP 地址,同时拥有通信线路以及路由器等联网设备,个人或机构向 ISP 缴纳一定的费用就可以接入互联网。 + +目前的互联网是一种多层次 ISP 结构,ISP 根据覆盖面积的大小分为主干 ISP、地区 ISP 和本地 ISP。 + +互联网交换点 IXP 允许两个 ISP 直接相连而不用经过第三个 ISP。 + +![](https://upload-images.jianshu.io/upload_images/21105806-623d2325a5d4f840.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 3. 互联网的组成 +1. 边缘部分:所有连接在互联网上的主机,用户可以直接使用; +2. 核心部分:由大量的网络和连接这些网络的路由器组成,为边缘部分的主机提供服务。 + +![](https://upload-images.jianshu.io/upload_images/21105806-a905c2e3733cf293.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 4. 主机之间的通信方式 +1. 客户-服务器(C/S):客户是服务的请求方,服务器是服务的提供方。 +2. 对等(P2P):不区分客户和服务器。 + ### 5. 电路交换与分组交换 +![](https://upload-images.jianshu.io/upload_images/21105806-24a657b10fdd15d5.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑴. 电路交换 +电路交换用于电话通信系统,两个用户要通信之前需要建立一条专用的物理链路,并且在整个通信过程中始终占用该链路。由于通信的过程中不可能一直在使用传输线路,因此电路交换对线路的利用率很低,往往不到 10%。 + #### ⑵. 报文交换 +报文交换用于邮局通信系统,邮局接收到一份报文之后,先存储下来,然后把相同目的地的报文一起转发到下一个目的地,这个过程就是存储转发过程。 + #### ⑶. 分组交换 +分组交换也使用了存储转发,但是转发的是分组而不是报文。把整块数据称为一个报文,由于一个报文可能很长,需要先进行切分,来满足分组能处理的大小。在每个切分的数据前面加上首部之后就成为了分组,首部包含了目的地址和源地址等控制信息。 + +![](https://upload-images.jianshu.io/upload_images/21105806-0f87879b8ac9fb62.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +存储转发允许在一条传输线路上传送多个主机的分组,也就是说两个用户之间的通信不需要占用端到端的线路资源。 + +相比于报文交换,由于分组比报文更小,因此分组交换的存储转发速度更加快速。 + ### 6. 时延 +总时延 = 发送时延 + 传播时延 + 处理时延 + 排队时延 + +![](https://upload-images.jianshu.io/upload_images/21105806-57c614f13b647a4a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑴. 发送时延 +主机或路由器发送数据帧所需要的时间。 + +![](https://upload-images.jianshu.io/upload_images/21105806-bf9d8078edf7ea54.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +其中 l 表示数据帧的长度,v 表示发送速率。 + #### ⑵. 传播时延 +电磁波在信道中传播一定的距离需要花费的时间,电磁波传播速度接近光速。 + +![](https://upload-images.jianshu.io/upload_images/21105806-bf0bc5bf4da6b65a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +其中 l 表示信道长度,v 表示电磁波在信道上的传播速率。 + #### ⑶. 处理时延 +主机或路由器收到分组时进行处理所需要的时间,例如分析首部、从分组中提取数据部、进行差错检验或查找适当的路由等。 + #### ⑷. 排队时延 +分组在路由器的输入队列和输出队列中排队等待的时间,取决于网络当前的通信量。 + ### 7. 计算机网络体系结构* +![](https://upload-images.jianshu.io/upload_images/21105806-c275ad97d4284637.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑴. 七层协议 +如图 a 所示,其中表示层和会话层用途如下: + +- 表示层:信息的语法、语义以及它们的关联,如加密解密、转换翻译、压缩解压缩; +- 会话层:不同机器上的用户之间建立及管理会话。 + #### ⑵. 五层协议 +- 应用层:为特定应用程序提供数据传输服务,例如 HTTP、DNS 等。数据单位为报文。 +- 运输层:提供的是进程间的通用数据传输服务。由于应用层协议很多,定义通用的运输层协议就可以支持不断增多的应用层协议。运输层包括两种协议:传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。TCP 主要提供完整性服务,UDP 主要提供及时性服务。 +- 网络层:为主机之间提供数据传输服务,而运输层协议是为主机中的进程提供服务。网络层把运输层传递下来的报文段或者用户数据报封装成分组。 +- 数据链路层:网络层针对的还是主机之间的数据传输服务,而主机之间可以有很多链路,链路层协议就是为同一链路的结点提供服务。数据链路层把网络层传来的分组封装成帧。 +- 物理层:考虑的是怎样在传输媒体上传输数据比特流,而不是指具体的传输媒体。物理层的作用是尽可能屏蔽传输媒体和通信手段的差异,使数据链路层感觉不到这些差异。 + #### ⑶. 数据在各层之间的传递过程 +在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。 + +路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要运输层和应用层。 + +![](https://upload-images.jianshu.io/upload_images/21105806-a5039603bbd84b51.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑷. TCP/IP 体系结构 +它只有四层,相当于五层协议中数据链路层和物理层合并为网络接口层。 + +现在的 TCP/IP 体系结构不严格遵循 OSI 分层概念,应用层可能会直接使用 IP 层或者网络接口层。 + +![](https://upload-images.jianshu.io/upload_images/21105806-b63d2969eae11e2f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +TCP/IP 协议族是一种沙漏形状,中间小两边大,IP 协议在其中占用举足轻重的地位。 + +![](https://upload-images.jianshu.io/upload_images/21105806-2f9a38548d6fa93e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ## (二)、物理层 ### 1. 通信方式 +1. 单向通信,又称为单工通信; +2. 双向交替通信,又称为半双工通信; +3. 双向同时通信,又称为全双工通信。 + ### 2. 带通调制 +模拟信号是连续的信号,数字信号是离散的信号。带通调制把数字信号转换为模拟信号。 + +![](https://upload-images.jianshu.io/upload_images/21105806-343aa4560ebd1a2a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 3. 信道复用技术 #### ⑴. 频分复用、时分复用 +频分复用的所有用户在相同的时间占用不同的频率带宽资源;时分复用的所有用户在不同的时间占用相同的频率带宽资源。 + +使用这两种方式进行通信,在通信的过程中用户会一直占用一部分信道资源。但是由于计算机数据的突发性质,通信过程没必要一直占用信道资源而不让出给其它用户使用,因此这两种方式对信道的利用率都不高。 + +![](https://upload-images.jianshu.io/upload_images/21105806-5b93734bc44f3096.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑵. 统计时分复用 +是对时分复用的一种改进,不固定每个用户在时分复用帧中的位置,只要有数据就集中起来组成统计时分复用帧然后发送。 + +![](https://upload-images.jianshu.io/upload_images/21105806-78a3147b29855393.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑶. 波分复用 +光的频分复用。由于光的频率很高,因此习惯上用波长而不是频率来表示所使用的光载波。 + +![](https://upload-images.jianshu.io/upload_images/21105806-b412b35f8b1e8bd0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑷. 码分复用 +![](https://upload-images.jianshu.io/upload_images/21105806-6f1f39c088998277.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +![](https://upload-images.jianshu.io/upload_images/21105806-9a37ff5183bb5fe0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ## (三)、数据链路层 ### 1. 信道分类 +- 点对点信道:一对一通信方式; +- 广播信道:一对多通信方式。 + ### 2. 三个基本问题 #### ⑴. 封装成帧 +将网络层传下来的分组添加首部和尾部,用于标记帧的开始和结束。 + +![](https://upload-images.jianshu.io/upload_images/21105806-8aa30ff1a433a4ae.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑵. 透明传输 +透明表示一个实际存在的事物看起来好像不存在一样。 + +帧使用首部和尾部进行定界,如果帧的数据部分含有和首部尾部相同的内容,那么帧的开始和结束位置就会被错误的判定。需要在数据部分出现首部尾部相同的内容前面插入转义字符,如果出现转移字符,那么就在转义字符前面再加个转义字符,在接收端进行处理之后可以还原出原始数据。这个过程透明传输的内容是转义字符,用户察觉不到转义字符的存在。 + +![](https://upload-images.jianshu.io/upload_images/21105806-69f6747afd2a2b42.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑶. 差错检测 +目前数据链路层广泛使用了循环冗余检验(CRC)来检查比特差错。 + ### 3. 局域网 +局域网是典型的一种广播信道,主要特点是网络为一个单位所拥有,且地理范围和站点数目均有限。 + +可以按照网络拓扑对局域网进行分类: + +![](https://upload-images.jianshu.io/upload_images/21105806-bc0ca7bc988d12e9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 4. PPP 协议 +用于点对点信道中。互联网用户通常需要连接到某个 ISP 之后才能接入到互联网,PPP 协议是用户计算机和 ISP 进行通信时所使用的数据链路层协议。 + +![](https://upload-images.jianshu.io/upload_images/21105806-93fddbe69cc97273.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +在 PPP 的帧中: + +- F 字段为帧的定界符 +- A 和 C 字段暂时没有意义 +- FCS 字段是使用 CRC 的检验序列 +- 信息部分的长度不超过 1500 + +![](https://upload-images.jianshu.io/upload_images/21105806-17d0a6e89fe56df3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 5. CSMA/CD 协议* +用于广播信道中。在广播信道上,同一时间只能允许一台计算机发送数据。 + +CSMA/CD 表示载波监听多点接入 / 碰撞检测。 + +- 多点接入 :说明这是总线型网络,许多计算机以多点的方式连接到总线上。 +- 载波监听 :每个站都必须不停地监听信道。在发送前,如果监听到信道正在使用,就必须等待。 +- 碰撞检测 :在发送中,如果监听到信道已有其它站正在发送数据,就表示发生了碰撞。虽然每一个站在发送数据之前都已经监听到信道为空闲,但是由于电磁波的传播时延的存在,还是有可能会发生碰撞。 + +![](https://upload-images.jianshu.io/upload_images/21105806-41f8beae37dcc052.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +记端到端的传播时延为 τ,最先发送的站点最多经过 2τ 就可以知道是否发生了碰撞,称 2τ 为 争用期 。只有经过争用期之后还没有检测到碰撞,才能肯定这次发送不会发生碰撞。 + +当发生碰撞时,站点要停止发送,等待一段时间再发送。这个时间采用 截断二进制指数退避算法 来确定,从离散的整数集合 {0, 1, .., (2k-1)} 中随机取出一个数,记作 r,然后取 r 倍的争用期作为重传等待时间。 + ### 6. 扩展局域网* #### ⑴. 在物理层进行扩展 +使用集线器进行扩展。 + +集线器的主要功能是对接收到的信号进行放大,以扩大网络的传输距离。 + +集线器不能根据 MAC 地址进行转发,而是以广播的方式发送数据帧。 + +集线器是一种共享式的传输设备,意味着同一时刻只能传输一组数据帧。 + +![](https://upload-images.jianshu.io/upload_images/21105806-672b76cffeb72ea8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑵. 在链路层进行扩展 +最开始使用的是网桥,它收到一个帧时,根据帧的 MAC 地址,查找网桥中的地址表,确定帧转发的接口。 + +网桥不是共享式设备,因此性能比集线器这种共享式设备更高。 + +交换机的问世很快就淘汰了网桥,它实质上是一个多接口网桥,而网桥是两接口。交换机的每个接口都能直接与一个主机或者另一个交换机相连,并且一般都工作在全双工方式。 + +交换机具有自学习能力,学习的是交换表的内容。交换表中存储着 MAC 地址到接口的映射。下图中,交换机有 4 个接口,主机 A 向主机 B 发送数据帧时,交换机把主机 A 到接口 1 的映射写入交换表中。为了发送数据帧到 B,先查交换表,此时没有主机 B 的表项,那么主机 A 就发送广播帧,主机 C 和主机 D 会丢弃该帧。主机 B 收下之后,查找交换表得到主机 A 映射的接口为 1,就发送数据帧到接口 1,同时交换机添加主机 B 到接口 3 的映射。 + +![](https://upload-images.jianshu.io/upload_images/21105806-8813257ce86df728.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑶. 虚拟局域网 +虚拟局域网可以建立与物理位置无关的逻辑组,只有在同一个虚拟局域网中的成员才会收到链路层广播信息,例如下图中 (A1, A2, A3, A4) 属于一个虚拟局域网,A1 发送的广播会被 A2、A3、A4 收到,而其它站点收不到。 + +![](https://upload-images.jianshu.io/upload_images/21105806-a6524092a93a92d7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 7. MAC 层* +MAC 地址是 6 字节(48 位)的地址,用于唯一标识网络适配器(网卡),一台主机拥有多少个适配器就有多少个 MAC 地址,例如笔记本电脑普遍存在无线网络适配器和有线网络适配器。 + +![](https://upload-images.jianshu.io/upload_images/21105806-89e04a0fa88eb146.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +在 MAC 帧中: + +- 类型 :标记上层使用的协议; +- 数据 :长度在 46-1500 之间,如果太小则需要填充; +- FCS :帧检验序列,使用的是 CRC 检验方法; +- 前同步码 :只是为了计算 FCS 临时加入的,计算结束之后会丢弃。 + ## (四)、网络层 ### 1. 网际协议 IP 概述 +因为网络层是整个互联网的核心,因此应当让网络层尽可能简单。网络层向上只提供简单灵活的、无连接的、尽最大努力交互的数据报服务。 + +使用 IP 协议,可以把异构的物理网络连接起来,使得在网络层看起来好像是一个统一的网络。 + +![](https://upload-images.jianshu.io/upload_images/21105806-1771b0c8d73dc687.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +与 IP 协议配套使用的还有三个协议: + +- 地址解析协议 ARP(Address Resolution Protocol) +- 网际控制报文协议 ICMP(Internet Control Message Protocol) +- 网际组管理协议 IGMP(Internet Group Management Protocol) + +![](https://upload-images.jianshu.io/upload_images/21105806-5b7a6ea096149a28.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 2. IP 数据报格式 +![](https://upload-images.jianshu.io/upload_images/21105806-4e6eb90fd7b8e8c3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +- 版本 : 有 4(IPv4)和 6(IPv6)两个值; +- 首部长度 : 占 4 位,因此最大值为 15。值为 1 表示的是 1 个 32 位字的长度,也就是 4 字节。因为首部固定长度为 20 字节,因此该值最小为 5。如果可选字段的长度不是 4 字节的整数倍,就用尾部的填充部分来填充。 +- 区分服务 : 用来获得更好的服务,一般情况下不使用。 +- 总长度 : 包括首部长度和数据部分长度。 +- 标识 : 在数据报长度过长从而发生分片的情况下,相同数据报的不同分片具有相同的标识符。 +- 片偏移 : 和标识符一起,用于发生分片的情况。片偏移的单位为 8 字节。 + +![](https://upload-images.jianshu.io/upload_images/21105806-65bab67fbf94e0b6.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +- 生存时间 :TTL,它的存在是为了防止无法交付的数据报在互联网中不断兜圈子。以路由器跳数为单位,当 TTL 为 0 时就丢弃数据报。 +- 协议 :指出携带的数据应该上交给哪个协议进行处理,例如 ICMP、TCP、UDP 等。 +- 首部检验和 :因为数据报每经过一个路由器,都要重新计算检验和,因此检验和不包含数据部分可以减少计算的工作量。 + + ### 3. IP 地址编址方式 +IP 地址的编址方式经历了三个历史阶段: + +- 分类 +- 子网划分 +- 无分类 + #### ⑴. 分类 +由两部分组成,网络号和主机号,其中不同分类具有不同的网络号长度,并且是固定的。 + +IP 地址 ::= {< 网络号 >, < 主机号 >} + +![](https://upload-images.jianshu.io/upload_images/21105806-a90a86286ddc0882.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑵. 子网划分 +通过在主机号字段中拿一部分作为子网号,把两级 IP 地址划分为三级 IP 地址。注意,外部网络看不到子网的存在。 + +IP 地址 ::= {< 网络号 >, < 子网号 >, < 主机号 >} + +要使用子网,必须配置子网掩码。一个 B 类地址的默认子网掩码为 255.255.0.0,如果 B 类地址的子网占两个比特,那么子网掩码为 11111111 11111111 11000000 00000000,也就是 255.255.192.0。 + #### ⑶. 无分类 +无分类编址 CIDR 消除了传统 A 类、B 类和 C 类地址以及划分子网的概念,使用网络前缀和主机号来对 IP 地址进行编码,网络前缀的长度可以根据需要变化。 + +IP 地址 ::= {< 网络前缀号 >, < 主机号 >} + +CIDR 的记法上采用在 IP 地址后面加上网络前缀长度的方法,例如 128.14.35.7/20 表示前 20 位为网络前缀。 + +CIDR 的地址掩码可以继续称为子网掩码,子网掩码首 1 长度为网络前缀的长度。 + +一个 CIDR 地址块中有很多地址,一个 CIDR 表示的网络就可以表示原来的很多个网络,并且在路由表中只需要一个路由就可以代替原来的多个路由,减少了路由表项的数量。把这种通过使用网络前缀来减少路由表项的方式称为路由聚合,也称为 构成超网 。 + +在路由表中的项目由“网络前缀”和“下一跳地址”组成,在查找时可能会得到不止一个匹配结果,应当采用最长前缀匹配来确定应该匹配哪一个。 + ### 4. IP 地址和 MAC 地址 +网络层实现主机之间的通信,而链路层实现具体每段链路之间的通信。因此在通信过程中,IP 数据报的源地址和目的地址始终不变,而 MAC 地址随着链路的改变而改变。 + +![](https://upload-images.jianshu.io/upload_images/21105806-077f35d640f72b30.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 5. 地址解析协议 ARP +实现由 IP 地址得到 MAC 地址。 + +![](https://upload-images.jianshu.io/upload_images/21105806-4425c6bbd8e37177.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +每个主机都有一个 ARP 高速缓存,里面有本局域网上的各主机和路由器的 IP 地址到硬件地址的映射表。 + +如果主机 A 知道主机 B 的 IP 地址,但是 ARP 高速缓存中没有该 IP 地址到 MAC 地址的映射,此时主机 A 通过广播的方式发送 ARP 请求分组,主机 B 收到该请求后会发送 ARP 响应分组给主机 A 告知其 MAC 地址,随后主机 A 向其高速缓存中写入主机 B 的 IP 地址到硬件地址的映射。 + +![](https://upload-images.jianshu.io/upload_images/21105806-bbf54bd08ba4ec13.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 6. 路由器的结构 +路由器从功能上可以划分为:路由选择和分组转发。 + +分组转发结构由三个部分组成:交换结构、一组输入端口和一组输出端口。 + +![](https://upload-images.jianshu.io/upload_images/21105806-1172014db09b09d9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 7. 路由器分组转发流程 +1. 从数据报的首部提取目的主机的 IP 地址 D,得到目的网络地址 N。 +2. 若 N 就是与此路由器直接相连的某个网络地址,则进行直接交付; +3. 若路由表中有目的地址为 D 的特定主机路由,则把数据报传送给表中所指明的下一跳路由器; +4. 若路由表中有到达网络 N 的路由,则把数据报传送给路由表中所指明的下一跳路由器; +5. 若路由表中有一个默认路由,则把数据报传送给路由表中所指明的默认路由器; +6. 报告转发分组出错。 + +![](https://upload-images.jianshu.io/upload_images/21105806-fc83f21a95c3a14f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 8. 路由选择协议 +互联网使用的路由选择协议都是自适应的,能随着网络通信量和拓扑结构的变化而自适应地进行调整。 + +互联网可以划分为许多较小的自治系统 AS,一个 AS 可以使用一种和别的 AS 不同的路由选择协议。 + +可以把路由选择协议划分为两大类: + +- 内部网关协议 IGP(Interior Gateway Protocol):在 AS 内部使用,如 RIP 和 OSPF。 +- 外部网关协议 EGP(External Gateway Protocol):在 AS 之间使用,如 BGP。 + +![](https://upload-images.jianshu.io/upload_images/21105806-2d0da720aff11c2b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑴. 内部网关协议 RIP +RIP 是一种分布式的基于距离向量的路由选择协议。距离是指跳数,直接相连的路由器跳数为 1,跳数最多为 15,超过 15 表示不可达。 + +RIP 按固定的时间间隔仅和相邻路由器交换自己的路由表,经过若干次交换之后,所有路由器最终会知道到达本自治系统中任何一个网络的最短距离和下一跳路由器地址。 + +距离向量算法: + +- 对地址为 X 的相邻路由器发来的 RIP 报文,先修改报文中的所有项目,把下一跳字段中的地址改为 X,并把所有的距离字段加 1; +- 对修改后的 RIP 报文中的每一个项目,进行以下步骤: + - 若原来的路由表中没有目的网络 N,则把该项目添加到路由表中; + - 否则:若下一跳路由器地址是 X,则把收到的项目替换原来路由表中的项目;否则:若收到的项目中的距离 d 小于路由表中的距离,则进行更新(例如原始路由表项为 Net2, 5, P,新表项为 Net2, 4, X,则更新);否则什么也不做。 +- 若 3 分钟还没有收到相邻路由器的更新路由表,则把该相邻路由器标为不可达,即把距离置为 16。 + +RIP 协议实现简单,开销小,但是 RIP 能使用的最大距离为 15,限制了网络的规模。并且当网络出现故障时,要经过比较长的时间才能将此消息传送到所有路由器。 + #### ⑵. 内部网关协议 OSPF +开放最短路径优先 OSPF,是为了克服 RIP 的缺点而开发出来的。 + +开放表示 OSPF 不受某一家厂商控制,而是公开发表的;最短路径优先表示使用了 Dijkstra 提出的最短路径算法 SPF。 + +OSPF 具有以下特点: + +- 向本自治系统中的所有路由器发送信息,这种方法是洪泛法。 +- 发送的信息就是与相邻路由器的链路状态,链路状态包括与哪些路由器相连以及链路的度量,度量用费用、距离、时延、带宽等来表示。 +- 只有当链路状态发生变化时,路由器才会发送信息。 + +所有路由器都具有全网的拓扑结构图,并且是一致的。相比于 RIP,OSPF 的更新过程收敛的很快。 + #### ⑶. 外部网关协议 BGP +AS 之间的路由选择很困难,主要是互联网规模很大。并且各个 AS 内部使用不同的路由选择协议,就无法准确定义路径的度量。并且 AS 之间的路由选择必须考虑有关的策略,比如有些 AS 不愿意让其它 AS 经过。 + +BGP 只能寻找一条比较好的路由,而不是最佳路由。它采用路径向量路由选择协议。 + +每个 AS 都必须配置 BGP 发言人,通过在两个相邻 BGP 发言人之间建立 TCP 连接来交换路由信息。 + +![](https://upload-images.jianshu.io/upload_images/21105806-fdd780fcd8fea617.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 9. 网际控制报文协议 ICMP +ICMP 是为了更有效地转发 IP 数据报和提高交付成功的机会。它封装在 IP 数据报中,但是不属于高层协议。 + +![](https://upload-images.jianshu.io/upload_images/21105806-8120cc282efbc60e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +ICMP 报文分为差错报告报文和询问报文。 + +![](https://upload-images.jianshu.io/upload_images/21105806-b85c992a11db8775.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 10. 分组网间探测 PING +PING 是 ICMP 的一个重要应用,主要用来测试两台主机之间的连通性。 + +Ping 发送的 IP 数据报封装的是无法交付的 UDP 用户数据报。 + +Ping 的过程: + +1. 源主机向目的主机发送一连串的 IP 数据报。第一个数据报 P1 的生存时间 TTL 设置为 1,但 P1 到达路径上的第一个路由器 R1 时,R1 收下它并把 TTL 减 1,此时 TTL 等于 0,R1 就把 P1 丢弃,并向源主机发送一个 ICMP 时间超过差错报告报文; +2. 源主机接着发送第二个数据报 P2,并把 TTL 设置为 2。P2 先到达 R1,R1 收下后把 TTl 减 1 再转发给 R2,R2 收下后也把 TTL 减 1,由于此时 TTL 等于 0,R2 就丢弃 P2,并向源主机发送一个 ICMP 时间超过差错报文。 +3. 不断执行这样的步骤,知道最后一个数据报刚刚到达目的主机,主机不转发数据报,也不把 TTL 值减 1。但是因为数据报封装的是无法交付的 UDP,因此目的主机要向源主机发送 ICMP 终点不可达差错报告报文。 +4. 之后源主机知道了到达目的主机所经过的路由器 IP 地址以及到达每个路由器的往返时间。 + ### 11. 虚拟专用网 VPN +由于 IP 地址的紧缺,一个机构能申请到的 IP 地址数往往远小于本机构所拥有的主机数。并且一个机构并不需要把所有的主机接入到外部的互联网中,机构内的计算机可以使用仅在本机构有效的 IP 地址(专用地址)。 + +有三个专用地址块: + +- 10.0.0.0 ~ 10.255.255.255 +- 172.16.0.0 ~ 172.31.255.255 +- 192.168.0.0 ~ 192.168.255.255 + +VPN 使用公用的互联网作为本机构各专用网之间的通信载体。专用指机构内的主机只与本机构内的其它主机通信;虚拟指“好像是”,而实际上并不是,它有经过公用的互联网。 + +下图中,场所 A 和 B 的通信部经过互联网,如果场所 A 的主机 X 要和另一个场所 B 的主机 Y 通信,IP 数据报的源地址是 10.1.0.1,目的地址是 10.2.0.3。数据报先发送到与互联网相连的路由器 R1,R1 对内部数据进行加密,然后重新加上数据报的首部,源地址是路由器 R1 的全球地址 125.1.2.3,目的地址是路由器 R2 的全球地址 194.4.5.6。路由器 R2 收到数据报后将数据部分进行解密,恢复原来的数据报,此时目的地址为 10.2.0.3,就交付给 Y。 + +![](https://upload-images.jianshu.io/upload_images/21105806-b2ce2121fa85cfdb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 12. 网络地址转换 NAT +专用网内部的主机使用本地 IP 地址又想和互联网上的主机通信时,可以使用 NAT 来将本地 IP 转换为全球 IP。 + +在以前,NAT 将本地 IP 和全球 IP 一一对应,这种方式下拥有 n 个全球 IP 地址的专用网内最多只可以同时有 n 台主机接入互联网。为了更有效地利用全球 IP 地址,现在常用的 NAT 转换表把运输层的端口号也用上了,使得多个专用网内部的主机共用一个全球 IP 地址。使用端口号的 NAT 也叫做网络地址与端口转换 NAPT。 + +![](https://upload-images.jianshu.io/upload_images/21105806-7492bb5b191b0441.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ## (五)、传输层 +网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。运输层提供了进程间的逻辑通信,运输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看见的好像在两个运输层实体之间有一条端到端的逻辑通信信道。 + ### 1. UDP 和 TCP 的特点 +用户数据报协议 UDP(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部)。 + +传输控制协议 TCP(Transmission Control Protocol)是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块)。 + ### 2. UDP 首部格式 +![](https://upload-images.jianshu.io/upload_images/21105806-ef1fee59ae4ed6a9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。12 字节的伪首部是为了计算检验和临时添加的。 + ### 3. TCP 首部格式 +![](https://upload-images.jianshu.io/upload_images/21105806-4297d9bf75b7374a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +- 序号 :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。 +- 确认号 :期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。 +- 数据偏移 :指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。 +- 确认 ACK :当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。 +- 同步 SYN :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。 +- 终止 FIN :用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放运输连接。 +- 窗口 :窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。 + ### 4. TCP 的三次握手 +![](https://upload-images.jianshu.io/upload_images/21105806-0a73d7716a84d309.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +假设 A 为客户端,B 为服务器端。 + +1. 首先 B 处于 LISTEN(监听)状态,等待客户的连接请求。 +2. A 向 B 发送连接请求报文段,SYN=1,ACK=0,选择一个初始的序号 x。 +3. B 收到连接请求报文段,如果同意建立连接,则向 A 发送连接确认报文段,SYN=1,ACK=1,确认号为 x+1,同时也选择一个初始的序号 y。 +4. A 收到 B 的连接确认报文段后,还要向 B 发出确认,确认号为 y+1,序号为 x+1。 +5. B 收到 A 的确认后,连接建立。 + +**三次握手的原因** + +为了防止失效的连接请求到达服务器,让服务器错误打开连接。 + +失效的连接请求是指,客户端发送的连接请求在网络中滞留,客户端因为没及时收到服务器端发送的连接确认,因此就重新发送了连接请求。滞留的连接请求并不是丢失,之后还是会到达服务器。如果不进行第三次握手,那么服务器会误认为客户端重新请求连接,然后打开了连接。但是并不是客户端真正打开这个连接,因此客户端不会给服务器发送数据,这个连接就白白浪费了。 + ### 5. TCP 的四次挥手 +![](https://upload-images.jianshu.io/upload_images/21105806-63aa078e72109edb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +以下描述不讨论序号和确认号,因为序号和确认号的规则比较简单。并且不讨论 ACK,因为 ACK 在连接建立之后都为 1。 + +1. A 发送连接释放报文段,FIN=1; +2. B 收到之后发出确认,此时 TCP 属于半关闭状态,B 能向 A 发送数据但是 A 不能向 B 发送数据; +3. 当 B 要不再需要连接时,发送连接释放请求报文段,FIN=1 +4. A 收到后发出确认,进入 TIME-WAIT 状态,等待 2MSL 时间后释放连接。 +5. B 收到 A 的确认后释放连接。 + +**四次挥手的原因** + +客户端发送了 FIN 连接释放报文之后,服务器收到了这个报文,就进入了 CLOSE-WAIT 状态。这个状态是为了让服务器端发送还未传送完毕的数据,传送完毕之后,服务器会发送 FIN 连接释放报文。 + +**TIME_WAIT** + +客户端接收到服务器端的 FIN 报文后进入此状态,此时并不是直接进入 CLOSED 状态,还需要等待一个时间计时器设置的时间 2MSL。这么做有两个理由: + +1. 确保最后一个确认报文段能够到达。如果 B 没收到 A 发送来的确认报文段,那么就会重新发送连接释放请求报文段,A 等待一段时间就是为了处理这种情况的发生。 +2. 等待一段时间是为了让本连接持续时间内所产生的所有报文段都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文段。 + ### 6. TCP 滑动窗口 +![](https://upload-images.jianshu.io/upload_images/21105806-0192c1071c25e84c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗口大小。 + +发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接收窗口。 + +接收窗口只会对窗口内最后一个按序到达的字节进行确认,例如接收窗口已经收到的字节为 {31, 32, 34, 35},其中 {31, 32} 按序到达,而 {34, 35} 就不是,因此只对字节 32 进行确认。发送方得到一个字节的确认之后,就知道这个字节之前的所有字节都已经被接收。 + ### 7. TCP 可靠传输 +TCP 使用超时重传来实现可靠传输:如果一个已经发送的报文段在超时时间内没有收到确认,那么就重传这个报文段。 + +一个报文段从发送再到接收到确认所经过的时间称为往返时间 RTT,加权平均往返时间 RTTs 计算如下: + +![](https://upload-images.jianshu.io/upload_images/21105806-08cb7ea59672382d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +超时时间 RTO 应该略大于 RTTs,TCP 使用的超时时间计算如下: + +![](https://upload-images.jianshu.io/upload_images/21105806-14284a950b48895e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +其中 RTTd 为偏差。 + ### 8. TCP 流量控制 +流量控制是为了控制发送方发送速率,保证接收方来得及接收。 + +接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。 + ### 9. TCP 拥塞控制 +如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了让接收方能来得及接受,而拥塞控制是为了降低整个网络的拥塞程度。 + +![](https://upload-images.jianshu.io/upload_images/21105806-783a70377a84ba3a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +TCP 主要通过四种算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。发送方需要维护有一个叫做拥塞窗口(cwnd)的状态变量。注意拥塞窗口与发送方窗口的区别,拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。 + +为了便于讨论,做如下假设: + +1. 接收方有足够大的接收缓存,因此不会发生流量控制; +2. 虽然 TCP 的窗口基于字节,但是这里设窗口的大小单位为报文段。 + +![](https://upload-images.jianshu.io/upload_images/21105806-d8403d4375934832.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 10. 慢开始与拥塞避免 +发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 … + +注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。 + +如果出现了超时,则令 ssthresh = cwnd/2,然后重新执行慢开始。 + ### 11. 快重传与快恢复 +在接收方,要求每次接收到报文段都应该发送对已收到有序报文段的确认,例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。 + +在发送方,如果收到三个重复确认,那么可以确认下一个报文段丢失,例如收到三个 M2 ,则 M3 丢失。此时执行快重传,立即重传下一个报文段。 + +在这种情况下,只是丢失个别报文段,而不是网络拥塞,因此执行快恢复,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。 + +![](https://upload-images.jianshu.io/upload_images/21105806-959c608a1e45df4c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ## (六)、应用层 ### 1. 域名系统 DNS +把主机名解析为 IP 地址。 + +被设计成分布式系统。 + #### ⑴. 层次结构 +一个域名由多个层次构成,从上层到下层分别为顶级域名、二级域名、三级域名以及四级域名。所有域名可以画成一颗域名树。 + +![](https://upload-images.jianshu.io/upload_images/21105806-1ea61f3c18bed233.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +![](https://upload-images.jianshu.io/upload_images/21105806-9fabcf934d7d8bc8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +域名服务器可以分为以下四类: + +- 根域名服务器:解析顶级域名; +- 顶级域名服务器:解析二级域名; +- 权限域名服务器:解析区内的域名; +- 本地域名服务器:也称为默认域名服务器。可以在其中配置高速缓存。 + +区和域的概念不同,可以在一个域中划分多个区。图 b 在域 abc.com 中划分了两个区:abc.com 和 y.abc.com + +![](https://upload-images.jianshu.io/upload_images/21105806-e19f2e694aa9e29a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +因此就需要两个权限域名服务器: + +![](https://upload-images.jianshu.io/upload_images/21105806-f16b798383c0cb93.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑵. 解析过程 +主机向本地域名服务器解析的过程采用递归,而本地域名服务器向其它域名服务器解析可以使用递归和迭代两种方式。 + +迭代的方式下,本地域名服务器向一个域名服务器解析请求解析之后,结果返回到本地域名服务器,然后本地域名服务器继续向其它域名服务器请求解析;而递归的方式下,结果不是直接返回的,而是继续向前请求解析,最后的结果才会返回。 + +![](https://upload-images.jianshu.io/upload_images/21105806-af68dc94537431e4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 2. 文件传输协议 FTP +FTP 在运输层使用 TCP,并且需要建立两个并行的 TCP 连接:控制连接和数据连接。控制连接在整个会话期间一直保持打开,而数据连接在数据传送完毕之后就关闭。控制连接使用端口号 21,数据连接使用端口号 20。 + +![](https://upload-images.jianshu.io/upload_images/21105806-12b848681b888437.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 3. 远程终端协议 TELNET +TELNET 用于登录到远程主机上,并且远程主机上的输出也会返回。 + +TELNET 可以适应许多计算机和操作系统的差异,例如不同操作系统系统的换行符定义。 + ### 4. 电子邮件协议 +一个电子邮件系统由三部分组成:用户代理、邮件服务器以及邮件发送协议和读取协议。其中发送协议常用 SMTP,读取协议常用 POP3 和 IMAP。 + +![](https://upload-images.jianshu.io/upload_images/21105806-9df78b282645bcc3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + #### ⑴. POP3 +POP3 的特点是只要用户从服务器上读取了邮件,就把该邮件删除。 + #### ⑵. IMAP +IMAP 协议中客户端和服务器上的邮件保持同步,如果不去手动删除邮件,那么服务器上的邮件也不会被删除。IMAP 这种做法可以让用户随时随地去访问服务器上的邮件。IMAP 协议也支持创建自定义的文件夹。 + #### ⑶. SMTP +SMTP 只能发送 ASCII 码,而互联网邮件扩充 MIME 可以发送二进制文件。MIME 并没有改动或者取代 SMTP,而是增加邮件主题的结构,定义了非 ASCII 码的编码规则。 + +![](https://upload-images.jianshu.io/upload_images/21105806-92c3d67a60211ccb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + ### 5. 动态主机配置协议 DHCP +DHCP 提供了即插即用的连网方式,用户不再需要去手动配置 IP 地址等信息。 + +DHCP 配置的内容不仅是 IP 地址,还包括子网掩码、默认路由器 IP 地址、域名服务器的 IP 地址。 + +工作方式如下:需要 IP 地址的主机广播发送 DHCP 发现报文(将目的地址置为全 1,即 255.255.255.255:67,源地址设置为全 0,即 0.0.0.0:68),DHCP 服务器收到发现报文之后,则在 IP 地址池中取一个地址,发送 DHCP 提供报文给该主机。 + ### 6. 点对点传输 P2P +把某个文件分发的所有对等集合称为一个洪流。文件的数据单元称为文件块,它的大小是固定的。一个新的对等方加入某个洪流,一开始并没有文件块,但是能够从其它对等方中逐渐地下载到一些文件块,与此同时,它也为别的对等方上传一些文件块。 + +每个洪流都有一个基础设施,称为追踪器。当一个对等方加入洪流时,必须向追踪器登记,并周期性地通知追踪器它仍在洪流中。可以在任何时间加入和退出某个洪流。 + +一个新的对等方加入洪流时,追踪器会随机从洪流中选择若干个对等方,并让新对等方与这些对等方建立连接,把这些对等方称为相邻对等方。接收和发送文件块都是在相邻对等方中进行。 + +当一个对等方需要很多文件块时,通过使用最稀有优先的策略来取得文件块,也就是一个文件块在相邻对等方中副本最少,那么就优先请求这个文件块。 + +当很多对等方向同一个对等方请求文件块时,该对等方优先选择以最高速率向其发送文件块的对等方。 + +P2P 是一个分布式系统,任何时候都有对等方加入或者退出。使用分布式散列表 DHT,可以查找洪流中的资源和 IP 地址映射。 + ### 7. Web 页面请求过程 #### ⑴. DHCP 配置主机信息 +- 假设主机最开始没有 IP 地址以及其它信息,那么就需要先使用 DHCP 来获取。 +- 主机生成一个 DHCP 请求报文,并将这个报文放入具有目的端口 67 和源端口 68 的 UDP 报文段中。 +- 该报文段则被放入在一个具有广播 IP 目的地址(255.255.255.255) 和源 IP 地址(0.0.0.0)的 IP 数据报中。 +- 该数据报则被放置在 MAC 帧中,该帧具有目的地址 FF:FF:FF:FF:FF:FF,将广播到与交换机连接的所有设备。 +- 连接在交换机的 DHCP 服务器收到广播帧之后,不断地向上分解得到 IP 数据报、UDP 报文段、DHCP 请求报文,之后生成 DHCP ACK 报文,该报文包含以下信息:IP 地址、DNS 服务器的 IP 地址、默认网关路由器的 IP 地址和子网掩码。该报文被放入 UDP 报文段中,UDP 报文段有被放入 IP 数据报中,最后放入 MAC 帧中。 +- 该帧的目的地址是请求主机的 MAC 地址,因为交换机具有自学习能力,之前主机发送了广播帧之后就记录了 MAC 地址到其转发接口的交换表项,因此现在交换机就可以直接知道应该向哪个接口发送该帧。 +- 主机收到该帧后,不断分解得到 DHCP 报文。之后就配置它的 IP 地址、子网掩码和 DNS 服务器的 IP 地址,并在其 IP 转发表中安装默认网关。 + #### ⑵. ARP 解析 MAC 地址 +- 主机通过浏览器生成一个 TCP 套接字,套接字向 HTTP 服务器发送 HTTP 请求。为了生成该套接字,主机需要知道网站的域名对应的 IP 地址。 +- 主机生成一个 DNS 查询报文,该报文具有 53 号端口,因为 DNS 服务器的端口号是 53。 +- 该 DNS 查询报文被放入目的地址为 DNS 服务器 IP 地址的 IP 数据报中。 +- 该 IP 数据报被放入一个以太网帧中,该帧将发送到网关路由器。 +- DHCP 过程只知道网关路由器的 IP 地址,为了获取网关路由器的 MAC 地址,需要使用 ARP 协议。 +- 主机生成一个包含目的地址为网关路由器 IP 地址的 ARP 查询报文,将该 ARP 查询报文放入一个具有广播目的地址(FF:FF:FF:FF:FF:FF)的以太网帧中,并向交换机发送该以太网帧,交换机将该帧转发给所有的连接设备,包括网关路由器。 +- 网关路由器接收到该帧后,不断向上分解得到 ARP 报文,发现其中的 IP 地址与其接口的 IP 地址匹配,因此就发送一个 ARP 回答报文,包含了它的 MAC 地址,发回给主机。 + #### ⑶. DNS 解析域名 +- 知道了网关路由器的 MAC 地址之后,就可以继续 DNS 的解析过程了。 +- 网关路由器接收到包含 DNS 查询报文的以太网帧后,抽取出 IP 数据报,并根据转发表决定该 IP 数据报应该转发的路由器。 +- 因为路由器具有内部网关协议(RIP、OSPF)和外部网关协议(BGP)这两种路由选择协议,因此路由表中已经配置了网关路由器到达 DNS 服务器的路由表项。 +- 到达 DNS 服务器之后,DNS 服务器抽取出 DNS 查询报文,并在 DNS 数据库中查找待解析的域名。 +- 找到 DNS 记录之后,发送 DNS 回答报文,将该回答报文放入 UDP 报文段中,然后放入 IP 数据报中,通过路由器反向转发回网关路由器,并经过以太网交换机到达主机。 + #### ⑷. HTTP 请求页面 +- 有了 HTTP 服务器的 IP 地址之后,主机就能够生成 TCP 套接字,该套接字将用于向 Web 服务器发送 HTTP GET 报文。 +- 在生成 TCP 套接字之前,必须先与 HTTP 服务器进行三次握手来建立连接。生成一个具有目的端口 80 的 TCP SYN 报文段,并向 HTTP 服务器发送该报文段。 +- HTTP 服务器收到该报文段之后,生成 TCP SYNACK 报文段,发回给主机。 +- 连接建立之后,浏览器生成 HTTP GET 报文,并交付给 HTTP 服务器。 +- HTTP 服务器从 TCP 套接字读取 HTTP GET 报文,生成一个 HTTP 响应报文,将 Web 页面内容放入报文主体中,发回给主机。 +- 浏览器收到 HTTP 响应报文后,抽取出 Web 页面内容,之后进行渲染,显示 Web 页面。 + ### 8. 常用端口 +应用层协议|端口号|运输层协议 +--|:--:|--: +DNS|53|UDP +FTP|控制连接 21,数据连接 20|TCP +TELNET|23|TCP +DHCP|67 68|UDP +HTTP|80|TCP +SMTP|25|TCP +POP3|110|TCP +IMAP|143|TCP + # 三、HTTP ## (一)、基本概念 From 05405e57bb4bc5194e51fc555ba8f6a1571f20c5 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Mon, 22 Jun 2020 21:15:33 +0800 Subject: [PATCH 04/27] =?UTF-8?q?Create=20=E6=88=91=E5=87=AD=E8=BF=99?= =?UTF-8?q?=E4=B8=89=E6=8B=9B=E8=BD=BB=E6=9D=BE=E6=8B=BF=E5=88=B0offer.MD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...6\235\276\346\213\277\345\210\260offer.MD" | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 "Interview/\346\210\221\345\207\255\350\277\231\344\270\211\346\213\233\350\275\273\346\235\276\346\213\277\345\210\260offer.MD" diff --git "a/Interview/\346\210\221\345\207\255\350\277\231\344\270\211\346\213\233\350\275\273\346\235\276\346\213\277\345\210\260offer.MD" "b/Interview/\346\210\221\345\207\255\350\277\231\344\270\211\346\213\233\350\275\273\346\235\276\346\213\277\345\210\260offer.MD" new file mode 100644 index 0000000..fe5f388 --- /dev/null +++ "b/Interview/\346\210\221\345\207\255\350\277\231\344\270\211\346\213\233\350\275\273\346\235\276\346\213\277\345\210\260offer.MD" @@ -0,0 +1,154 @@ +我凭这三招轻松拿到offer +================= + + * [一、背景](#一背景) + * [二、找到工作的3个绝招](#二找到工作的3个绝招) + * [(一)、通过面试邀请](#一通过面试邀请) + * [(二)、通过面试(笔试和面试)](#二通过面试笔试和面试) + * [(三)、通过试用期](#三通过试用期) + * [三、总结](#三总结) +*** +# 一、背景 + +很多人都觉得java面试很难,找不到工作,总会把原因归根于: + +1. 自己学历低,不是计算机专业毕业的 +2. 培训出来的人太多,言下之意就是说,做Java这个行业的人数已经饱和 +3. 年底了公司不招了 +4. 没有实际工作经验 +5. 转行晚了,年纪大了 + +其实这一切都是自己迷茫时给自己找的借口,更或者说是给自己不愿努力找的的借口. + +当然今天在这里我并不想用什么理论来论证上面提到的这几个原因,我只相信实践,相信现实,这时候我们只需要换位思考一下; + +1. 从源头上分析入手,如果公司不招人,为什么招聘网站上那么多招聘信息,并且还在不断更新,难道发招聘信息不要钱么(你可以去百度一下,其实发招聘信息是很烧钱的)? +2. 那么多人转行做java,那么多培训机构,网上那么多的学习资料,不会都是假象吧,正所谓马克思所言,存在即是合理! +3. 每天都有新的程序员入职,难道这不是面试进去的,你可能会说面试通过的是少数,很多都找不到工作,当然这一点我并没有做调查,但是有一点可以确定的是, + +同样是人,为什么别人能面试进去,而自己不能勒(这里话说的有点难听...)如果你是真想找到好工作,牢记这句话,不要找借口:别人能做到的事,自己通过努力也一定能做到, + +缺什么补什么,不懂什么学什么,要有,如果公司只招一个人,必然就是自己,这种霸气! + +上面几点足以说明,公司是要招人的,也有人面试进去的,接下来我们详细分析一下找到工作的具体方法,记住我们不论做任何事情,一定是找方法而不是找借口 + +# 二、找到工作的3个绝招 + +1. 通过面试邀请 +2. 通过面试(笔试和面试) +3. 通过试用期 + +对你没有看错,这就是绝招,可能看到这里,你有点想骂娘,甚至心想,这不是废话么.... + +如果你理解不到或者读不懂,这确实是废话,就好比:好好学习,天天向上;早睡早起身体好等等,正所谓大道至简,说的就是这个意思. + +如果你是真的懂了,我相信你也不会阅读这篇文章,找工作对你来说绝对是很轻松的一件事,你也没必要继续往下面看 + +## (一)、通过面试邀请 + +如果你投递了简历,收不到面试邀请或者说收到很少的面试邀请,那么绝对是简历写得有问题,别去想是不是学历低,专业不对口,年底不招人,程序员饱和了,投简历的平台不对,最近运气差,去年没烧香等等一堆的借口; + +真正的原因而是写的简历有重大问题,应该赶紧找人帮你看看简历,这里强调一点,一定是找别人看简历,而不是自己在哪里乱改,就如同读书时的考试样,在考场上做错了的题,要想在考试中找出来并且改正确,几乎是不可能的事情,更多的是把正确的改错了... + +同样换位思考,如果你都能发现错误,就不会那样去写,很多人去改自己的简历,不外乎就是在加几个技能,在把项目写详细一点等,做一些无关紧要的修改.然后再投递出去,结果可能又是石沉大海...然后再改...再投, + +这样做的精神是值得称赞的,至少做到了屡败屡战的坚韧不拔精神,如果能坚持下去,最后获得面试邀请,也算是一种锻炼,可更多的人是把网上的职位都投递完了,也没有收到合意的面试邀请,最后直接崩溃了,开始怀疑人生...... + +上文中提到的找别人修改自己的简历,那么找谁来修改最合适勒,当然是专业的人做专业的事,是在合适不过的的了. + +可专业的修改必然是要收费的,就如同公司招专业的java技术人才干专业的事是一个道理.如果不愿意出钱的小伙伴也有其他办法, + +- 第一,你有没用认识在java行业干得比自己好的,如果有那么就找他帮着看一下. +- 第二,联系hr或者面试官问一下为什么自己的简历通过不了; +- 第三,最差的情况下你也应该问一下你的基友、同学、室友,就算他不是干java的,给你提不出什么正确的建议,但一起讨论一下也好过自己闭门造车. + +总结,以上就是通过面试邀请的绝招,记住如果没有面试邀请一定是简历写得有问题,一定要找人修改,最好找专业人士修改. + +## (二)、通过面试(笔试和面试) + +如果收到了面试邀请,通过面试就很容易了,有的小伙伴看到这里可能会说我是在吹牛,认为面试才是最难的..... + +这里我不想辩解什么,还是那句话用事实说话,每个人面试经历不一样最后得出的结果就不一样。 + +我们为什么会觉得面试很容易,这里就结合自己的经验给大家分析一下: + +面试和平常我们的考试不一样,平常我们考试是老师出题,我们回答,回答的正确与错误是在老师出题的时候就把答案确定了的,回答的内容必须和老师的答案一样才可以得分, + +但是考试范围那么广,每个知识点可以变化无穷的考,我们真的是很难做对。 + +不能做对的原因很简单,那就是我们不知道老师出什么题,给什么答案,但是,如果你自己给自己出题,自己给自己定答案,难道还会考不过.....这里说得有点啰嗦了,其目的是想让大家摆脱在学校的考试思维, + +记住:面试是把自己知道的技术告诉面试官,而不是点对点的回答面试官的问题;重要的事说3遍: + +1. 面试是把自己知道的技术告诉面试官,而不是点对点的回答面试官的问题; +2. 面试是把自己知道的技术告诉面试官,而不是点对点的回答面试官的问题; +3. 面试是把自己知道的技术告诉面试官,而不是点对点的回答面试官的问题; + +点对点的回答问题几乎很难面上,原因很简单虽然你回答的问题都是正确的,但是面试官没有通过你的回答确定你具备什么样的技术能力, + +更何况java的知识面很广,你很难知道每个知识点,举个简单的例子,下面是一场面试简要对话: + +>面试官:简要说一下你对spring的认识? +>求职者:spring是一个开源的轻量级的java框架。 +>面试官:简单说一下你对redis的认识? +>求职者:redis可以用来缓存数据。 +>面试官:了解Tomcat吗? +>求职者:小时候看过 +>......... + +上面的回答都是正确的,但是你肯定面不上......... + +这时候面试官快要崩溃了,自己要问的问题也问完了,但是还是不知道求职者的技术如何,然后就让你等通知,然后就没有然后了。 + +这就是为什么很多人面试完了总是等通知,有的面试者还在给自己找借口,说这家公司根本不招人,把人喊来面到耍的.... + +当然,上面的案例是夸张了一点,但实际面试中很多面试者也只是比上面多说一点点而已,根本体现不出你与其他面试者的差别,也体现不出你具备什么技术,更别说打动面试官了。 + +面试的本质是通过你说来展现你的技术,让面试官知道你的技术很好,如果在面中提现不出你的技术,那么面试必然失败。 + +只要你能深刻的认识到这一点,面试成功了99%,就好比只要高考题是你出的,你要考上清华、北大至少成功了99%是一个道理。 + +这样一来的话,在面试的时候,我们只需要把面试官往我们在面试前准备好的技术点引导就可以了。 + +但是问题的关键在于如何引导,准备什么样的技术点好勒,估计这是很多刚入行java编程小伙伴的苦恼,这个问题对于应聘不同岗位级别的是不同的,我们这里以应聘初级开发工程师为例简单说一下。 + +- 第一步:准备好面试要用的技术点,如多线程、jvm、高并发、数据结构、设计模式等 +- 第二步:简历写上对jvm、多线程有深入研究(引导面试官问这个),而其他技能上写熟练掌握 +- 第三步:实际回答问题的时候,面试官一般会问你提到的技术,然后你只需要滔滔不绝的说半个小时。 + +大体逻辑就是这样,准备技术点,引导面试官问,滔滔不绝的说,如果面试官听了后感觉像是上了一堂技术课,那么恭喜你面上了。 + +如果要准备得更加充分一点的话可以增加如下步骤: + +- 第一:模拟面试,找个技术比你好的,或者面试行家,进行模拟面试,并录音,反复分析,不断优化 +- 第二:实际面试中录音(如果场景可以的话),或者面试完后回忆面试问了什么问题,自己是如何回答的,详细的记录下来,同样找个行家,问一下这样回答是否合理,反复分析,不断优化 +- 第三:面试结束时,虚心的向面试官请教,自己哪里回答的不好(通常情况下你回答问题就算是错了,面试官也不会指出,只是让你等你通知) + +总之,必须找出面试不上的具体原因,并改正。如果能做到如上步骤,找个工作简直就是探囊取物,在简单不过了,你会收到很多面试入职通知书,你只需要选一家你顺眼的公司去上班就可以了。 + +总结,要想轻松通过面试,必须准备好技术特长,引导面试官,找行家模拟面试,找行家做面试后的分析。 + +## (三)、通过试用期 + +这一点就更简单了,大部分人只要通过了面试,都可以通过试用期。 + +正所谓:面试造飞机,上班扭螺丝;大概就是这个道理吧。 + +但是值得一提的是上班的时候遇到不懂的业务要及时问同事,不会的技术要及时查资料学习,或者问行家,千万不要不说话,在那里闭门造车, + +大家都以为你能搞定这个功能,结果等到最后测试功能的时候,你来一句做不来,那么你肯定会被开除! + +# 三、总结 + +面试需要用心准备,并不是技术学会了,就能找到工作,面试技巧和技术同等重要,否则在别人简单的学了一些技术, + +但是非常的重视面试技巧,花钱请专业的人辅导自己面试,最后轻松的找到了工作,而你苦心研究技术,却迟迟找不到工作,这样对你来说很不公平。 + +面试准备大体如下: + +1. 根据面试不同的岗位确定面试要准备的技术和资料,必须是系统化的资料,千万不要东看一点西看一点,这样效率很低。 +2. 写简历,理论上正确的简历投出去就应该有面试邀请,如果没有邀请,请找专业的人帮你修改简历。 +3. 找专业的人做模拟面试 +4. 正式面试 +5. 专业人士对面试进行分析,修正,找出面试失败的原因。 +6. 开心上班。 From c785c49caff9d6142643f39633a72c26c8684555 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Mon, 22 Jun 2020 21:16:45 +0800 Subject: [PATCH 05/27] =?UTF-8?q?Update=20=E6=88=91=E5=87=AD=E8=BF=99?= =?UTF-8?q?=E4=B8=89=E6=8B=9B=E8=BD=BB=E6=9D=BE=E6=8B=BF=E5=88=B0offer.MD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...350\275\273\346\235\276\346\213\277\345\210\260offer.MD" | 6 ++++++ 1 file changed, 6 insertions(+) diff --git "a/Interview/\346\210\221\345\207\255\350\277\231\344\270\211\346\213\233\350\275\273\346\235\276\346\213\277\345\210\260offer.MD" "b/Interview/\346\210\221\345\207\255\350\277\231\344\270\211\346\213\233\350\275\273\346\235\276\346\213\277\345\210\260offer.MD" index fe5f388..636b3da 100644 --- "a/Interview/\346\210\221\345\207\255\350\277\231\344\270\211\346\213\233\350\275\273\346\235\276\346\213\277\345\210\260offer.MD" +++ "b/Interview/\346\210\221\345\207\255\350\277\231\344\270\211\346\213\233\350\275\273\346\235\276\346\213\277\345\210\260offer.MD" @@ -89,11 +89,17 @@ 更何况java的知识面很广,你很难知道每个知识点,举个简单的例子,下面是一场面试简要对话: >面试官:简要说一下你对spring的认识? +> >求职者:spring是一个开源的轻量级的java框架。 +> >面试官:简单说一下你对redis的认识? +> >求职者:redis可以用来缓存数据。 +> >面试官:了解Tomcat吗? +> >求职者:小时候看过 +> >......... 上面的回答都是正确的,但是你肯定面不上......... From 1e23d62d5d92eddefb1e1a3e727d4ab17a5daf06 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Mon, 22 Jun 2020 21:17:44 +0800 Subject: [PATCH 06/27] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4b51573..ebe5d79 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ - [技术面试需要掌握的基础知识整理](https://github.com/Java-Super-Air/JavaStudy/blob/master/Interview/%E6%8A%80%E6%9C%AF%E9%9D%A2%E8%AF%95%E9%9C%80%E8%A6%81%E6%8E%8C%E6%8F%A1%E7%9A%84%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86%E6%95%B4%E7%90%86.MD) - [面试前的准备](https://github.com/Java-Super-Air/JavaStudy/blob/master/Interview/%E7%A8%8B%E5%BA%8F%E5%91%98%E9%9D%A2%E8%AF%95%E6%94%BB%E7%95%A5%EF%BC%9A%E9%9D%A2%E8%AF%95%E5%89%8D%E7%9A%84%E5%87%86%E5%A4%87.md) - [面试中的技巧](https://github.com/Java-Super-Air/JavaStudy/blob/master/Interview/%E7%A8%8B%E5%BA%8F%E5%91%98%E9%9D%A2%E8%AF%95%E6%94%BB%E7%95%A5%EF%BC%9A%E9%9D%A2%E8%AF%95%E4%B8%AD%E7%9A%84%E6%8A%80%E5%B7%A7.md) + - [我凭这三招轻松拿到offer](https://github.com/Java-Super-Air/JavaStudy/blob/master/Interview/%E6%88%91%E5%87%AD%E8%BF%99%E4%B8%89%E6%8B%9B%E8%BD%BB%E6%9D%BE%E6%8B%BF%E5%88%B0offer.MD) - **高效学习** From 1afb2e995731d282b3df6bc57dfe8cbd5fcf0768 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:27:43 +0800 Subject: [PATCH 07/27] =?UTF-8?q?Update=20Java=E5=9F=BA=E7=A1=80=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E9=9D=A2=E8=AF=95=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...350\257\206\351\235\242\350\257\225\351\242\230.md" | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git "a/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" "b/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" index 56b6221..ac7bcd3 100644 --- "a/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" @@ -1,4 +1,4 @@ -Java基础知识面试题目录 +# Java基础知识面试题目录 *** * [一.Java概述](#一java概述) * [1.何为编程](#1何为编程) @@ -119,6 +119,14 @@ Java基础知识面试题目录 * [(2).int 和 Integer 有什么区别](#2int-和-integer-有什么区别) * [(3).Integer a= 127 与 Integer b = 127相等吗](#3integer-a-127-与-integer-b--127相等吗) *** + +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊]()** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + # 一\.Java概述 ## 1\.何为编程 From 083d2e4c273c44c921f55285d0a03a4dc8749b8c Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:29:17 +0800 Subject: [PATCH 08/27] =?UTF-8?q?Update=20Java=E5=9F=BA=E7=A1=80=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E9=9D=A2=E8=AF=95=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\245\350\257\206\351\235\242\350\257\225\351\242\230.md" | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git "a/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" "b/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" index ac7bcd3..0ab6ab7 100644 --- "a/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" @@ -1,5 +1,5 @@ # Java基础知识面试题目录 -*** + * [一.Java概述](#一java概述) * [1.何为编程](#1何为编程) * [2.什么是Java](#2什么是java) @@ -124,9 +124,11 @@ >- **本文所涉及的所有面试题皆收自一线互联网大厂** >- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** ->- **资源分享群链接:[点击加入群聊]()** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** >- **视频资料请关注微信公众号"Java架构筑基"免费获取** +*** + # 一\.Java概述 ## 1\.何为编程 From e401f77ead99196ac84ca484cefa4d457757f2c8 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:31:19 +0800 Subject: [PATCH 09/27] =?UTF-8?q?Update=20Dubbo=E9=9D=A2=E8=AF=95=E9=A2=98?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dubbo\351\235\242\350\257\225\351\242\230.md" | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git "a/Interview/Dubbo\351\235\242\350\257\225\351\242\230.md" "b/Interview/Dubbo\351\235\242\350\257\225\351\242\230.md" index 86a19ef..cbc9d3f 100644 --- "a/Interview/Dubbo\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Dubbo\351\235\242\350\257\225\351\242\230.md" @@ -1,5 +1,5 @@ -Dubbo面试题目录 -*** +# Dubbo面试题目录 + * [一.基础知识](#一基础知识) * [1.为什么要用 Dubbo?](#1为什么要用-dubbo) * [2.Dubbo 是什么?](#2dubbo-是什么) @@ -61,6 +61,16 @@ Dubbo面试题目录 * [9.RPC的实现原理架构图](#9rpc的实现原理架构图) *** + +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.基础知识 ## 1.为什么要用 Dubbo? From 834557432921c52243828e6f0a3233f5c2a548c6 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:31:45 +0800 Subject: [PATCH 10/27] =?UTF-8?q?Update=20Java=E5=BC=82=E5=B8=B8=E9=9D=A2?= =?UTF-8?q?=E8=AF=95=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...45\270\270\351\235\242\350\257\225\351\242\230.md" | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git "a/Interview/Java\345\274\202\345\270\270\351\235\242\350\257\225\351\242\230.md" "b/Interview/Java\345\274\202\345\270\270\351\235\242\350\257\225\351\242\230.md" index d9e5231..bc82da6 100644 --- "a/Interview/Java\345\274\202\345\270\270\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Java\345\274\202\345\270\270\351\235\242\350\257\225\351\242\230.md" @@ -1,5 +1,5 @@ # Java异常面试题目录 -*** + * [一.Java异常架构与异常关键字](#一java异常架构与异常关键字) * [1.Java异常简介](#1java异常简介) * [2.Java异常架构](#2java异常架构) @@ -52,6 +52,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.Java异常架构与异常关键字 ## 1.Java异常简介 From 64e7266da5beb62a187327d2443d6c4c0b21daea Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:32:32 +0800 Subject: [PATCH 11/27] =?UTF-8?q?Update=20Java=E8=99=9A=E6=8B=9F=E6=9C=BA(?= =?UTF-8?q?JVM)=E9=9D=A2=E8=AF=95=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...34\272(JVM)\351\235\242\350\257\225\351\242\230.md" | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git "a/Interview/Java\350\231\232\346\213\237\346\234\272(JVM)\351\235\242\350\257\225\351\242\230.md" "b/Interview/Java\350\231\232\346\213\237\346\234\272(JVM)\351\235\242\350\257\225\351\242\230.md" index 9d7ef9c..78808e0 100644 --- "a/Interview/Java\350\231\232\346\213\237\346\234\272(JVM)\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Java\350\231\232\346\213\237\346\234\272(JVM)\351\235\242\350\257\225\351\242\230.md" @@ -49,6 +49,16 @@ * [2.常用的 JVM 调优的参数都有哪些?](#2常用的-jvm-调优的参数都有哪些) *** + +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.Java内存区域 ## 1.说一下 JVM 的主要组成部分及其作用? From 83e87fd032943a5cd96c365a12a1bc5c29ab8be6 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:33:48 +0800 Subject: [PATCH 12/27] =?UTF-8?q?Update=20Java=E9=9B=86=E5=90=88=E5=AE=B9?= =?UTF-8?q?=E5=99=A8=E9=9D=A2=E8=AF=95=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...50\351\235\242\350\257\225\351\242\230.md" | 810 +----------------- 1 file changed, 10 insertions(+), 800 deletions(-) diff --git "a/Interview/Java\351\233\206\345\220\210\345\256\271\345\231\250\351\235\242\350\257\225\351\242\230.md" "b/Interview/Java\351\233\206\345\220\210\345\256\271\345\231\250\351\235\242\350\257\225\351\242\230.md" index 29c6f45..93c06e2 100644 --- "a/Interview/Java\351\233\206\345\220\210\345\256\271\345\231\250\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Java\351\233\206\345\220\210\345\256\271\345\231\250\351\235\242\350\257\225\351\242\230.md" @@ -67,6 +67,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.集合容器概述 ## 1.什么是集合 @@ -135,803 +144,4 @@ Collection集合主要有List和Set两大接口 * List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。 * Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。 -Map是一个键值对集合,存储键、值和之间的映射。 Key无序,唯一;value 不要求有序,允许重复。Map没有继承于Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。 - -Map 的常用实现类:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap - -## 7.集合框架底层数据结构 - -**Collection** - -List - -* Arraylist: Object数组 -* Vector: Object数组 -* LinkedList: 双向循环链表 - -Set - -* HashSet(无序,唯一):基于 HashMap 实现的,底层采用 HashMap 来保存元素 -* LinkedHashSet: LinkedHashSet 继承与 HashSet,并且其内部是通过 LinkedHashMap 来实现的。有点类似于我们之前说的LinkedHashMap 其内部是基于 Hashmap 实现一样,不过还是有一点点区别的。 -* TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。) - -Map - -* HashMap: JDK1.8之前HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突).JDK1.8以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间 -* LinkedHashMap:LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。 -* HashTable: 数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的 -* TreeMap: 红黑树(自平衡的排序二叉树) - -## 8.哪些集合类是线程安全的? - -* vector:就比arraylist多了个同步化机制(线程安全),因为效率较低,现在已经不太建议使用。在web应用中,特别是前台页面,往往效率(页面响应速度)是优先考虑的。 -* statck:堆栈类,先进后出。 -* hashtable:就比hashmap多了个线程安全。 -* enumeration:枚举,相当于迭代器。 - -## 9.Java集合的快速失败机制 “fail-fast”? - -是java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生 fail-fast 机制。 - -例如:假设存在两个线程(线程1、线程2),线程1通过Iterator在遍历集合A中的元素,在某个时候线程2修改了集合A的结构(是结构上面的修改,而不是简单的修改集合元素的内容),那么这个时候程序就会抛出 ConcurrentModificationException 异常,从而产生fail-fast机制。 - -原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历。 - -解决办法: - -* 在遍历过程中,所有涉及到改变modCount值得地方全部加上synchronized。 -* 使用CopyOnWriteArrayList来替换ArrayList - -## 10.怎么确保一个集合不能被修改? - -可以使用 Collections. unmodifiableCollection(Collection c) 方法来创建一个只读集合,这样改变集合的任何操作都会抛出 Java. lang. UnsupportedOperationException 异常。 - -示例代码如下: - -``` -List list = new ArrayList<>(); -list. add("x"); -Collection clist = Collections. unmodifiableCollection(list); -clist. add("y"); // 运行时此行报错 -System. out. println(list. size()); -``` - -# 二.Collection接口 - -## 1.List接口 - -### (1). 迭代器 Iterator 是什么? - -Iterator 接口提供遍历任何 Collection 的接口。我们可以从一个 Collection 中使用迭代器方法来获取迭代器实例。迭代器取代了 Java 集合框架中的 Enumeration,迭代器允许调用者在迭代过程中移除元素。 - -### (2). Iterator 怎么使用?有什么特点? - -Iterator 使用代码如下: - -``` -List list = new ArrayList<>(); -Iterator it = list. iterator(); -while(it. hasNext()){ -  String obj = it. next(); -  System. out. println(obj); -} -``` -Iterator 的特点是只能单向遍历,但是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。 - -### (3). 如何边遍历边移除 Collection 中的元素? - -边遍历边修改 Collection 的唯一正确方式是使用 Iterator.remove() 方法,如下: - -``` -Iterator it = list.iterator(); -while(it.hasNext()){ -   *// do something* -   it.remove(); -} -``` -一种最常见的错误代码如下: -``` -for(Integer i : list){ -   list.remove(i) -} -``` -运行以上错误代码会报 ConcurrentModificationException 异常。这是因为当使用 foreach(for(Integer i : list)) 语句时,会自动生成一个iterator 来遍历该 list,但同时该 list 正在被 Iterator.remove() 修改。Java 一般不允许一个线程在遍历 Collection 时另一个线程修改它。 - -### (4). Iterator 和 ListIterator 有什么区别? - -* Iterator 可以遍历 Set 和 List 集合,而 ListIterator 只能遍历 List。 -* Iterator 只能单向遍历,而 ListIterator 可以双向遍历(向前/后遍历)。 -* ListIterator 实现 Iterator 接口,然后添加了一些额外的功能,比如添加一个元素、替换一个元素、获取前面或后面元素的索引位置。 - -### (5). 遍历一个 List 有哪些不同的方式?每种方法的实现原理是什么?Java 中 List 遍历的最佳实践是什么? - -遍历方式有以下几种: - -* for 循环遍历,基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置的元素,当读取到最后一个元素后停止。 -* 迭代器遍历,Iterator。Iterator 是面向对象的一个设计模式,目的是屏蔽不同数据集合的特点,统一遍历集合的接口。Java 在 Collections 中支持了 Iterator 模式。 -* foreach 循环遍历。foreach 内部也是采用了 Iterator 的方式实现,使用时不需要显式声明 Iterator 或计数器。优点是代码简洁,不易出错;缺点是只能做简单的遍历,不能在遍历过程中操作数据集合,例如删除、替换。 - -最佳实践:Java Collections 框架中提供了一个 RandomAccess 接口,用来标记 List 实现是否支持 Random Access。 - -* 如果一个数据集合实现了该接口,就意味着它支持 Random Access,按位置读取元素的平均时间复杂度为 O(1),如ArrayList。 -* 如果没有实现该接口,表示不支持 Random Access,如LinkedList。 - -推荐的做法就是,支持 Random Access 的列表可用 for 循环遍历,否则建议用 Iterator 或 foreach 遍历。 - -### (6). 说一下 ArrayList 的优缺点 - -ArrayList的优点如下: - -* ArrayList 底层以数组实现,是一种随机访问模式。ArrayList 实现了 RandomAccess 接口,因此查找的时候非常快。 -* ArrayList 在顺序添加一个元素的时候非常方便。 - -ArrayList 的缺点如下: - -* 删除元素的时候,需要做一次元素复制操作。如果要复制的元素很多,那么就会比较耗费性能。 -* 插入元素的时候,也需要做一次元素复制操作,缺点同上。 - -ArrayList 比较适合顺序添加、随机访问的场景。 - -### (7). 如何实现数组和 List 之间的转换? - -* 数组转 List:使用 Arrays. asList(array) 进行转换。 -* List 转数组:使用 List 自带的 toArray() 方法。 - -代码示例: - -``` -// list to array -List list = new ArrayList(); -list.add("123"); -list.add("456"); -list.toArray(); -// array to list -String[] array = new String[]{"123","456"}; -Arrays.asList(array); -``` - -### (8). ArrayList 和 LinkedList 的区别是什么? - -* 数据结构实现:ArrayList 是动态数组的数据结构实现,而 LinkedList 是双向链表的数据结构实现。 -* 随机访问效率:ArrayList 比 LinkedList 在随机访问的时候效率要高,因为 LinkedList 是线性的数据存储方式,所以需要移动指针从前往后依次查找。 -* 增加和删除效率:在非首尾的增加和删除操作,LinkedList 要比 ArrayList 效率要高,因为 ArrayList 增删操作要影响数组内的其他数据的下标。 -* 内存空间占用:LinkedList 比 ArrayList 更占内存,因为 LinkedList 的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。 -* 线程安全:ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全; - -综合来说,在需要频繁读取集合中的元素时,更推荐使用 ArrayList,而在插入和删除操作较多时,更推荐使用 LinkedList。 - -补充:数据结构基础之双向链表 - -双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。 - -### (9). ArrayList 和 Vector 的区别是什么? - -这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合 - -* 线程安全:Vector 使用了 Synchronized 来实现线程同步,是线程安全的,而 ArrayList 是非线程安全的。 -* 性能:ArrayList 在性能方面要优于 Vector。 -* 扩容:ArrayList 和 Vector 都会根据实际的需要动态的调整容量,只不过在 Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。 - -Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。 - -Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。 - -### (10). 插入数据时,ArrayList、LinkedList、Vector谁速度较快?阐述 ArrayList、Vector、LinkedList 的存储性能和特性? - -ArrayList、LinkedList、Vector 底层的实现都是使用数组方式存储数据。数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。 - -Vector 中的方法由于加了 synchronized 修饰,因此 Vector 是线程安全容器,但性能上较ArrayList差。 - -LinkedList 使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但插入数据时只需要记录当前项的前后项即可,所以 LinkedList 插入速度较快。 - -### (11). 多线程场景下如何使用 ArrayList? - -ArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。例如像下面这样: - -``` -List synchronizedList = Collections.synchronizedList(list); -synchronizedList.add("aaa"); -synchronizedList.add("bbb"); -for (int i = 0; i < synchronizedList.size(); i++) { -    System.out.println(synchronizedList.get(i)); -} -``` - -### (12). 为什么 ArrayList 的 elementData 加上 transient 修饰? - -ArrayList 中的数组定义如下: - -``` -private transient Object[] elementData; -``` -再看一下 ArrayList 的定义: -``` -public class ArrayList extends AbstractList -     implements List, RandomAccess, Cloneable, java.io.Serializable -``` -可以看到 ArrayList 实现了 Serializable 接口,这意味着 ArrayList 支持序列化。transient 的作用是说不希望 elementData 数组被序列化,重写了 writeObject 实现: -``` -private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ -    *// Write out element count, and any hidden stuff* -        int expectedModCount = modCount; -    s.defaultWriteObject(); -    *// Write out array length* -        s.writeInt(elementData.length); -    *// Write out all elements in the proper order.* -        for (int i=0; i map; -public HashSet() { -    map = new HashMap<>(); -} -public boolean add(E e) { -    // 调用HashMap的put方法,PRESENT是一个至始至终都相同的虚值 - return map.put(e, PRESENT)==null; -} -``` -hashCode()与equals()的相关规定: -* 如果两个对象相等,则hashcode一定也是相同的 -* 两个对象相等,对两个equals方法返回true -* 两个对象有相同的hashcode值,它们也不一定是相等的 -* 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖 -* hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。 - -==与equals的区别 - -* ==是判断两个变量或实例是不是指向同一个内存空间 equals是判断两个变量或实例所指向的内存空间的值是不是相同 -* ==是指对内存地址进行比较 equals()是对字符串的内容进行比较3.==指引用是否相同 equals()指的是值是否相同 - -### (3). HashSet与HashMap的区别 - -| **HashMap** | **HashSet** | -|:----:|:----:|:----:|:----:| -| 实现了Map接口 | 实现Set接口 | -| 存储键值对 | 仅存储对象 | -| 调用put()向map中添加元素 | 调用add()方法向Set中添加元素 | -| HashMap使用键(Key)计算Hashcode | HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false | -| HashMap相对于HashSet较快,因为它是使用唯一的键获取对象 | HashSet较HashMap来说比较慢 | - -## 3.Queue - -### (1). BlockingQueue是什么? - -Java.util.concurrent.BlockingQueue是一个队列,在进行检索或移除一个元素的时候,它会等待队列变为非空;当在添加一个元素时,它会等待队列中的可用空间。BlockingQueue接口是Java集合框架的一部分,主要用于实现生产者-消费者模式。我们不需要担心等待生产者有可用的空间,或消费者有可用的对象,因为它都在BlockingQueue的实现类中被处理了。Java提供了集中BlockingQueue的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue,、SynchronousQueue等。 - -### (2). 在 Queue 中 poll()和 remove()有什么区别? - -* 相同点:都是返回第一个元素,并在队列中删除返回的对象。 -* 不同点:如果没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。 - -代码示例: - -``` -Queue queue = new LinkedList(); -queue. offer("string"); // add -System. out. println(queue. poll()); -System. out. println(queue. remove()); -System. out. println(queue. size()); -``` - -# 三.Map接口 - -## 1.说一下 HashMap 的实现原理? - -HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。 - -HashMap的数据结构: 在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。 - -HashMap 基于 Hash 算法实现的 - -* 当我们往Hashmap中put元素时,利用key的hashCode重新hash计算出当前对象的元素在数组中的下标 -* 存储时,如果出现hash值相同的key,此时有两种情况。(1)如果key相同,则覆盖原始值;(2)如果key不同(出现冲突),则将当前的key-value放入链表中 -* 获取时,直接找到hash值对应的下标,在进一步判断key是否相同,从而找到对应值。 -* 理解了以上过程就不难明白HashMap是如何解决hash冲突的问题,核心就是使用了数组的存储方式,然后将冲突的key的对象放入链表中,一旦发现冲突就在链表中做进一步的对比。 - -需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn) - -## 2.HashMap在JDK1.7和JDK1.8中有哪些不同?HashMap的底层实现 - -在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做拉链法的方式可以解决哈希冲突。 - -### (1). JDK1.8之前 - -JDK1.8之前采用的是拉链法。拉链法:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。 - -![图片](https://uploader.shimo.im/f/m7Z3P9PP9GEFN8gL.png!thumbnail) - -### (2). JDK1.8之后 - -相比于之前的版本,jdk1.8在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。 - -![图片](https://uploader.shimo.im/f/ttJG9qpBfO8pXJPx.png!thumbnail) - -### (3). JDK1.7 VS JDK1.8 比较 - -JDK1.8主要解决或优化了一下问题: - -* resize 扩容优化 -* 引入了红黑树,目的是避免单条链表过长而影响查询效率,红黑树算法请参考 -* 解决了多线程死循环问题,但仍是非线程安全的,多线程时可能会造成数据丢失问题。 -| **不同** | **JDK 1.7** | **JDK1.8** | -|:----:|:----:|:----:| -| 存储结构 | 数组 + 链表 | 数组 + 链表 + 红黑树 | -| 初始化方式 | 单独函数:inflateTable() | 直接集成到了扩容函数resize()中 | -| hash值计算方式 | 扰动处理 = 9次扰动 = 4次位运算 + 5次异或运算 | 扰动处理 = 2次扰动 = 1次位运算 + 1次异或运算 | -| 存放数据的规则 | 无冲突时,存放数组;冲突时,存放链表 | 无冲突时,存放数组;冲突 & 链表长度 < 8:存放单链表;冲突 & 链表长度 > 8:树化并存放红黑树 | -| 插入数据方式 | 头插法(先讲原位置的数据移到后1位,再插入数据到该位置) | 尾插法(直接插入到链表尾部/红黑树) | -| 扩容后存储位置的计算方式 | 全部按照原来方法进行计算(即hashCode ->> 扰动函数 ->> (h&length-1)) | 按照扩容后的规律计算(即扩容后的位置=原位置 or 原位置 + 旧容量) | - -## 3.HashMap的put方法的具体流程? - -当我们put的时候,首先计算 key的hash值,这里调用了 hash方法,hash方法实际是让key.hashCode()与key.hashCode()>>>16进行异或操作,高16bit补0,一个数和0异或不变,所以 hash 函数大概的作用就是:高16bit不变,低16bit和高16bit做了一个异或,目的是减少碰撞。按照函数注释,因为bucket数组大小是2的幂,计算下标index = (table.length - 1) & hash,如果不做 hash 处理,相当于散列生效的只有几个低 bit 位,为了减少散列的碰撞,设计者综合考虑了速度、作用、质量之后,使用高16bit和低16bit异或来简单处理减少碰撞,而且JDK8中用了复杂度 O(logn)的树结构来提升碰撞下的性能。 - -putVal方法执行流程图 - -![图片](https://uploader.shimo.im/f/JcWMYA65MBwu1UEw.png!thumbnail) - -``` -public V put(K key, V value) { -    return putVal(hash(key), key, value, false, true); -} -static final int hash(Object key) { -    int h; -    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); -} -//实现Map.put和相关方法 -final V putVal(int hash, K key, V value, boolean onlyIfAbsent, -                   boolean evict) { -    Node[] tab; Node p; int n, i; -    // 步骤①:tab为空则创建  -    // table未初始化或者长度为0,进行扩容 -    if ((tab = table) == null || (n = tab.length) == 0) -        n = (tab = resize()).length; -    // 步骤②:计算index,并对null做处理   -    // (n - 1) & hash 确定元素存放在哪个桶中,桶为空,新生成结点放入桶中(此时,这个结点是放在数组中) -    if ((p = tab[i = (n - 1) & hash]) == null) -        tab[i] = newNode(hash, key, value, null); -    // 桶中已经存在元素 -    else { -        Node e; K k; -        // 步骤③:节点key存在,直接覆盖value  -        // 比较桶中第一个元素(数组中的结点)的hash值相等,key相等 -        if (p.hash == hash && -            ((k = p.key) == key || (key != null && key.equals(k)))) -                // 将第一个元素赋值给e,用e来记录 -                e = p; -        // 步骤④:判断该链为红黑树  -        // hash值不相等,即key不相等;为红黑树结点 -        // 如果当前元素类型为TreeNode,表示为红黑树,putTreeVal返回待存放的node, e可能为null -        else if (p instanceof TreeNode) -            // 放入树中 -            e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value); -        // 步骤⑤:该链为链表  -        // 为链表结点 -        else { -            // 在链表最末插入结点 -            for (int binCount = 0; ; ++binCount) { -                // 到达链表的尾部 -                 -                //判断该链表尾部指针是不是空的 -                if ((e = p.next) == null) { -                    // 在尾部插入新结点 -                    p.next = newNode(hash, key, value, null); -                    //判断链表的长度是否达到转化红黑树的临界值,临界值为8 -                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st -                        //链表结构转树形结构 -                        treeifyBin(tab, hash); -                    // 跳出循环 -                    break; -                } -                // 判断链表中结点的key值与插入的元素的key值是否相等 -                if (e.hash == hash && -                    ((k = e.key) == key || (key != null && key.equals(k)))) -                    // 相等,跳出循环 -                    break; -                // 用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表 -                p = e; -            } -        } -        //判断当前的key已经存在的情况下,再来一个相同的hash值、key值时,返回新来的value这个值 -        if (e != null) {  -            // 记录e的value -            V oldValue = e.value; -            // onlyIfAbsent为false或者旧值为null -            if (!onlyIfAbsent || oldValue == null) -                //用新值替换旧值 -                e.value = value; -            // 访问后回调 -            afterNodeAccess(e); -            // 返回旧值 -            return oldValue; -        } -    } -    // 结构性修改 -    ++modCount; -    // 步骤⑥:超过最大容量就扩容  -    // 实际大小大于阈值则扩容 -    if (++size > threshold) -        resize(); -    // 插入后回调 -    afterNodeInsertion(evict); -    return null; -} -``` -* ①.判断键值对数组table[i]是否为空或为null,否则执行resize()进行扩容; -* ②.根据键值key计算hash值得到插入的数组索引i,如果table[i]==null,直接新建节点添加,转向⑥,如果table[i]不为空,转向③; -* ③.判断table[i]的首个元素是否和key一样,如果相同直接覆盖value,否则转向④,这里的相同指的是hashCode以及equals; -* ④.判断table[i] 是否为treeNode,即table[i] 是否是红黑树,如果是红黑树,则直接在树中插入键值对,否则转向⑤; -* ⑤.遍历table[i],判断链表长度是否大于8,大于8的话把链表转换为红黑树,在红黑树中执行插入操作,否则进行链表的插入操作;遍历过程中若发现key已经存在直接覆盖value即可; -* ⑥.插入成功后,判断实际存在的键值对数量size是否超多了最大容量threshold,如果超过,进行扩容。 - -## 4.HashMap的扩容操作是怎么实现的? - -* ①.在jdk1.8中,resize方法是在hashmap中的键值对大于阀值时或者初始化时,就调用resize方法进行扩容; -* ②.每次扩展的时候,都是扩展2倍; -* ③.扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。 - -在putVal()中,我们看到在这个函数里面使用到了2次resize()方法,resize()方法表示的在进行第一次初始化时会对其进行扩容,或者当该数组的实际大小大于其临界值值(第一次为12),这个时候在扩容的同时也会伴随的桶上面的元素进行重新分发,这也是JDK1.8版本的一个优化的地方,在1.7中,扩容之后需要重新去计算其Hash值,根据Hash值对其进行分发,但在1.8版本中,则是根据在同一个桶的位置中进行判断(e.hash & oldCap)是否为0,重新进行hash分配后,该元素的位置要么停留在原始位置,要么移动到原始位置+增加的数组大小这个位置上 - -``` -final Node[] resize() { -    Node[] oldTab = table;//oldTab指向hash桶数组 -    int oldCap = (oldTab == null) ? 0 : oldTab.length; -    int oldThr = threshold; -    int newCap, newThr = 0; -    if (oldCap > 0) {//如果oldCap不为空的话,就是hash桶数组不为空 -        if (oldCap >= MAXIMUM_CAPACITY) {//如果大于最大容量了,就赋值为整数最大的阀值 -            threshold = Integer.MAX_VALUE; -            return oldTab;//返回 -        }//如果当前hash桶数组的长度在扩容后仍然小于最大容量 并且oldCap大于默认值16 -        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && -                 oldCap >= DEFAULT_INITIAL_CAPACITY) -            newThr = oldThr << 1; // double threshold 双倍扩容阀值threshold -    } -    // 旧的容量为0,但threshold大于零,代表有参构造有cap传入,threshold已经被初始化成最小2的n次幂 -    // 直接将该值赋给新的容量 -    else if (oldThr > 0) // initial capacity was placed in threshold -        newCap = oldThr; -    // 无参构造创建的map,给出默认容量和threshold 16, 16*0.75 -    else {               // zero initial threshold signifies using defaults -        newCap = DEFAULT_INITIAL_CAPACITY; -        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); -    } -    // 新的threshold = 新的cap * 0.75 -    if (newThr == 0) { -        float ft = (float)newCap * loadFactor; -        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? -                  (int)ft : Integer.MAX_VALUE); -    } -    threshold = newThr; -    // 计算出新的数组长度后赋给当前成员变量table -    @SuppressWarnings({"rawtypes","unchecked"}) -        Node[] newTab = (Node[])new Node[newCap];//新建hash桶数组 -    table = newTab;//将新数组的值复制给旧的hash桶数组 -    // 如果原先的数组没有初始化,那么resize的初始化工作到此结束,否则进入扩容元素重排逻辑,使其均匀的分散 -    if (oldTab != null) { -        // 遍历新数组的所有桶下标 -        for (int j = 0; j < oldCap; ++j) { -            Node e; -            if ((e = oldTab[j]) != null) { -                // 旧数组的桶下标赋给临时变量e,并且解除旧数组中的引用,否则就数组无法被GC回收 -                oldTab[j] = null; -                // 如果e.next==null,代表桶中就一个元素,不存在链表或者红黑树 -                if (e.next == null) -                    // 用同样的hash映射算法把该元素加入新的数组 -                    newTab[e.hash & (newCap - 1)] = e; -                // 如果e是TreeNode并且e.next!=null,那么处理树中元素的重排 -                else if (e instanceof TreeNode) -                    ((TreeNode)e).split(this, newTab, j, oldCap); -                // e是链表的头并且e.next!=null,那么处理链表中元素重排 -                else { // preserve order -                    // loHead,loTail 代表扩容后不用变换下标,见注1 -                    Node loHead = null, loTail = null; -                    // hiHead,hiTail 代表扩容后变换下标,见注1 -                    Node hiHead = null, hiTail = null; -                    Node next; -                    // 遍历链表 -                    do {              -                        next = e.next; -                        if ((e.hash & oldCap) == 0) { -                            if (loTail == null) -                                // 初始化head指向链表当前元素e,e不一定是链表的第一个元素,初始化后loHead -                                // 代表下标保持不变的链表的头元素 -                                loHead = e; -                            else                                 -                                // loTail.next指向当前e -                                loTail.next = e; -                            // loTail指向当前的元素e -                            // 初始化后,loTail和loHead指向相同的内存,所以当loTail.next指向下一个元素时, -                            // 底层数组中的元素的next引用也相应发生变化,造成lowHead.next.next..... -                            // 跟随loTail同步,使得lowHead可以链接到所有属于该链表的元素。 -                            loTail = e;                            -                        } -                        else { -                            if (hiTail == null) -                                // 初始化head指向链表当前元素e, 初始化后hiHead代表下标更改的链表头元素 -                                hiHead = e; -                            else -                                hiTail.next = e; -                            hiTail = e; -                        } -                    } while ((e = next) != null); -                    // 遍历结束, 将tail指向null,并把链表头放入新数组的相应下标,形成新的映射。 -                    if (loTail != null) { -                        loTail.next = null; -                        newTab[j] = loHead; -                    } -                    if (hiTail != null) { -                        hiTail.next = null; -                        newTab[j + oldCap] = hiHead; -                    } -                } -            } -        } -    } -    return newTab; -} -``` - -## 5.HashMap是怎么解决哈希冲突的? - -答:在解决这个问题之前,我们首先需要知道什么是哈希冲突,而在了解哈希冲突之前我们还要知道什么是哈希才行; - -### (1). 什么是哈希? - -Hash,一般翻译为“散列”,也有直接音译为“哈希”的,这就是把任意长度的输入通过散列算法,变换成固定长度的输出,该输出就是散列值(哈希值);这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来唯一的确定输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。 - -所有散列函数都有如下一个基本特性**:根据同一散列函数计算出的散列值如果不同,那么输入值肯定也不同。但是,根据同一散列函数计算出的散列值如果相同,输入值不一定相同**。 - -### (2). 什么是哈希冲突? - -当两个不同的输入值,根据同一散列函数计算出相同的散列值的现象,我们就把它叫做碰撞(哈希碰撞)。 - -### (3). HashMap的数据结构 - -在Java中,保存数据有两种比较简单的数据结构:数组和链表。数组的特点是:寻址容易,插入和删除困难;链表的特点是:寻址困难,但插入和删除容易;所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做链地址法的方式可以解决哈希冲突: - -![图片](https://uploader.shimo.im/f/MwJ6gfjMInUwoPIv.png!thumbnail) - -这样我们就可以将拥有相同哈希值的对象组织成一个链表放在hash值所对应的bucket下,但相比于hashCode返回的int类型,我们HashMap初始的容量大小DEFAULT_INITIAL_CAPACITY = 1 << 4(即2的四次方16)要远小于int类型的范围,所以我们如果只是单纯的用hashCode取余来获取对应的bucket这将会大大增加哈希碰撞的概率,并且最坏情况下还会将HashMap变成一个单链表,所以我们还需要对hashCode作一定的优化 - -### (4). hash()函数 - -上面提到的问题,主要是因为如果使用hashCode取余,那么相当于参与运算的只有hashCode的低位,高位是没有起到任何作用的,所以我们的思路就是让hashCode取值出的高位也参与运算,进一步降低hash碰撞的概率,使得数据分布更平均,我们把这样的操作称为扰动,在JDK 1.8中的hash()函数如下: - -``` -static final int hash(Object key) { -    int h; -    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);// 与自己右移16位进行异或运算(高低位异或) -} -``` -这比在JDK 1.7中,更为简洁,相比在1.7中的4次位运算,5次异或运算(9次扰动),在1.8中,只进行了1次位运算和1次异或运算(2次扰动); - -### (5). JDK1.8新增红黑树 - -![图片](https://uploader.shimo.im/f/VvIndOPZ3Rwcs3l5.png!thumbnail) - -通过上面的链地址法(使用散列表)和扰动函数我们成功让我们的数据分布更平均,哈希碰撞减少,但是当我们的HashMap中存在大量数据时,加入我们某个bucket下对应的链表有n个元素,那么遍历时间复杂度就为O(n),为了针对这个问题,JDK1.8在HashMap中新增了红黑树的数据结构,进一步使得遍历复杂度降低至O(logn); - -### (6). 总结 - -简单总结一下HashMap是使用了哪些方法来有效解决哈希冲突的: - -* 使用链地址法(使用散列表)来链接拥有相同hash值的数据; -* 使用2次扰动函数(hash函数)来降低哈希冲突的概率,使得数据分布更平均; -* 引入红黑树进一步降低遍历的时间复杂度,使得遍历更快; - -## 6.能否使用任何类作为 Map 的 key? - -可以使用任何类作为 Map 的 key,然而在使用之前,需要考虑以下几点: - -* 如果类重写了 equals() 方法,也应该重写 hashCode() 方法。 -* 类的所有实例需要遵循与 equals() 和 hashCode() 相关的规则。 -* 如果一个类没有使用 equals(),不应该在 hashCode() 中使用它。 -* 用户自定义 Key 类最佳实践是使之为不可变的,这样 hashCode() 值可以被缓存起来,拥有更好的性能。不可变的类也可以确保 hashCode() 和 equals() 在未来不会改变,这样就会解决与可变相关的问题了。 - -## 7.为什么HashMap中String、Integer这样的包装类适合作为K? - -答:String、Integer等包装类的特性能够保证Hash值的不可更改性和计算准确性,能够有效的减少Hash碰撞的几率 - -* 都是final类型,即不可变性,保证key的不可更改性,不会存在获取hash值不同的情况 -* 内部已重写了equals()、hashCode()等方法,遵守了HashMap内部的规范(不清楚可以去上面看看putValue的过程),不容易出现Hash值计算错误的情况; - -## 8.如果使用Object作为HashMap的Key,应该怎么办呢? - -答:重写hashCode()和equals()方法 - -* 重写hashCode()是因为需要计算存储数据的存储位置,需要注意不要试图从散列码计算中排除掉一个对象的关键部分来提高性能,这样虽然能更快但可能会导致更多的Hash碰撞; -* 重写equals()方法,需要遵守自反性、对称性、传递性、一致性以及对于任何非null的引用值x,x.equals(null)必须返回false的这几个特性,目的是为了保证key在哈希表中的唯一性; - -## 9.HashMap为什么不直接使用hashCode()处理后的哈希值直接作为table的下标? - -答:hashCode()方法返回的是int整数类型,其范围为-(2 ^ 31)~(2 ^ 31 - 1),约有40亿个映射空间,而HashMap的容量范围是在16(初始化默认值)~2 ^ 30,HashMap通常情况下是取不到最大值的,并且设备上也难以提供这么多的存储空间,从而导致通过hashCode()计算出的哈希值可能不在数组大小范围内,进而无法匹配存储位置; - -**那怎么解决呢?** - -* HashMap自己实现了自己的hash()方法,通过两次扰动使得它自己的哈希值高低位自行进行异或运算,降低哈希碰撞概率也使得数据分布更平均; -* 在保证数组长度为2的幂次方的时候,使用hash()运算之后的值与运算(&)(数组长度 - 1)来获取数组下标的方式进行存储,这样一来是比取余操作更加有效率,二来也是因为只有当数组长度为2的幂次方时,h&(length-1)才等价于h%length,三来解决了“哈希值与数组大小范围不匹配”的问题; - -## 10.HashMap 的长度为什么是2的幂次方 - -为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法。 - -**这个算法应该如何设计呢?** - -我们首先可能会想到采用%取余的操作来实现。但是,重点来了:“取余(%)操作中如果除数是2的幂次则等价于与其除数减一的与(&)操作(也就是说 hash%length==hash&(length-1)的前提是 length 是2的 n 次方;)。” 并且 采用二进制位操作 &,相对于%能够提高运算效率,这就解释了 HashMap 的长度为什么是2的幂次方。 - -**那为什么是两次扰动呢?** - -答:这样就是加大哈希值低位的随机性,使得分布更均匀,从而提高对应数组存储下标位置的随机性&均匀性,最终减少Hash冲突,两次就够了,已经达到了高位低位同时参与运算的目的; - -## 11.HashMap 与 HashTable 有什么区别? - -* 线程安全: HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!); -* 效率: 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它; -* 对Null key 和Null value的支持: HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛NullPointerException。 -* 初始容量大小和每次扩充容量大小的不同: ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。 -* 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。 -* 推荐使用:在 Hashtable 的类注释可以看到,Hashtable 是保留类不建议使用,推荐在单线程环境下使用 HashMap 替代,如果需要多线程使用则用 ConcurrentHashMap 替代。 - -## 12.如何决定使用 HashMap 还是 TreeMap? - -对于在Map中插入、删除和定位元素这类操作,HashMap是最好的选择。然而,假如你需要对一个有序的key集合进行遍历,TreeMap是更好的选择。基于你的collection的大小,也许向HashMap中添加元素会更快,将map换为TreeMap进行有序key的遍历。 - -## 13.HashMap 和 ConcurrentHashMap 的区别 - -* ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的synchronized锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。(JDK1.8之后ConcurrentHashMap启用了一种全新的方式实现,利用CAS算法。) -* HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。 - -## 14.ConcurrentHashMap 和 Hashtable 的区别? - -ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。 - -* 底层数据结构: JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 数组+链表 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的; -* 实现线程安全的方式(重要): ① 在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16倍。) 到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化) 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。 - -**两者的对比图:** - -HashTable: - -![图片](https://uploader.shimo.im/f/16ZFDMx3cpgnZFTd.png!thumbnail) - -JDK1.7的ConcurrentHashMap: - -![图片](https://uploader.shimo.im/f/xvvpaHsr0OQSzQuc.png!thumbnail) - -JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点 Node: 链表节点): - -![图片](https://uploader.shimo.im/f/J6lGBBZIHmQX7A0s.png!thumbnail) - -答:ConcurrentHashMap 结合了 HashMap 和 HashTable 二者的优势。HashMap 没有考虑同步,HashTable 考虑了同步的问题。但是 HashTable 在每次同步执行时都要锁住整个结构。 ConcurrentHashMap 锁的方式是稍微细粒度的。 - -## 15.ConcurrentHashMap 底层具体实现知道吗?实现原理是什么? - -**JDK1.7** - -首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。 - -在JDK1.7中,ConcurrentHashMap采用Segment + HashEntry的方式进行实现,结构如下: - -一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。 - -![图片](https://uploader.shimo.im/f/fWaLOhI4ofsgZsCk.png!thumbnail) - -该类包含两个静态内部类 HashEntry 和 Segment ;前者用来封装映射表的键值对,后者用来充当锁的角色; - -Segment 是一种可重入的锁 ReentrantLock,每个 Segment 守护一个HashEntry 数组里得元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。 - -**JDK1.8** - -在JDK1.8中,放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。 - -结构如下: - -![图片](https://uploader.shimo.im/f/BGWZlGZ1N2QpH0i2.png!thumbnail) - -如果相应位置的Node还没有初始化,则调用CAS插入相应的数据; - -``` -else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { -    if (casTabAt(tab, i, null, new Node(hash, key, value, null))) -        break;                   // no lock when adding to empty bin -} -``` -如果相应位置的Node不为空,且当前该节点不处于移动状态,则对该节点加synchronized锁,如果该节点的hash不小于0,则遍历链表更新节点或插入新节点; -``` -if (fh >= 0) { -    binCount = 1; -    for (Node e = f;; ++binCount) { -        K ek; -        if (e.hash == hash && -            ((ek = e.key) == key || -             (ek != null && key.equals(ek)))) { -            oldVal = e.val; -            if (!onlyIfAbsent) -                e.val = value; -            break; -        } -        Node pred = e; -        if ((e = e.next) == null) { -            pred.next = new Node(hash, key, value, null); -            break; -        } -    } -} -``` -* 如果该节点是TreeBin类型的节点,说明是红黑树结构,则通过putTreeVal方法往红黑树中插入节点;如果binCount不为0,说明put操作对数据产生了影响,如果当前链表的个数达到8个,则通过treeifyBin方法转化为红黑树,如果oldVal不为空,说明是一次更新操作,没有对元素个数产生影响,则直接返回旧值; -* 如果插入的是一个新节点,则执行addCount()方法尝试更新元素个数baseCount; - -# 四.辅助工具类 - -## 1.Array 和 ArrayList 有何区别? - -* Array 可以存储基本数据类型和对象,ArrayList 只能存储对象。 -* Array 是指定固定大小的,而 ArrayList 大小是自动扩展的。 -* Array 内置方法没有 ArrayList 多,比如 addAll、removeAll、iteration 等方法只有 ArrayList 有。 - -对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。 - -## 2.如何实现 Array 和 List 之间的转换? - -* Array 转 List: Arrays. asList(array) ; -* List 转 Array:List 的 toArray() 方法。 - -## 3.comparable 和 comparator的区别? - -* comparable接口实际上是出自java.lang包,它有一个 compareTo(Object obj)方法用来排序 -* comparator接口实际上是出自 java.util 包,它有一个compare(Object obj1, Object obj2)方法用来排序 - -一般我们需要对一个集合使用自定义排序时,我们就要重写compareTo方法或compare方法,当我们需要对某一个集合实现两种排序方式,比如一个song对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写compareTo方法和使用自制的Comparator方法或者以两个Comparator来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的Collections.sort(). - -## 4.Collection 和 Collections 有什么区别? - -* java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。 -* Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。 - -## 5.TreeMap 和 TreeSet 在排序时如何比较元素?Collections 工具类中的 sort()方法如何比较元素? - -TreeSet 要求存放的对象所属的类必须实现 Comparable 接口,该接口提供了比较元素的 compareTo()方法,当插入元素时会回调该方法比较元素的大小。TreeMap 要求存放的键值对映射的键必须实现 Comparable 接口从而根据键对元素进 行排 序。 - -Collections 工具类的 sort 方法有两种重载的形式, - -第一种要求传入的待排序容器中存放的对象比较实现 Comparable 接口以实现元素的比较; - -第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator 接口的子类型(需要重写 compare 方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注入比较元素大小的算法,也是对回调模式的应用(Java 中对函数式编程的支持)。 +Map是一个键值对集合,存储键、值和之间的映射。 Key无序,唯一;value 不要求有序,允许重复。Map From 146e9ce5896abbcaa711eb7636d80357a2d0ee23 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:33:58 +0800 Subject: [PATCH 13/27] =?UTF-8?q?Update=20=E6=B6=88=E6=81=AF=E4=B8=AD?= =?UTF-8?q?=E9=97=B4=E4=BB=B6MQ=E4=B8=8ERabbitMQ=E9=9D=A2=E8=AF=95?= =?UTF-8?q?=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\216RabbitMQ\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266MQ\344\270\216RabbitMQ\351\235\242\350\257\225\351\242\230.md" "b/Interview/\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266MQ\344\270\216RabbitMQ\351\235\242\350\257\225\351\242\230.md" index 585bd15..d83919c 100644 --- "a/Interview/\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266MQ\344\270\216RabbitMQ\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/\346\266\210\346\201\257\344\270\255\351\227\264\344\273\266MQ\344\270\216RabbitMQ\351\235\242\350\257\225\351\242\230.md" @@ -24,6 +24,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 消息中间件MQ与RabbitMQ面试题 ## 1.为什么使用MQ?MQ的优点 From 955cfe8141ee9ad5b4300e4203340ac3a9b0d840 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:34:09 +0800 Subject: [PATCH 14/27] =?UTF-8?q?Update=20=E5=B9=B6=E5=8F=91=E7=BC=96?= =?UTF-8?q?=E7=A8=8B=E9=9D=A2=E8=AF=95=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...6\347\250\213\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/\345\271\266\345\217\221\347\274\226\347\250\213\351\235\242\350\257\225\351\242\230.md" "b/Interview/\345\271\266\345\217\221\347\274\226\347\250\213\351\235\242\350\257\225\351\242\230.md" index f078248..ed0e53d 100644 --- "a/Interview/\345\271\266\345\217\221\347\274\226\347\250\213\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/\345\271\266\345\217\221\347\274\226\347\250\213\351\235\242\350\257\225\351\242\230.md" @@ -145,6 +145,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.基础知识 ## 1.并发编程的优缺点 From 6a1fb889babb082349d4c1383e5eaf3971a0e701 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:34:17 +0800 Subject: [PATCH 15/27] =?UTF-8?q?Update=20ZooKeeper=E9=9D=A2=E8=AF=95?= =?UTF-8?q?=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ZooKeeper\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/ZooKeeper\351\235\242\350\257\225\351\242\230.md" "b/Interview/ZooKeeper\351\235\242\350\257\225\351\242\230.md" index 466ef22..81c5769 100644 --- "a/Interview/ZooKeeper\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/ZooKeeper\351\235\242\350\257\225\351\242\230.md" @@ -35,6 +35,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # ZooKeeper 面试题 ## 1. ZooKeeper 是什么? From a82f3688f1aea3a6199868a9c573ed2b753fe340 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:34:26 +0800 Subject: [PATCH 16/27] =?UTF-8?q?Update=20Tomcat=E9=9D=A2=E8=AF=95?= =?UTF-8?q?=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Tomcat\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/Tomcat\351\235\242\350\257\225\351\242\230.md" "b/Interview/Tomcat\351\235\242\350\257\225\351\242\230.md" index 100203a..1e8e31e 100644 --- "a/Interview/Tomcat\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Tomcat\351\235\242\350\257\225\351\242\230.md" @@ -16,6 +16,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # Tomcat面试题 ## 1.Tomcat是什么? From 15f3a50795c8d811b10b220e0d2784f4d1f7403f Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:34:35 +0800 Subject: [PATCH 17/27] =?UTF-8?q?Update=20Spring=E9=9D=A2=E8=AF=95?= =?UTF-8?q?=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Spring\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/Spring\351\235\242\350\257\225\351\242\230.md" "b/Interview/Spring\351\235\242\350\257\225\351\242\230.md" index eb42eb3..69f77de 100644 --- "a/Interview/Spring\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Spring\351\235\242\350\257\225\351\242\230.md" @@ -85,6 +85,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.Spring概述 ## 1.什么是spring? From 760df0e9bb3bd3190e2009bb4620d828d0e767e3 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:34:43 +0800 Subject: [PATCH 18/27] =?UTF-8?q?Update=20Spring=20MVC=E9=9D=A2=E8=AF=95?= =?UTF-8?q?=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Spring MVC\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/Spring MVC\351\235\242\350\257\225\351\242\230.md" "b/Interview/Spring MVC\351\235\242\350\257\225\351\242\230.md" index b615754..3560856 100644 --- "a/Interview/Spring MVC\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Spring MVC\351\235\242\350\257\225\351\242\230.md" @@ -38,6 +38,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.概述 ## 1.什么是Spring MVC?简单介绍下你对Spring MVC的理解? From 6d06102863a6854219ed6342db3fe6a01c5a6633 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:34:54 +0800 Subject: [PATCH 19/27] =?UTF-8?q?Update=20Spring=20Cloud=E9=9D=A2=E8=AF=95?= =?UTF-8?q?=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Spring Cloud\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/Spring Cloud\351\235\242\350\257\225\351\242\230.md" "b/Interview/Spring Cloud\351\235\242\350\257\225\351\242\230.md" index db33ce7..a530100 100644 --- "a/Interview/Spring Cloud\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Spring Cloud\351\235\242\350\257\225\351\242\230.md" @@ -37,6 +37,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.为什么需要学习Spring Cloud 不论是商业应用还是用户应用,在业务初期都很简单,我们通常会把它实现为单体结构的应用。但是,随着业务逐渐发展,产品思想会变得越来越复杂,单体结构的应用也会越来越复杂。这就会给应用带来如下的几个问题: From 088a7a9110fb8e2d7c95021ca70becbc40434aa4 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:35:01 +0800 Subject: [PATCH 20/27] =?UTF-8?q?Update=20Spring=20Boot=E9=9D=A2=E8=AF=95?= =?UTF-8?q?=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Spring Boot\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/Spring Boot\351\235\242\350\257\225\351\242\230.md" "b/Interview/Spring Boot\351\235\242\350\257\225\351\242\230.md" index 5fa3498..47da0ba 100644 --- "a/Interview/Spring Boot\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Spring Boot\351\235\242\350\257\225\351\242\230.md" @@ -47,6 +47,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.概述 ## 1.什么是 Spring Boot? From 17c6b5e0b0df9c31a1dddf6640d8ac63988320de Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:35:11 +0800 Subject: [PATCH 21/27] =?UTF-8?q?Update=20Redis=E9=9D=A2=E8=AF=95=E9=A2=98?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "Interview/Redis\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/Redis\351\235\242\350\257\225\351\242\230.md" "b/Interview/Redis\351\235\242\350\257\225\351\242\230.md" index 03606d6..0b2bf91 100644 --- "a/Interview/Redis\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Redis\351\235\242\350\257\225\351\242\230.md" @@ -85,6 +85,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.概述 ## 1.什么是Redis From 2d9615478cf22804e606e83d4fa4b9ccde669ba1 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:35:19 +0800 Subject: [PATCH 22/27] =?UTF-8?q?Update=20Netty=E9=9D=A2=E8=AF=95=E9=A2=98?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "Interview/Netty\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/Netty\351\235\242\350\257\225\351\242\230.md" "b/Interview/Netty\351\235\242\350\257\225\351\242\230.md" index 570b5e1..f7cdd1b 100644 --- "a/Interview/Netty\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Netty\351\235\242\350\257\225\351\242\230.md" @@ -34,6 +34,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # Netty面试题 ## 1.Netty 是什么? From 0ea6e91ba7965330949892c1dbfdb90a2e4084f8 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:35:31 +0800 Subject: [PATCH 23/27] =?UTF-8?q?Update=20MySQL=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E9=9D=A2=E8=AF=95=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...6\345\272\223\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/MySQL\346\225\260\346\215\256\345\272\223\351\235\242\350\257\225\351\242\230.md" "b/Interview/MySQL\346\225\260\346\215\256\345\272\223\351\235\242\350\257\225\351\242\230.md" index 2a18cf3..65c213a 100644 --- "a/Interview/MySQL\346\225\260\346\215\256\345\272\223\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/MySQL\346\225\260\346\215\256\345\272\223\351\235\242\350\257\225\351\242\230.md" @@ -115,6 +115,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.数据库基础知识 ## 1.为什么要使用数据库 From 3d50631e04b048d12a4260544c3faf97be67c8ac Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:35:42 +0800 Subject: [PATCH 24/27] =?UTF-8?q?Update=20MyBatis=E9=9D=A2=E8=AF=95?= =?UTF-8?q?=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MyBatis\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/MyBatis\351\235\242\350\257\225\351\242\230.md" "b/Interview/MyBatis\351\235\242\350\257\225\351\242\230.md" index 006fb6d..8fca7c3 100644 --- "a/Interview/MyBatis\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/MyBatis\351\235\242\350\257\225\351\242\230.md" @@ -49,6 +49,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.MyBatis简介 ## 1.MyBatis是什么? From 62b11f5f13e216147eee2f2ba54e7c1a5ca51b5b Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Tue, 21 Jul 2020 21:35:52 +0800 Subject: [PATCH 25/27] =?UTF-8?q?Update=20Linux=E9=9D=A2=E8=AF=95=E9=A2=98?= =?UTF-8?q?.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "Interview/Linux\351\235\242\350\257\225\351\242\230.md" | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git "a/Interview/Linux\351\235\242\350\257\225\351\242\230.md" "b/Interview/Linux\351\235\242\350\257\225\351\242\230.md" index 2e543d7..eaffe72 100644 --- "a/Interview/Linux\351\235\242\350\257\225\351\242\230.md" +++ "b/Interview/Linux\351\235\242\350\257\225\351\242\230.md" @@ -101,6 +101,15 @@ *** +# 说明 + +>- **本文所涉及的所有面试题皆收自一线互联网大厂** +>- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **视频资料请关注微信公众号"Java架构筑基"免费获取** + +*** + # 一.Linux 概述 ## 1.什么是Linux From d0e047ae0dd5c3326db48dd33e55422a2c553786 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Sat, 12 Dec 2020 13:23:09 +0800 Subject: [PATCH 26/27] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebe5d79..82a6bd7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ **说明**:**本仓库所有Java面试题皆来自一线互联网大场面试真题** -**Github用户如果访问速度缓慢的话,可以转移[资源分享群](https://jq.qq.com/?_wv=1027&k=5Op2CWT)到下载本地PDF文件,或者在线阅读**。 +**Github用户如果访问速度缓慢的话,可以转移[资源分享群](https://jq.qq.com/?_wv=1027&k=1FAag3Hz)到下载本地PDF文件,或者在线阅读**。 **视频资料请关注微信公众号【Java架构筑基】免费获取** From f45457f44283fd214932f37e97181915cb05d3f9 Mon Sep 17 00:00:00 2001 From: Java-Super-Air <65396037+Java-Super-Air@users.noreply.github.com> Date: Sat, 12 Dec 2020 13:29:06 +0800 Subject: [PATCH 27/27] =?UTF-8?q?Update=20and=20rename=20Interview/Java?= =?UTF-8?q?=E5=9F=BA=E7=A1=80=E7=9F=A5=E8=AF=86=E9=9D=A2=E8=AF=95=E9=A2=98?= =?UTF-8?q?.md=20to=20=E9=9D=A2=E8=AF=95/Java=E5=9F=BA=E7=A1=80=E7=9F=A5?= =?UTF-8?q?=E8=AF=86=E9=9D=A2=E8=AF=95=E9=A2=98.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename "Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" => "\351\235\242\350\257\225/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" (99%) diff --git "a/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" "b/\351\235\242\350\257\225/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" similarity index 99% rename from "Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" rename to "\351\235\242\350\257\225/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" index 0ab6ab7..8bb45b9 100644 --- "a/Interview/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" +++ "b/\351\235\242\350\257\225/Java\345\237\272\347\241\200\347\237\245\350\257\206\351\235\242\350\257\225\351\242\230.md" @@ -124,7 +124,7 @@ >- **本文所涉及的所有面试题皆收自一线互联网大厂** >- **GitHub用户如果访问过慢的话,可以移步至【资源分享群】下载本地文件** ->- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=5Op2CWT)** +>- **资源分享群链接:[点击加入群聊](https://jq.qq.com/?_wv=1027&k=1FAag3Hz)** >- **视频资料请关注微信公众号"Java架构筑基"免费获取** ***