互联网就业技术指南全栈~~技术栈web知识库

GO语言面试系列:(五)Gopher 全栈面试参考

2018-10-30  本文已影响117人  Chole121

先前准备 Golang 面试用的笔记,仅供参考。

前言

本文结构:

1.  └──计算机基础
2.       ├── 计算机网络
3.       ├── 数据结构
4.       ├── 算法
5.       ├── 操作系统
6.       ├── 数据库
7.       └── OOP 与设计模式
8.  └── Golang 面试题

参考资料:笔试面试知识整理Golang 面试题解析Go面试题答案与解析

这篇文章的内容力大多一笔带过,细节上我参考的书籍有:

计算机网络

HTTP 协议

请求报文

1.  <method> <request-URL> <version>    # 状态行       # 状态行
2.  <headers>                           # 请求头       # 响应头
3.  <entity-body>                       # 请求实体     # 响应体

HTTP 协议不限制 GET URL 长度,但浏览器限制字符数(Chrome 8K)& 也不限制 POST 资源大小

响应报文

状态行:协议版本、状态码、状态描述

1: Informational

2:Success

3:Redirection

4:Client Error

5:Server Error

Conditional Get (条件 GET)

用户访问过该网页,再次访问。

GET 头部带有 If-Modified-Since:,若响应 304,则直接使用浏览器的缓存。否则返回正常实体

持久连接

HTTP 1.0 中:客户端请求头添加 Connection: Keep-alive,服务端同样在响应头中添加,保持连接

HTTP 1.1 中:默认所有连接都是长连接,添加 Connection: Close 才关闭,设置

HTTP Pipelining 管线化

批量提交 HTTP 请求,不排队等待响应才发送下一个请求:请求1 -> 响应1 -> 请求2 -> 响应2 -> 请求3 -> 响应3 变为 请求1 -> 请求2 -> 请求3 -> 响应1 -> 响应2 -> 响应3

HTTP1.0 中发下一个请求前,必须等待响应。

会话追踪

HTTP 请求是无状态的协议,不会保存客户端信息,实现:

cookie

session

token(重写 URL)

HTTP 安全

CSRF 跨站请求伪造:攻击者知道所有参数、构造合法请求

伪造成其他用户,发起请求:如拼接恶意链接给其他用户点击。防范:

XSS 跨站脚本攻击:在客户端不知情的情况下运行 JS 脚本。防范:

TCP 协议

特点

应用:HTTP、FTP、SMTP、SSH(数据准确性要求高)

优点:稳定可靠

缺点:慢、效率低、占资源多、易攻击(DDOS)

三次握手 Three-way Handshake

客户端执行 connect() 主动连接:

1.  A:听得到吗?
2.  B:听得到,你能听到我吗?
3.  A:可以,我们可以交流了233
4.
5.  前两次:保证 B 能接收到 A 的信息,并作出正确响应
6.  第三次:为了防止 A 的延迟的连接请求,B 一直在等待 A 的数据而浪费资源

理解:传输的信道不是绝对可靠的。为了不可靠的信道上可靠的传输信息,最少要进行三次通信。

TCP Flags 标志位:

1. SYN:synchronous 建立连接
2. ACK:acknowledgement 确认连接
3. PSH:push 推送
4. FIN:finish 释放连接(请求方数据已发送完毕)
5. RST: reset 重置(复位请求)
6. URG: urgent 紧急

1.SYN = 1 & ACK = 0:请求连接报文
2.SYN = 1 & ACK = 1:同意建立连接的响应报文

过程:

四次挥手 Four-way handshake

服务端和客户端均可主动断开连接:服务端、客户端均需确认对方无数据再发送

注意: 中间直接发送 ACK + FIN,则主动方会直接跳过 FIN_WAIT_2 状态

TCP Keep-Alive 机制(心跳包)

数据交互完毕后,一方主动释放连接。但出现意外时,TCP 连接不能及时释放,导致要维护很多半打开的连接。

实现:定时(半秒等)给对方发一个探测包,若收到 ACK 则认为连接存活,若 重试一定次数 都没收到回应则直接丢弃该 TCP 连接。

在我的 B 站直播间数据爬虫 抓取时,就需要每隔半分钟给 B 站的弹幕服务器发一个心跳包,否则连接会在一分钟后断开。

UDP 协议

特点

应用:DNS、流媒体(速度要求 > 质量要求)

优点:快、比 TCP 稍安全

缺点:不可靠、不稳定

