> 文章列表 > go+vue——go入门

go+vue——go入门

go+vue——go入门

go+vue

  • 技术选择
    • 入坑理由
      • 需要搭建前后端,Java 0 基础 ,环境容易出现问题;GO上手快,问题少
      • 推荐:【七米】
          • 代码
          • 博客
  • 搭建Go语言开发环境
    • 下载 并 安装
    • 检查是否安装好?
    • GOPROXY 非常重要(帮你下载国外、GitHub代码)
    • VScode
      • 插件
        • chinese
        • go
    • 新建项目
      • 错误提示,直接点【install】
      • go mod init
      • 编写程序 hello world
      • 编译 go build
      • 执行 .\\xxx.exe
      • 【终端】【选择默认配置文件】
      • 终端 之间 切换
      • 跨平台编译
      • go mod tidy 帮你分析 你的依赖 是否 都在 .mod 中 require
        • 执行 go mod tidy , 下载第三方包
        • 并 帮你 引入 第三方包
        • 需要先 保存一下 Ctrl + S , 执行 go mod tidy 才有效
        • 下载第三方库
  • Go语言基础之变量和常量
    • 变量(同C++)
    • 常量 iota (只能在常量的表达式中使用)
  • 基本数据类型
    • 整数
      • 进制
    • 浮点数
    • 布尔 bool
    • 字符串 string
    • 转义字符
    • 多行字符串
    • 字符串的常用操作
    • byte和rune类型
      • for range
  • 运算符
    • 算数
    • 自增 自减
    • 关系
    • 逻辑
    • 赋值运算符
  • Go语言基础之流程控制
    • if else
    • for
      • break
      • continue
      • for range
    • switch case
  • array 数组
    • 推导、索引
    • 遍历 [i]
    • for index, vallue := range cityArray {
    • 二维数组
      • 推导:只能推一位
      • 数组是值类型:(不同于C++)
  • slice 切片 :又称动态数组, 对底层数组的封装
    • 初始化: []中什么都不写,就是 定义 切片 的语法
      • 数组 初始化 切片
      • 切片 初始化 切片
      • make(类型, 初始元素个数, 容量 | 默认-同初始元素个数)
      • len cap
      • 内存图
    • 操作
      • 零值: nil
      • 比较 : 通过 len(切片)==0 来判断是不是空,不能通过==nil
      • 赋值拷贝(浅拷贝)
      • 遍历: 索引、for range ; for i,vk := range xxlist ;
      • 添加元素
        • 错误方式:没有初始化,就 [i]=xxx
      • NewSlice = append(xxx) 必须返回,因为可能是扩容之后的 【新切片】 %v d p
      • append 可以一次增加多个元素
      • copy
      • 删除
      • 练习:空串
      • 练习:sort.Ints ( 切片 )
  • map 映射
    • 初始化
      • map[string] int ; make ( map[string] int , 容量 可选 )
      • Println( a )
      • Printf(" %#v ", a)
      • 初始化 赋值
      • 错误方式:没有初始化, 就 c[100] = 200
    • 操作
      • 查询
        • 没有查到:value 返回 零值: 0、空串、nil
        • 查到后:value 返回 key对应的值value
      • 遍历 for range ; for k,v := range xxmap ; map 是无序hash
      • 删除 delete(scoreMap, "key name")
      • 按序 遍历
        • 无序
        • 顺序: sort.Strings( key_Slice ) 切片排序+遍历map[有序切片]
    • 复杂:slice+map
      • slice里map:两层初始化工作
      • map value 是 slice:两层初始化工作
      • 统计单词个数
  • 函数
    • 返回
    • 参数
      • 可变参数: 切片
      • 可变参数 在 固定参数 后
      • 没有默认参数
      • 参数 类型简写
    • 返回值 多个
      • 参数类型简写
    • defer 延迟执行 : 处理: 资源的释放、文件关闭、解锁、记录时间
    • 作用域
      • 全局变量
      • 局部变量 : 覆盖 全局变量, 从内 往外 找
      • for 语句块 局部变量i
    • 一等公民 : 三可做 : 变量、参数、返回值
      • 函数 作为 变量
      • 函数 作为 参数
    • 匿名函数
      • 定义变量
      • 直接调用
    • 闭包: 返回的函数 是否 包含 外层变量的 引用
      • 函数作为返回值
      • 最基本的 闭包: 返回 内部 匿名 函数 anon ,anon 完成对 外层 变量 引用(参数)
      • 升级
        • 传入资源
      • 闭包进阶示例2:
      • 闭包进阶示例3:
    • 内置函数
      • panic
      • defer recover
  • 指针
    • 指针=取地址&, %v=%p
    • 取值=*指针
    • 函数 传 指针
    • 未初始化, 引发 panic
    • new : 整型、浮点型、bool、字符串、数组
    • make : slice、map、channel
  • 结构体
    • 自定义 类型 %T
    • 别名
    • 结构体
      • 结构体 实例化
        • 匿名结构体
        • 结构体 指针 new
        • 可以直接写 p. 不用 ( *p ).
      • 结构体初始化
        • 取结构体的地址实例化 默认 初始化
        • 取结构体的地址实例化 初始化
        • 键值对 初始化 , 最后的“ ,”必须要写
        • 列表 初始化 , 最后的“ ,”必须要写
    • 构造函数
      • struct 是 值类型
    • 方法
      • 标识符 首字母大写
      • 构造函数
      • 定义方法
        • 值类型 的 接收者
        • 指针类型 的 接收者
        • 对比
      • 任意类型添加方法
        • 不可以,不是自己 的 包
        • 可以,起别名(继承)
    • 结构体 嵌套
      • 匿名字段
        • 按照 类型 访问
        • 不能重复 类型
      • 嵌套 结构体
        • 嵌套 结构体
        • 匿名 嵌套 结构体
      • 嵌套 结构体 字段冲突
        • 指定 结构体 名字
      • 结构体 继承 (用 嵌套 模拟, 更准确的说 应该叫 组合)
    • 结构体 字段 可见性
    • JSON
      • 序列化、 反序列化
      • 如果 首字母小写, JSON 访问不了, 字段缺失
        • 序列化,缺失
        • 反序列化,字段为 零值: 空串 、 0
      • 结构体标签(Tag)
        • 序列化 结果
        • json、db、xml
    • 注意: 结构体内 slice、 map 使用 make(类型, len(变量名))
    • 学生信息管理系统
      • main.go
      • student.go
  • 包 packet
    • 包名 定义
    • 包的声明 === 文件夹 的名字
    • 导入 包 $GOPATH/src 后面写起
    • 给包 起别名
    • 匿名导入包 , 只调用 init() 函数
    • func init() {} 优先于 main 函数 , 多用于 初始化: 日志、加载配置文件
      • 全局声明 --> init() --> main()
      • init() 执行顺序
    • 同一个包 , 多个文件 , 可以相互调用
  • 接口 : 是一个类型, 一个抽象的类型
    • struct 必须实现 接口 interface 的函数
      • 不实现 interface 的 函数, 就报错
    • 多态 : 类似于C++虚函数+父指针
    • 指针 给 接口
      • 使用 【值接受者】 实现接口
      • 使用 【指针接受者】 实现接口
    • 一个 struct 类型, 可以 实现 多个接口 (例子如下) ; 多个类型 也可实现 一个接口 (例子如上)
    • 接口 的嵌套
    • 空接口 : 作为 : 函数参数 ; map 扩展 值value
      • Println ( a ... interface{} )
        • 变长的函数参数 func f1(parms ...int){
        • 打散Slice , arr1 = append(arr1,arr2...)
      • 底层实现
      • 类型 断言 x.( string )
        • Switch 猜猜猜 (为什么,不先打印出来,先判断是true、"",然后再猜?)
  • reflect 反射 (暂时跳过 ----------------------------------------- -----------------------------------------)
  • 结构体 反射 (暂时跳过 ----------------------------------------- -----------------------------------------)
  • 并发编程
    • goroutine
      • go关键字
      • 等待
        • time.Sleep(time.Second)
        • wg.Wait()
      • 匿名函数
        • 还是一个闭包、 包含外部函数变量 i
        • 传参
      • 单核(一个先执行完) 、 多核(混在一起)
    • channel
      • make (chan int, 10)
        • 无缓冲区 ,阻塞 死锁 ,同步通道: 当面交付 数据, 快递员交到你的手上
        • 带缓冲区 , 异步通道: 驿站
      • 长度、 容量、 len cap
      • ch1 ch2 同步三个
        • 两种 从通道 取值 方式
      • 单向通道
        • chan<-
        • <-chan
        • 报错:
      • chan 异常 总结
      • worker pool 线程池 (暂时跳过 ----------------------------------------- -----------------------------------------)
  • 并发同步 与 锁 (暂时跳过 ----------------------------------------- -----------------------------------------)
  • 网络 编程 (暂时跳过 ----------------------------------------- -----------------------------------------)
  • 单元测试 (暂时跳过 ----------------------------------------- -----------------------------------------)

技术选择

入坑理由

需要搭建前后端,Java 0 基础 ,环境容易出现问题;GO上手快,问题少

go+vue——go入门
go+vue——go入门
看到有人推【七米】,所以。。。
go+vue——go入门

推荐:【七米】

代码

https://github.com/Q1mi/go_tutorial

博客

https://liwenzhou.com/
go+vue——go入门
https://www.liwenzhou.com/posts/Go/golang-menu/
go+vue——go入门
https://www.liwenzhou.com/posts/Go/install/
go+vue——go入门

搭建Go语言开发环境

下载 并 安装

下载地址
Go官网下载地址:https://golang.org/dl/
Go官方镜像站(推荐):https://golang.google.cn/dl/

检查是否安装好?

go+vue——go入门

GOPROXY 非常重要(帮你下载国外、GitHub代码)

复制、粘贴到cmd命令行、回车。
之后什么都不用做,cmd命令行 也没有输出。

go env -w GOPROXY=https://goproxy.cn,direct

go+vue——go入门

VScode

插件

chinese

go+vue——go入门
第一个【简体】,安装完后,【右下角】【重启】
go+vue——go入门

go

go+vue——go入门

新建项目

错误提示,直接点【install】

非常重要!!! 如果此时VS Code右下角弹出提示让你安装插件,务必点 install all 进行安装。

这一步需要先执行完上面提到的go env -w GOPROXY=https://goproxy.cn,direct命令配置好GOPROXY。
go+vue——go入门
go+vue——go入门
go+vue——go入门

go mod init

使用go module模式新建项目时,我们需要通过go mod init 项目名命令对项目进行初始化,该命令会在项目根目录下生成go.mod文件。例如,我们使用hello作为我们第一个Go项目的名称,执行如下命令。

go mod init hellogo mod init github.com/Q1mi/hello
后面跟, 别人 导入 你的 【包】 的名字。

go+vue——go入门
require 表示引用其他人的库
go+vue——go入门

编写程序 hello world

go+vue——go入门

编译 go build

go buildgo build -o  Out_Name.exe

生成可执行文件 hello.exe
go+vue——go入门

执行 .\\xxx.exe

只有一个go文件时,临时编译并执行,不生产exe文件
go run main.gowindows   ——  powershell.\\xxx.exewindows   ——  cmdxxx.exelinux、mac
./xxx.exe  

看清楚【终端】是 powershell 还是 cmd
go+vue——go入门

go+vue——go入门

go+vue——go入门

【终端】【选择默认配置文件】

go+vue——go入门
go+vue——go入门

终端 之间 切换

go+vue——go入门

跨平台编译

cmd 中 设置环境变量
go+vue——go入门
不能在 windows 执行, 只能在 Linux 上执行
go+vue——go入门

go mod tidy 帮你分析 你的依赖 是否 都在 .mod 中 require

cmd中执行go mod tidy

执行 go mod tidy , 下载第三方包

先 import , 并在代码中调用
再执行,go mod tidy

go+vue——go入门
go+vue——go入门

并 帮你 引入 第三方包

go+vue——go入门

需要先 保存一下 Ctrl + S , 执行 go mod tidy 才有效

go+vue——go入门

下载第三方库

go get github.com/q1mi/hello

go+vue——go入门

Go语言基础之变量和常量

https://www.liwenzhou.com/posts/Go/var-and-const/

变量(同C++)

注意事项:

函数外的每个语句都必须以关键字开始(var、const、func等)
:=不能使用在函数外。
_多用于占位,表示忽略值。

常量 iota (只能在常量的表达式中使用)

const (n1 = iota //0n2        //1n3        //2n4        //3)const (n1 = iota //0n2        //1_n4        //3)const (n1 = iota //0n2 = 100  //100n3 = iota //2n4        //3)const n5 = iota //0iotaconst关键字出现时将被重置为0const (_  = iotaKB = 1 << (10 * iota)MB = 1 << (10 * iota)GB = 1 << (10 * iota)TB = 1 << (10 * iota)PB = 1 << (10 * iota))
定义数量级 
(这里的<<表示左移操作,
1<<10表示将1的二进制表示向左移10位,也就是由1变成了10000000000,也就是十进制的1024。
同理2<<2表示将2的二进制表示向左移2位,也就是由10变成了1000,也就是十进制的8。)const (a, b = iota + 1, iota + 2 //1,2,  iota=0c, d                      //2,3,  iota=1e, f                      //3,4)
多个iota定义在一行
0+1, 0+2  =  1, 2  //,  iota=0
1+1, 1+2  =  2, 3  //,  iota=1
iotaconst关键字出现时将被重置为0const中每新增【一行】常量声明将使iota计数一次
(iota可理解为const语句块中的行索引)。 
使用iota能简化定义,在定义枚举时很有用。

基本数据类型

整数

注意: 在使用int和 uint类型时,不能假定它是32位或64位的整型,而是考虑int和uint可能在【不同平台上的差异】。

  1. 注意事项 获取对象的长度的内建【len() 】函数返回的长度可以根据不同平台的字节长度进行变化。
  2. 实际使用中,【切片或 map 】 的【元素数量】等都可以用int来表示。
  3. 在涉及到【二进制传输、读写文件】的结构描述时,为了保持文件的结构不会受到不同编译目标平台字节长度的影响,【不要使用】int和 uint。

进制

go+vue——go入门
go+vue——go入门

浮点数

go+vue——go入门

布尔 bool

注意:

布尔类型变量的默认值为false。
Go 语言中不允许将整型强制转换为布尔型.
布尔型无法参与数值运算,也无法与其他类型进行转换。
go+vue——go入门

字符串 string

Go语言中的字符串以【原生数据】类型出现,使用字符串就像使用其他原生数据类型(int、bool、float32、float64 等)一样。 Go 语言里的字符串的内部实现使用【UTF-8】编码。 字符串的值为双引号(")中的内容,可以在Go语言的源码中直接添加非ASCII码字符,例如:
go+vue——go入门

转义字符

go+vue——go入门
go+vue——go入门

多行字符串

Go语言中要定义一个多行字符串时,就必须使用反引号字符:
go+vue——go入门

字符串的常用操作

方法 介绍
len(str) 求长度
+或fmt.Sprintf 拼接字符串
strings.Split 分割
strings.contains 判断是否包含
strings.HasPrefix,strings.HasSuffix 前缀/后缀判断
strings.Index(),strings.LastIndex() 子串出现的位置
strings.Join(a[]string, sep string) join操作

go+vue——go入门
go+vue——go入门
go+vue——go入门

byte和rune类型

rune

/ruːn/


/ruːn/

n.
如尼字母(属于北欧古文字体系);神秘的记号;有魔力的符号
go+vue——go入门

go+vue——go入门

for range

go+vue——go入门

运算符

算数

go+vue——go入门

自增 自减

go+vue——go入门

关系

go+vue——go入门

逻辑

go+vue——go入门

go+vue——go入门
go+vue——go入门

赋值运算符

go+vue——go入门

Go语言基础之流程控制

if else

go+vue——go入门
go+vue——go入门

for

go+vue——go入门
go+vue——go入门
go+vue——go入门

go+vue——go入门

break

go+vue——go入门

continue

go+vue——go入门

for range

Go语言中可以使用for range遍历数组、切片、字符串、map 及通道(channel)。 通过for range遍历的返回值有以下规律:

数组、切片、字符串返回索引和值。
map返回键和值。
通道(channel)只返回通道内的值。

switch case

go+vue——go入门
go+vue——go入门
C++ 不可以:
【关系运算】
go+vue——go入门

array 数组

go+vue——go入门

推导、索引

长度为8,数据元素为string
go+vue——go入门

遍历 [i]

go+vue——go入门

for index, vallue := range cityArray {

go+vue——go入门
go+vue——go入门

二维数组

go+vue——go入门
go+vue——go入门
go+vue——go入门

推导:只能推一位

go+vue——go入门

数组是值类型:(不同于C++)

数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。
go+vue——go入门
go+vue——go入门
go+vue——go入门

slice 切片 :又称动态数组, 对底层数组的封装

初始化: []中什么都不写,就是 定义 切片 的语法

func main() {// 声明切片类型var a []string              //声明一个字符串【切片】,【未 初始化】var b = []int{}             //声明一个整型【切片】并【初始化】var c = []bool{false, true} //声明一个布尔【切片】并【初始化】var d = []bool{false, true} //声明一个布尔【切片】并【初始化】fmt.Println(a)              //[]fmt.Println(b)              //[]fmt.Println(c)              //[false true]fmt.Println(a == nil)       //true		未 初始化[]Tfmt.Println(b == nil)       //false		初始化[]T{}fmt.Println(c == nil)       //false		初始化// fmt.Println(c == d)   //切片是引用类型,不支持直接比较,只能和nil比较
}

go+vue——go入门

数组 初始化 切片

go+vue——go入门

切片 初始化 切片

go+vue——go入门

make(类型, 初始元素个数, 容量 | 默认-同初始元素个数)

go+vue——go入门

len cap

go+vue——go入门

内存图

切片定义切片,容量等于从【起点处】到【终点处】
go+vue——go入门

操作

零值: nil

数值类型:数组
int 零值: 0
字符串 零值: 空串

引用类型: 切片、 map
切片、 map 的零值: nil

比较 : 通过 len(切片)0 来判断是不是空,不能通过nil

切片之间是不能比较的,我们不能使用==操作符来判断两个切片是否含有全部相等元素。 切片唯一合法的比较操作是和nil比较。 一个nil值的切片并没有底层数组,一个nil值的切片的长度和容量都是0。但是我们不能说一个长度和容量都是0的切片一定是nil,例如下面的示例:
go+vue——go入门
go+vue——go入门
go+vue——go入门

赋值拷贝(浅拷贝)

go+vue——go入门

遍历: 索引、for range ; for i,vk := range xxlist ;

go+vue——go入门

添加元素

错误方式:没有初始化,就 [i]=xxx

go+vue——go入门

NewSlice = append(xxx) 必须返回,因为可能是扩容之后的 【新切片】 %v d p

go+vue——go入门

append 可以一次增加多个元素

go+vue——go入门
go+vue——go入门

copy

由于切片是引用类型,所以a和b其实都指向了同一块内存地址。修改b的同时a的值也会发生变化。

Go语言内建的copy()函数可以迅速地将一个切片的数据复制到另外一个切片空间中,copy()函数的使用格式如下:

copy(destSlice, srcSlice []T)
其中:
srcSlice: 数据来源切片
destSlice: 目标切片
go+vue——go入门

删除

go+vue——go入门

练习:空串

go+vue——go入门

练习:sort.Ints ( 切片 )

[ … ] int 是 数组
a[ : ] 转换成切片,对应的仍然是底层数组
go+vue——go入门

map 映射

初始化

map[string] int ; make ( map[string] int , 容量 可选 )

go+vue——go入门
go+vue——go入门

Println( a )

go+vue——go入门

Printf(" %#v ", a)

go+vue——go入门

初始化 赋值

go+vue——go入门

错误方式:没有初始化, 就 c[100] = 200

go+vue——go入门

操作

查询

go+vue——go入门

没有查到:value 返回 零值: 0、空串、nil

go+vue——go入门

查到后:value 返回 key对应的值value

go+vue——go入门

遍历 for range ; for k,v := range xxmap ; map 是无序hash

go+vue——go入门

删除 delete(scoreMap, “key name”)

go+vue——go入门

按序 遍历

无序

go+vue——go入门

顺序: sort.Strings( key_Slice ) 切片排序+遍历map[有序切片]

go+vue——go入门

复杂:slice+map

slice里map:两层初始化工作

go+vue——go入门
go+vue——go入门

map value 是 slice:两层初始化工作

go+vue——go入门

统计单词个数

go+vue——go入门
go+vue——go入门

函数

返回

go+vue——go入门

参数

可变参数: 切片

go+vue——go入门
go+vue——go入门

可变参数 在 固定参数 后

go+vue——go入门

没有默认参数

参数 类型简写

go+vue——go入门

返回值 多个

参数类型简写

go+vue——go入门

defer 延迟执行 : 处理: 资源的释放、文件关闭、解锁、记录时间

go+vue——go入门

作用域

全局变量

go+vue——go入门

局部变量 : 覆盖 全局变量, 从内 往外 找

go+vue——go入门

for 语句块 局部变量i

go+vue——go入门

一等公民 : 三可做 : 变量、参数、返回值

函数 作为 变量

go+vue——go入门

函数 作为 参数

go+vue——go入门

go+vue——go入门

匿名函数

定义变量

go+vue——go入门

直接调用

go+vue——go入门

闭包: 返回的函数 是否 包含 外层变量的 引用

闭包指的是一个【函数和与其相关的引用环境】组合而成的实体。
简单来说,【闭包 = 函数 + 引用环境】。
简单来说,【闭包 = 函数 + 外层变量 引用】。
简单来说,【闭包 = 返回 匿名函数 + 匿名函数 完成对 外层变量的引用】。
首先我们来看一个例子:

函数作为返回值

go+vue——go入门

最基本的 闭包: 返回 内部 匿名 函数 anon ,anon 完成对 外层 变量 引用(参数)

go+vue——go入门

升级

传入资源

go+vue——go入门

闭包进阶示例2:

外层变量 的 引用: suffix = ".jpg"".txt"// 没有后缀、增加后缀
func makeSuffixFunc(suffix string) func(string) string {return func(name string) string {if !strings.HasSuffix(name, suffix) {return name + suffix}return name}
}func main() {jpgFunc := makeSuffixFunc(".jpg")txtFunc := makeSuffixFunc(".txt")fmt.Println(jpgFunc("test")) //test.jpgfmt.Println(txtFunc("test")) //test.txt
}

go+vue——go入门

闭包进阶示例3:


func calc(base int) (func(int) int, func(int) int) {add := func(i int) int {base += ireturn base}sub := func(i int) int {base -= ireturn base}return add, sub
}func main() {f1, f2 := calc(10)fmt.Println(f1(1), f2(2)) //11 9fmt.Println(f1(3), f2(4)) //12 8fmt.Println(f1(5), f2(6)) //13 7
}

go+vue——go入门

内置函数

内置函数 介绍
close 主要用来关闭channel
len 用来求长度,比如string、array、slice、map、channel
new 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针
make 用来分配内存,主要用来分配引用类型,比如chan、map、slice
append 用来追加元素到数组、slice中
panic和recover 用来做错误处理

panic

panic/recover
Go语言中目前(Go1.12)是没有异常机制,但是使用panic/recover模式来处理错误。 panic可以在任何地方引发,但recover只有在defer调用的函数中有效。 首先来看一个例子:
go+vue——go入门

defer recover

go+vue——go入门

指针

Go 语言 函数 都是 值拷贝/浅拷贝

指针=取地址&, %v=%p

go+vue——go入门

取值=*指针

go+vue——go入门

函数 传 指针

go+vue——go入门

未初始化, 引发 panic

go+vue——go入门

new : 整型、浮点型、bool、字符串、数组

go+vue——go入门

make : slice、map、channel

new与make的区别
二者都是用来做内存分配的。
make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。

func new(Type) *Type
func make(t Type, size ...IntegerType) Type

结构体

自定义 类型 %T

go+vue——go入门

别名

自定义类型是定义了一个全新的类型。我们可以基于内置的基本类型定义,也可以通过struct定义。例如:
//将MyInt定义为int类型
type MyInt int
通过type关键字的定义,MyInt就是一种新的类型,它具有int的特性。类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。就像一个孩子小时候有小名、乳名,上学后用学名,英语老师又会给他起英文名,但这些名字都指的是他本人。
type TypeAlias = Type
我们之前见过的runebyte就是类型别名,他们的定义如下:
type byte = uint8
type rune = int32

go+vue——go入门

结构体

使用typestruct关键字来定义结构体,具体代码格式如下:type 类型名 struct {字段名 字段类型字段名 字段类型…
}
其中:类型名:标识自定义结构体的名称,在同一个包内不能重复。
字段名:表示结构体字段名。结构体中的字段名必须唯一。
字段类型:表示结构体字段的具体类型。

go+vue——go入门
go+vue——go入门

结构体 实例化

go+vue——go入门

匿名结构体

go+vue——go入门

结构体 指针 new

go+vue——go入门

可以直接写 p. 不用 ( *p ).

go+vue——go入门

结构体初始化

go+vue——go入门

取结构体的地址实例化 默认 初始化

go+vue——go入门

取结构体的地址实例化 初始化

go+vue——go入门

键值对 初始化 , 最后的“ ,”必须要写

go+vue——go入门

列表 初始化 , 最后的“ ,”必须要写

go+vue——go入门

构造函数

struct 是 值类型

go+vue——go入门
go+vue——go入门

方法

标识符 首字母大写

变量 标识符 首字母 大写,
表示 对外部 可见;
在别的包中 可见。

构造函数

go+vue——go入门

定义方法

值类型 的 接收者

方法: 接受者
构造函数: 任意调用
值类型 的 接收者: p Person
go+vue——go入门

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self。方法的定义格式如下:func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {函数体
}
其中,接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。
接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
方法名、参数列表、返回参数:具体格式与函数定义相同。

指针类型 的 接收者

指针类型 的 接收者: p *Person
go+vue——go入门

针类型的接收者
指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向对象中的this或者self。 例如我们为Person添加一个SetAge方法,来修改实例变量的年龄。// SetAge 设置p的年龄
// 使用指针接收者
func (p *Person) SetAge(newAge int8) {p.age = newAge
}

对比

什么时候应该使用指针类型接收者
需要【修改】接收者中的值
接收者是【拷贝代价】比较大的大对象
保证一致性,如果有某个【方法使用】了指针接收者,
那么其他的方法也应该使用指针接收者。
go+vue——go入门

任意类型添加方法

不可以,不是自己 的 包

go+vue——go入门

可以,起别名(继承)

go+vue——go入门

任意类型添加方法
在Go语言中,接收者的类型可以是任何类型,
不仅仅是结构体,任何类型都可以拥有方法。 举个例子,
我们基于内置的int类型使用type关键字可以定义新的自定义类型,
然后为我们的自定义类型添加方法。//MyInt 将int定义为自定义MyInt类型
type MyInt int//SayHello 为MyInt添加一个SayHello的方法
func (m MyInt) SayHello() {fmt.Println("Hello, 我是一个int。")
}
func main() {var m1 MyIntm1.SayHello() //Hello, 我是一个int。m1 = 100fmt.Printf("%#v  %T\\n", m1, m1) //100  main.MyInt
}注意事项: 非本地类型不能定义方法,
也就是说我们不能给别的包的类型定义方法。
只能在我【自己】的代码包 中 添加 方法,
不能给 go 语言 内置包 中的 基本数据类型  添加方法。
比如,不能给 : int string  array map  添加方法,
但是,可以起别名,然后添加方法。

结构体 嵌套

匿名字段

按照 类型 访问

go+vue——go入门

不能重复 类型

go+vue——go入门

嵌套 结构体

嵌套 结构体

go+vue——go入门

go+vue——go入门

匿名 嵌套 结构体

go+vue——go入门

go+vue——go入门

嵌套 结构体 字段冲突

go+vue——go入门
go+vue——go入门

指定 结构体 名字

go+vue——go入门

结构体 继承 (用 嵌套 模拟, 更准确的说 应该叫 组合)

go+vue——go入门

结构体 字段 可见性

结构体中字段大写开头表示可公开访问,
小写表示私有(仅在定义当前结构体的包中可访问)。

JSON

https://www.json.com

go+vue——go入门

序列化、 反序列化

go+vue——go入门

如果 首字母小写, JSON 访问不了, 字段缺失

序列化,缺失

go+vue——go入门

反序列化,字段为 零值: 空串 、 0

go+vue——go入门

结构体标签(Tag)

结构体标签(Tag)
Tag是结构体的【元信息】,可以在运行的时候通过【反射】的机制读取出来。 
Tag在结构体字段的【后方】定义,由一对【反引号】包裹起来,具体的格式如下:`key1:"value1" key2:"value2"`
结构体tag由一个或多个键值对组成。
键与值使用【冒号】分隔,【值】用【双引号】括起来。
同一个结构体字段可以设置【多个】键值对tag,不同的键值对之间使用【空格】分隔。注意事项: 为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。例如我们为Student结构体的每个字段定义json序列化时使用的Tag://Student 学生
type Student struct {ID     int    `json:"id"` //通过指定tag实现json序列化该字段时的keyGender string //json序列化是默认使用字段名作为keyname   string //私有不能被json包访问
}func main() {s1 := Student{ID:     1,Gender: "男",name:   "沙河娜扎",}data, err := json.Marshal(s1)if err != nil {fmt.Println("json marshal failed!")return}fmt.Printf("json str:%s\\n", data) //json str:{"id":1,"Gender":"男"}
}

序列化 结果

JSON 中的
title 变成 小写
student_list 小写
Tag在结构体字段的【后方】定义,由一对【反引号】包裹起来,具体的格式如下
go+vue——go入门

go+vue——go入门

json、db、xml

go+vue——go入门

注意: 结构体内 slice、 map 使用 make(类型, len(变量名))

因为slice和map这两种数据类型都包含了指向底层数据的指针,
因此我们在需要复制它们时要特别注意。
我们来看下面的例子:


错误:type Person struct {name   stringage    int8dreams []string
}func (p *Person) SetDreams(dreams []string) {p.dreams = dreams
}func main() {p1 := Person{name: "小王子", age: 18}data := []string{"吃饭", "睡觉", "打豆豆"}p1.SetDreams(data)// 你真的想要修改 p1.dreams 吗?data[1] = "不睡觉"fmt.Println(p1.dreams)  // ?
}正确的做法是在方法中使用传入的slice的拷贝进行结构体赋值。func (p *Person) SetDreams(dreams []string) {p.dreams = make([]string, len(dreams))copy(p.dreams, dreams)
}

学生信息管理系统

main.go

package mainimport ("fmt""os"
)// 学员信息管理系统// 需求:
// 1. 添加学员信息
// 2. 编辑学员信息
// 3. 展示所有学员信息func showMenu() {fmt.Println("欢迎来到学员信息管理系统")fmt.Println("1. 添加学员")fmt.Println("2. 编辑学员信息")fmt.Println("3. 展示所有学员信息")fmt.Println("4. 退出系统")
}// 获取用户输入的学员信息
func getInput() *student {var (id    intname  stringclass string)fmt.Println("请按要求输入学员信息")fmt.Print("请输入学员的学号:")fmt.Scanf("%d\\n", &id)fmt.Print("请输入学员的姓名:")fmt.Scanf("%s\\n", &name)fmt.Print("请输入学员的班级:")fmt.Scanf("%s\\n", &class)// 就能拿到用户输入的学员的所有信息stu := newStudent(id, name, class) // 调用student的构造函数造一个学生return stu
}func main() {sm := newStudentMgr()for {// 1. 打印系统菜单showMenu()// 2. 等待用户选择要执行的选项var input intfmt.Print("请输入你要操作的序号:")fmt.Scanf("%d\\n", &input)fmt.Println("用户输入的是:", input)// 3. 执行用户选择的动作switch input {case 1:// 添加学员stu := getInput()sm.addStudent(stu)case 2:// 编辑学员stu := getInput()sm.modifyStudent(stu)case 3:// 展示所有学员sm.showStudent()case 4:// 退出os.Exit(0)}}
}

student.go

package mainimport "fmt"type student struct {id    int // 学号是唯一的name  stringclass string
}// newStudent 是student类型的构造函数
func newStudent(id int, name, class string) *student {return &student{id:    id,name:  name,class: class,}
}// 学员管理的类型
type studentMgr struct {allStudents []*student
}// newStudentMgr 是studentMgr的构造函数
func newStudentMgr() *studentMgr {return &studentMgr{allStudents: make([]*student, 0, 100),}
}// 1. 添加学生
func (s *studentMgr) addStudent(newStu *student) {s.allStudents = append(s.allStudents, newStu)
}// 2. 编辑学生
func (s *studentMgr) modifyStudent(newStu *student) {for i, v := range s.allStudents {if newStu.id == v.id { // 当学号相同时,就表示找到了要修改的学生s.allStudents[i] = newStu // 根据切片的索引直接把新学生赋值进来return}}// 如果走到这里说明输入的学生没有找到fmt.Printf("输入的学生信息有误,系统中没有学号是:%d的学生\\n", newStu.id)
}// 3. 展示学生
func (s *studentMgr) showStudent() {for _, v := range s.allStudents {fmt.Printf("学号:%d 姓名:%s 班级:%s\\n", v.id, v.name, v.class)}
}

包 packet

包名 定义

  1. 一个文件夹 == 一个包;
  2. 一个文件夹下,
    • 所有.go文件的 第一行 packet 都相同
    • .go文件的名字无所谓,xx.go
  3. 包名 可以 和 文件夹名 不一样
  4. 包名 不能 包含 “-” 中横线
  5. 包名 为 main 的 包 是程序 入口
    • 不包含 main 包, 的源代码 , 不会得到 可执行文件

包的声明 === 文件夹 的名字

默认: 包的声明 === 文件夹 的名字
go+vue——go入门

导入 包 $GOPATH/src 后面写起

打包生成 的二进制文件, 不需要在引入 packet, 引入的 源代码 已经被打包进去。
go+vue——go入门
go+vue——go入门

给包 起别名

go+vue——go入门

匿名导入包 , 只调用 init() 函数

func init() {} 优先于 main 函数 , 多用于 初始化: 日志、加载配置文件

go+vue——go入门

全局声明 --> init() --> main()

go+vue——go入门

init() 执行顺序

go+vue——go入门
go+vue——go入门
go+vue——go入门
go+vue——go入门

同一个包 , 多个文件 , 可以相互调用

go+vue——go入门

接口 : 是一个类型, 一个抽象的类型

接口类型
接口是一种由程序员来定义的类型,一个接口类型就是一组方法的集合,它规定了需要实现的所有方法。相较于使用结构体类型,当我们使用接口类型说明相比于它是什么更关心它能做什么。接口的定义
每个接口类型由任意个方法签名组成,接口的定义格式如下:type 接口类型名 interface{方法名1( 参数列表1 ) 返回值列表1方法名2( 参数列表2 ) 返回值列表2}
其中:接口类型名:Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有关闭操作的接口叫closer等。接口名最好要能突出该接口的类型含义。
方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。
举个例子,定义一个包含Write方法的Writer接口。type Writer interface{Write([]byte) error
}
当你看到一个Writer接口类型的值时,你不知道它是什么,唯一知道的就是可以通过调用它的Write方法来做一些事情。

struct 必须实现 接口 interface 的函数

go+vue——go入门

不实现 interface 的 函数, 就报错

go+vue——go入门

多态 : 类似于C++虚函数+父指针

go+vue——go入门

指针 给 接口

使用 【值接受者】 实现接口

go+vue——go入门
go+vue——go入门

使用 【指针接受者】 实现接口

值p1 := person{} 就 不能存到 m接口 里面
值p2 := &person{} 可以 存到 m接口 里面

go+vue——go入门

一个 struct 类型, 可以 实现 多个接口 (例子如下) ; 多个类型 也可实现 一个接口 (例子如上)

go+vue——go入门
go+vue——go入门

接口 的嵌套

go+vue——go入门
go+vue——go入门

空接口 : 作为 : 函数参数 ; map 扩展 值value

所有类型 都满足
可以接受 任意数据
不需要 提前 定义, 直接使用 interface{}
go+vue——go入门

Println ( a … interface{} )

go+vue——go入门

变长的函数参数 func f1(parms …int){

变长的函数参数
package mainimport ("fmt"
)func f1(parms ...int){for i,v := range parms {fmt.Printf("%v %v\\n",i,v)}
}func main() {f1(012)
}

go+vue——go入门
等价于下面代码


package mainimport ("fmt"
)func f1(parms []int){for i,v := range parms {fmt.Printf("f2:%v %v\\n",i,v)}
}func main() {b := []int{0,1,2}f1(b)  

打散Slice , arr1 = append(arr1,arr2…)

打散Slice
package mainimport ("fmt"
)func main() {var arr1 []intarr2 := []int{1,2,3}arr1 = append(arr1,0)	arr1 = append(arr1,arr2...)	 //arr2... 将切片arr2打散成 ==> arr1 = append(arr1,1,2,3)fmt.Printf("%v\\n",arr1)var arr3 []bytearr3 = append(arr3,[]byte("hello")...)  fmt.Printf("%s\\n",arr3)
}

go+vue——go入门

底层实现

go+vue——go入门

类型 断言 x.( string )

go+vue——go入门

Switch 猜猜猜 (为什么,不先打印出来,先判断是true、“”,然后再猜?)

go+vue——go入门

reflect 反射 (暂时跳过 ----------------------------------------- -----------------------------------------)

结构体 反射 (暂时跳过 ----------------------------------------- -----------------------------------------)

并发编程

goroutine 用户态 线程 、 轻量级 、 灵活 、 资源少 、 go 语言 调度 、 包装好的 线程池 启动一个个任务

channel 通信

goroutine

go关键字


Go语言中使用 goroutine 非常简单,只需要在函数或方法调用前加上go关键字就可以创建一个 goroutine ,从而让该函数或方法在新创建的 goroutine 中执行。go f()  // 创建一个新的 goroutine 运行函数f
匿名函数也支持使用go关键字创建 goroutine 去执行。go func(){// ...
}()
一个 goroutine 必定对应一个函数/方法,可以创建多个 goroutine 去执行相同的函数/方法。

等待

go+vue——go入门

time.Sleep(time.Second)

go+vue——go入门
go+vue——go入门

wg.Wait()

package mainimport ("fmt""sync"
)// 声明全局等待组变量
var wg sync.WaitGroupfunc hello() {fmt.Println("hello")wg.Done() // 告知当前goroutine完成
}func main() {wg.Add(1) // 登记1个goroutinego hello()fmt.Println("你好")wg.Wait() // 阻塞等待登记的goroutine完成
}

go+vue——go入门

go+vue——go入门

匿名函数

还是一个闭包、 包含外部函数变量 i

main 函数 i 都走到10000, 闭包 才想起 自己要做什么事情
go+vue——go入门

传参

go+vue——go入门

单核(一个先执行完) 、 多核(混在一起)

go+vue——go入门
go+vue——go入门

channel

make (chan int, 10)

go+vue——go入门

无缓冲区 ,阻塞 死锁 ,同步通道: 当面交付 数据, 快递员交到你的手上

go+vue——go入门

带缓冲区 , 异步通道: 驿站

go+vue——go入门

长度、 容量、 len cap

go+vue——go入门

ch1 ch2 同步三个

go+vue——go入门

两种 从通道 取值 方式

go+vue——go入门

单向通道

chan<-

go+vue——go入门

<-chan

go+vue——go入门

报错:

go+vue——go入门

chan 异常 总结

go+vue——go入门
关闭 后 , 读完数据, 返回 value, ok = ( int 0 , false ) = ( string ""空串 , false )

worker pool 线程池 (暂时跳过 ----------------------------------------- -----------------------------------------)

并发同步 与 锁 (暂时跳过 ----------------------------------------- -----------------------------------------)

网络 编程 (暂时跳过 ----------------------------------------- -----------------------------------------)

单元测试 (暂时跳过 ----------------------------------------- -----------------------------------------)

教育在线