TCP UDP
报文 面向字节流 面向报文
双工性 全双工 一对一、一对多、多对一、多对多
流量控制 滑动窗口
拥塞控制 快重传、快恢复
传输速度

IP 协议

地址分类

IPv4 用点分十进制表示,IP 地址 = 网络地址 + 主机地址(层次)

全零 0.0.0.0 :本主机、全一:255.255.255.255 当前子网的广播地址

A 类:8 – 1(0) = 7 位网络号

B 类:16 – 2(10) = 14 位网络号

C 类:24 – 3(110) = 21 位网络号

子网掩码

用子网掩码划分一个 IP 的网络地址和主机地址。

IP & 子网掩码 = 网络地址

192.168.1.1/24192.168.1.1/255.255.255.0 的简写,前 24 位为网络号

子网划分:将大的整体网络划为小的子网络

Socket 编程

socket:三元组(IP 地址、协议、端口号)标识网络中唯一的进程,在 unix 中是文件

socket 使用了门面 Facade 模式:外部与内部的通信必须经过 facade

socket 隐藏了 TCP/IP 细节,开放交互接口。有:

1. socket() // 创建套接字
2. bind()   // 将套接字绑定到服务器地址上
3. listen() // 等待连接请求
4. accept() // 允许连接
5. read()   // 读数据
6. write()  // 写数据
7. close()  // 关闭连接

搭建简单的 Server:

数据结构

1.└── 数据结构
2.   ├── 数组
3.   ├── 链表
4.   ├── 栈
5.   ├── 队列
6.   ├── 哈希表
7.   ├── 二叉树
8.   ├── 堆
9.   └── 字典树 trie

参考资料:常见数据结构及其多种实现的可视化

数组 Array

元素在内存中连续存放。每个元素占用内存相同,可通过下标算出元素位置快速访问

优点:访问快 O(1)

缺点:增加、删除元素需要移动大量元素,慢 O(N)

场景:快速访问元素,很少插入和删除元素

数组 链表
内存分配、元素存储位置 静态分配内存(栈,系统自动分配) 动态分配内存(堆,申请和管理麻烦)
分配方式 系统自动分配、速度快 自己申请和管理、new 慢
大小 编译时确定,具体值 是不连续的内存区域

链表 Linked List

元素在内存中不是连续存放。元素间通过指向指针联系在一起,访问元素必须从第一个元素开始遍历查找

优点:插入、删除元素只需改变指针,快 O(1)

缺点:访问慢 O(N)

场景:经常插入、删除元素

分类

时间复杂度

栈 Stack

元素遵循后进先出(LIFO)原则。元素仅在表尾(栈顶)进行插入(入栈 push)、删除(出栈 pop

实现

时间复杂度

队列

元素遵循先进先出(FIFO)原则。元素在一端插入(进队列 enqueue)、另一端删除(出队列 dequeue)

出队列元素是在队列中存在时间最长的元素。

实现

时间复杂度

哈希表

散列表:根据 key 键值直接访问数据的内存地址

hash(data) = key:hash() 是一类算法,处理任意长度的 data,得到定长的 key 值,过程不可逆。

若 data 是数据集,则 key 也是数据集,将 keys 与原始数据一一映射就得到哈希表,即 M[key] = data

二叉树

遍历方式(栈实现较好)

二叉搜索树

性质:左子节点均小于根节点、右子节点均大于根节点

复杂度

算法

└── 算法
     ├── 排序
     ├── 查找
     └── 字符串算法

参考资料:常见算法的过程可视化常见算法的 Go 实现

排序

数据大小 n,好的复杂度 O(logN),坏的 O(N2)

应用场景

交换排序一:冒泡排序

过程

优化

分析

情况 复杂度
最佳情况:已有序,全遍历 1 次 O(N)
最坏情况:反序,全遍历 N 次 O(N^2)
平均情况 O(N^2)

交换排序二:快速排序

过程

分析

情况 复杂度
最佳 O(logN)
最坏(数组反序、数组元素全部相同) O(N^2)
平均 O(logN)

选择排序一:简单选择排序

过程

分析

情况 复杂度
最佳、最差、平均 O(N^2)

选择排序二:堆排序

过程

分析

情况 复杂度
最佳、最差、平均 O(logN)

插入排序一:直接插入排序

过程

分析

情况 复杂度
最佳(数组升序) O(N)
最坏(数组反序) O(N^2)
平均 O(N^2)

插入排序二:希尔排序

过程:选择一个固定(动态)的步长,对步长内的元素进行直接插入排序

分析

情况 复杂度
最佳、最坏、平均(都要步长分割、遍历) O(NlogN)

归并排序

过程

分析

情况 复杂度
最佳 O(N)
最坏 O(NlogN)
平均 O(NlogN)

基数排序(非比较)

过程

分析

情况 复杂度
最佳、最坏、平均 O(N * b)

查找

无序查找:顺序查找

有序查找(无序序列需要提前排序)

二分查找(折半查找)

插值查找

二叉查找树

平衡二叉查找树(AVL 树)

操作系统

体系结构

1.   [+1] = [00000001]原 = [00000001]反 = [00000001]补
2.   [-1] = [10000001]原 = [11111110]反 = [11111111]补
0x12345678 的存储:

Big Endian
低地址                                            高地址
---------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     12     |      34    |     56      |     78    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian
低地址                                            高地址
---------------------------------------------------->
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|     78     |      56    |     34      |     12    |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

基础

操作系统功能:文件管理、存储管理、输入输出管理、作业管理、进程管理

中断

系统调用

中断和系统调用的关系:程序申请核心态时,将产生一个软件中断,系统将处理

并发多任务

多道程序:程序运行, CPU 空闲时(等待 IO),此时 CPU 去运行其他程序

分时系统:改进后的多道程序,程序运行一段时间后会主动让出 CPU

多任务系统:操作系统从底层接管所有硬件资源,程序以进程运行,程序运行超时会被强制暂停

进程

运行中的程序

4 个地址空间

3 种状态

4 种进程间通信

死锁:多个进程因循环等待资源而都无法执行

线程

轻量级进程

协程

IO 多路复用

内核发现某个进程指定的 IO 准备读取,就会通知该进程,如客户端处理多个描述符时,大大减小创建和维护的开销。

并发与并行

数据库

事务:一系列 SQL 集合

ACID 特性

4 种隔离级别

3 种实现隔离的锁

索引

优点

缺点

场景

OOP

三个基本特征:封装、继承、多态

封装

继承:使用现有类的所有功能。

多态:子类覆盖父类的同名方法

​​

Golang 面试题

问答类

1. 在 Go 中如何使用多行字符串?

使用反引号 “ 来包含多行字串,或使用 + 来连接多行字符串(注意换行会包含\n,缩进会包含 \t,空格没有转义符):

1.func main() {
2.   str1 := `
3. line1
4.    line2
5.`
6.    str2 := "\n line1\n\t" +
7.      "line2\n"
8.    fmt.Println(str1 == str2) // true
9.}

2. 如何获取命令行的参数?

有两种方法:

使用 os 库,如:

1.  func main() {
2.     args := os.Args
3.     if args == nil  { // 校验参数并输出提示信息
4.        return
5.    }
6.    fmt.Printf("%T\n", args)
7.    fmt.Printf("%v\n", args)
8. }

可以看出 os.Args 接收到的参数是 string slice,元素分别是运行的程序名、多个参数值:

使用 flag 库,步骤:

func main() {
    name := flag.String("name", "", "Your name")
    var age int
    flag.IntVar(&age, "age", -1, "Your age")

    flag.Parse()

    println("name", *name)
    println("age", age)
}

注意上边获取参数值的两种方式,使用时也有所不同:

func Int(name string, value string, usage string) *string // 返回地址
func IntVar(p *int, name string, value int, usage string) // 修改第一个参数值

3. 如何在不输出的情况下格式化字符串?

使用 func Sprintf(format string, a ...interface{}) string 即可,常用在手动组合 SQL 语句上:

func main() {
    fmt.Println(formatSQL(20))
}

func formatSQL(id int) string {
    return fmt.Sprintf("SELECT * FROM users WHERE id=%d", id)
}

4. 如何交换两个变量的值?

直接使用元组(tuple)赋值即可:

a, b = b, a

注意元组赋值是对应有序赋值的:

1. a, b, c = b, c, a // 交换三个变量的值
2.
3. a := 1
4. b := 2
5. a, b, a = b, a, b // a = 2, b = 1

5. 如何复制 slice、map 和 interface 的值?

slice:

func main() {
    names := []string{"Tom", "Jerry"}
    nums := []string{"one", "two", "three"}
    pNames := names   // 确认 names 被更新 

    // names = nums   // 直接赋值
      
    // fmt.Println(copy(names, nums))   // 使用 copy
    fmt.Println(names, nums, pNames)
}

map:

最简单的方法,遍历所有 key:

func main() {
    src := map[string]bool{"key1": false, "key2": true}
    dst := make(map[string]bool)

    for key, value := range src { // 遍历所有 key
        dst[key] = value
    }
    fmt.Println(dst)
}

interface:

Go 中没有内建的函数来直接拷贝 interface 的值,也不能直接赋值。如 2 个 struct 的字段完全一致,可以使用强制类型转换或反射来赋值。

参考:关于结构体复制问题Copying Interface Values In Go

6. 下边两种 slice 的声明有何不同?哪种更好?

var nums []int
nums := []int{}

第一种如果不使用 nums,就不会为其分配内存,更好(不使用编译也不会通过)。

写出程序运行输出的内容

1. 考察多个 defer 与 panic 的执行顺序

func main() {
    deferCall()
}

func deferCall() {
    defer func() { fmt.Println("打印前") }()
    defer func() { fmt.Println("打印中") }()
    defer func() { fmt.Println("打印后") }()

    panic("触发异常")
}

defer 可以类比为析构函数,多个 defer 本身的执行是栈 LIFO 先进后出的顺序,代码抛出的 panic 如果在所有 defer 中都不使用 recover 恢复,则直接退出程序。

如果手动使用 os.Exit() 退出,则 defer 不执行。

2. 考察 defer 与 return 的执行顺序

func main() {
    fmt.Println(double1(5))
    fmt.Println(double1(6))
    fmt.Println()
    fmt.Println(double2(5))
    fmt.Println(double2(6))
}

// 匿名返回
// 加倍参数,若结果超过 10 则还原
func double1(v1 int) int {
    var v2 int
    defer func() {
        if v2 > 10 {
            v2 = v1 // v2 不会被修改
        }
    }()

    v2 = v1 * 2
    return v2 
}

// 有名返回
func double2(v1 int)(v2 int) {
    // v2 与函数一起被声明,在 defer 中能被修改
    defer func() {
        if v2 > 10 {
          v2 = v1 // v2 被修改
        } 
    }() 

    v2 = v1 * 2
    return
}

注意 return var 会分为三步执行:

return 语句为 var 赋值

检查是否存在 defer 语句:逆序执行多条 defer,有名返回函数可能会再次修改 var

真正返回 var 到调用处

3. 考察 goroutine 的传值方式

func main() {
    runtime.GOMAXPROCS(1) // 强制使多个 goroutine 串行执行
    wg := sync.WaitGroup{}
    wg.Add(10)

    for i := 0; i < 5; i++ {
        go func() {
            fmt.Println("i: ", i)
            wg.Done()
        }()
        // time.Sleep(1 * time.Second)  // 此时将顺序输出 1 2 3 4 5 
    }

  for i := 0; i < 5; i++ {
      go func(i int) {
          fmt.Println("i: ", i)
          wg.Done()
      }(i)
  }
  wg.Wait()
}

第一个 for 循环:以极快的速度分配完 5 个 goroutine,此时 i 的值为 5,gouroutine 得到的 i 都是 5

第二个 for 循环:每次都会将 i 的值拷贝一份传给 goroutine,得到的 i 不同,输出不同

4. 考察 defer 参数的计算时机

1. func main() {
2.     a := 1
3.     b := 2
4.     defer add("A", a, add("B", a, b))
5.     a = 0
6.     defer add("C", a, add("D", a, b))
7.     b = 1
8. }
9.
10.
11. func add(desc string, a, b int) int {
12.     sum := a + b
13.     fmt.Println(desc, a, b, sum)
14.     return sum
15. }

defer 语句会计算好 func 的参数,再放入执行栈中。

注意第 7 行:四个 defer func 的参数此时已是确定值,不再对 defer 中的 b 造成影响。

5. 考察 Go 的组合

1. type People struct{}
2. 
3. func (p *People) ShowA() {
4.     fmt.Println("people showA")
5.     p.ShowB()
6. }
7. func (p *People) ShowB() {
8.     fmt.Println("people showB")
9. }
10.
11.
12. type Teacher struct {
13.     People
14. }
15.
16. func (t *Teacher) ShowB() {
17.    fmt.Println("teacher showB")
18. }
19.
20. func main() {
21.    t := Teacher{}
22.     t.ShowB()
23.     t.ShowA()
24. }

第 13 行: Teacher 通过嵌入 People 来获取了 ShowA()showB()

第 16 行:Teacher 实现并覆盖了 showB()

第 24 行:调用未覆盖的 showA(),因为它的 receiver 依旧是 People,相当于 People 调用

来源:https://wuyin.io/2018/03/16/golang-interviews/

添加小编微信:grey0801,欢迎交流!

上一篇 下一篇

猜你喜欢

热点阅读