• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

go+vue——go入门

武飞扬头像
待木成植2
帮助1

技术选择

入坑理由

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

学新通
学新通
看到有人推【七米】,所以。。。
学新通

推荐:【七米】

代码

https://github.com/Q1mi/go_tutorial

博客

https://liwenzhou.com/
学新通
https://www.liwenzhou.com/posts/Go/golang-menu/
学新通
https://www.liwenzhou.com/posts/Go/install/
学新通

搭建Go语言开发环境

下载 并 安装

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

检查是否安装好?

学新通

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

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

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

学新通

VScode

插件

chinese

学新通
第一个【简体】,安装完后,【右下角】【重启】
学新通

go

学新通

新建项目

错误提示,直接点【install】

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

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

go mod init

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

go mod init hello

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

学新通
require 表示引用其他人的库
学新通

编写程序 hello world

学新通

编译 go build

go build

go build -o  Out_Name.exe

生成可执行文件 hello.exe
学新通

执行 .\xxx.exe

只有一个go文件时,临时编译并执行,不生产exe文件
go run main.go

windows   ——  powershell
 .\xxx.exe

windows   ——  cmd
 xxx.exe
 
linux、mac
./xxx.exe  

看清楚【终端】是 powershell 还是 cmd
学新通

学新通

学新通

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

学新通
学新通

终端 之间 切换

学新通

跨平台编译

cmd 中 设置环境变量
学新通
不能在 windows 执行, 只能在 Linux 上执行
学新通

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

cmd中执行

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

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

学新通
学新通

并 帮你 引入 第三方包

学新通

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

学新通

下载第三方库
go get github.com/q1mi/hello

学新通

Go语言基础之变量和常量

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

变量(同C )

注意事项:

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

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

const (
		n1 = iota //0
		n2        //1
		n3        //2
		n4        //3
	)

const (
		n1 = iota //0
		n2        //1
		_
		n4        //3
	)

const (
		n1 = iota //0
		n2 = 100  //100
		n3 = iota //2
		n4        //3
	)
	const n5 = iota //0

iota在const关键字出现时将被重置为0。

const (
		_  = iota
		KB = 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=0
		c, d                      //2,3,  iota=1
		e, f                      //3,4
	)
多个iota定义在一行
0 1, 0 2  =  1, 2  //,  iota=0
1 1, 1 2  =  2, 3  //,  iota=1
iota在const关键字出现时将被重置为0。
const中每新增【一行】常量声明将使iota计数一次
(iota可理解为const语句块中的行索引)。 
使用iota能简化定义,在定义枚举时很有用。


学新通

基本数据类型

整数

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

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

进制

学新通
学新通

浮点数

学新通

布尔 bool

注意:

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

字符串 string

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

转义字符

学新通
学新通

多行字符串

Go语言中要定义一个多行字符串时,就必须使用反引号字符:
学新通

字符串的常用操作

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

学新通
学新通
学新通

byte和rune类型

rune

/ruːn/


/ruːn/

n.
如尼字母(属于北欧古文字体系);神秘的记号;有魔力的符号
学新通

学新通

for range

学新通

运算符

算数

学新通

自增 自减

学新通

关系

学新通

逻辑

学新通

学新通
学新通

赋值运算符

学新通

Go语言基础之流程控制

if else

学新通
学新通

for

学新通
学新通
学新通

学新通

break

学新通

continue

学新通

for range

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

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

switch case

学新通
学新通
C 不可以:
【关系运算】
学新通

array 数组

学新通

推导、索引

长度为8,数据元素为string
学新通

遍历 [i]

学新通

for index, vallue := range cityArray {

学新通
学新通

二维数组

学新通
学新通
学新通

推导:只能推一位

学新通

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

数组是值类型,赋值和传参会复制整个数组。因此改变副本的值,不会改变本身的值。
学新通
学新通
学新通

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		未 初始化[]T
	fmt.Println(b == nil)       //false		初始化[]T{}
	fmt.Println(c == nil)       //false		初始化
	// fmt.Println(c == d)   //切片是引用类型,不支持直接比较,只能和nil比较
}

学新通

数组 初始化 切片

学新通

切片 初始化 切片

学新通

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

学新通

len cap

学新通

内存图

切片定义切片,容量等于从【起点处】到【终点处】
学新通

操作

零值: nil

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

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

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

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

赋值拷贝(浅拷贝)

学新通

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

学新通

添加元素

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

学新通

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

学新通

append 可以一次增加多个元素

学新通
学新通

copy

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

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

copy(destSlice, srcSlice []T)
其中:
srcSlice: 数据来源切片
destSlice: 目标切片
学新通

删除

学新通

练习:空串

学新通

练习:sort.Ints ( 切片 )

[ … ] int 是 数组
a[ : ] 转换成切片,对应的仍然是底层数组
学新通

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:

外层变量 的 引用: 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.jpg
	fmt.Println(txtFunc("test")) //test.txt
}
学新通

学新通

闭包进阶示例3:


func calc(base int) (func(int) int, func(int) int) {
	add := func(i int) int {
		base  = i
		return base
	}

	sub := func(i int) int {
		base -= i
		return base
	}
	return add, sub
}

func main() {
	f1, f2 := calc(10)
	fmt.Println(f1(1), f2(2)) //11 9
	fmt.Println(f1(3), f2(4)) //12 8
	fmt.Println(f1(5), f2(6)) //13 7
}
学新通

学新通

内置函数

内置函数 介绍
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调用的函数中有效。 首先来看一个例子:
学新通

defer recover

学新通

指针

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

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

学新通

取值=*指针

学新通

函数 传 指针

学新通

未初始化, 引发 panic

学新通

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

学新通

make : slice、map、channel

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

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

结构体

自定义 类型 %T

学新通

别名

自定义类型是定义了一个全新的类型。我们可以基于内置的基本类型定义,也可以通过struct定义。例如:
//将MyInt定义为int类型
type MyInt int
通过type关键字的定义,MyInt就是一种新的类型,它具有int的特性。


类型别名规定:TypeAlias只是Type的别名,本质上TypeAlias与Type是同一个类型。就像一个孩子小时候有小名、乳名,上学后用学名,英语老师又会给他起英文名,但这些名字都指的是他本人。
type TypeAlias = Type
我们之前见过的rune和byte就是类型别名,他们的定义如下:
type byte = uint8
type rune = int32

学新通

结构体

使用type和struct关键字来定义结构体,具体代码格式如下:

type 类型名 struct {
    字段名 字段类型
    字段名 字段类型
    …
}
其中:

类型名:标识自定义结构体的名称,在同一个包内不能重复。
字段名:表示结构体字段名。结构体中的字段名必须唯一。
字段类型:表示结构体字段的具体类型。

学新通
学新通

结构体 实例化

学新通

匿名结构体

学新通

结构体 指针 new

学新通

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

学新通

结构体初始化

学新通

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

学新通

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

学新通

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

学新通

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

学新通

构造函数

struct 是 值类型

学新通
学新通

方法

标识符 首字母大写

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

构造函数

学新通

定义方法

值类型 的 接收者

方法: 接受者
构造函数: 任意调用
值类型 的 接收者: p Person
学新通

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者 self。

方法的定义格式如下:

func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
    函数体
}
其中,

接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写,而不是self、this之类的命名。例如,Person类型的接收者变量应该命名为 p,Connector类型的接收者变量应该命名为c等。
接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。
方法名、参数列表、返回参数:具体格式与函数定义相同。
指针类型 的 接收者

指针类型 的 接收者: p *Person
学新通

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

// SetAge 设置p的年龄
// 使用指针接收者
func (p *Person) SetAge(newAge int8) {
	p.age = newAge
}

对比

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

任意类型添加方法

不可以,不是自己 的 包

学新通

可以,起别名(继承)

学新通

任意类型添加方法
在Go语言中,接收者的类型可以是任何类型,
不仅仅是结构体,任何类型都可以拥有方法。 

举个例子,
我们基于内置的int类型使用type关键字可以定义新的自定义类型,
然后为我们的自定义类型添加方法。

//MyInt 将int定义为自定义MyInt类型
type MyInt int

//SayHello 为MyInt添加一个SayHello的方法
func (m MyInt) SayHello() {
	fmt.Println("Hello, 我是一个int。")
}
func main() {
	var m1 MyInt
	m1.SayHello() //Hello, 我是一个int。
	m1 = 100
	fmt.Printf("%#v  %T\n", m1, m1) //100  main.MyInt
}

注意事项: 非本地类型不能定义方法,
也就是说我们不能给别的包的类型定义方法。
只能在我【自己】的代码包 中 添加 方法,
不能给 go 语言 内置包 中的 基本数据类型  添加方法。
比如,不能给 : int string  array map  添加方法,
但是,可以起别名,然后添加方法。
学新通

结构体 嵌套

匿名字段

按照 类型 访问

学新通

不能重复 类型

学新通

嵌套 结构体

嵌套 结构体

学新通

学新通

匿名 嵌套 结构体

学新通

学新通

嵌套 结构体 字段冲突

学新通
学新通

指定 结构体 名字

学新通

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

学新通

结构体 字段 可见性

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

JSON

https://www.json.com

学新通

序列化、 反序列化

学新通

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

序列化,缺失

学新通

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

学新通

结构体标签(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序列化该字段时的key
	Gender string //json序列化是默认使用字段名作为key
	name   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在结构体字段的【后方】定义,由一对【反引号】包裹起来,具体的格式如下
学新通

学新通

json、db、xml

学新通

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

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


错误:

type Person struct {
	name   string
	age    int8
	dreams []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 main

import (
	"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    int
		name  string
		class 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 int
		fmt.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 main

import "fmt"

type student struct {
	id    int // 学号是唯一的
	name  string
	class 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 包, 的源代码 , 不会得到 可执行文件
  6.  

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

默认: 包的声明 === 文件夹 的名字
学新通

导入 包 $GOPATH/src 后面写起

打包生成 的二进制文件, 不需要在引入 packet, 引入的 源代码 已经被打包进去。
学新通
学新通

给包 起别名

学新通

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

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

学新通

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

学新通

init() 执行顺序

学新通
学新通
学新通
学新通

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

学新通

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

接口类型
接口是一种由程序员来定义的类型,一个接口类型就是一组方法的集合,它规定了需要实现的所有方法。

相较于使用结构体类型,当我们使用接口类型说明相比于它是什么更关心它能做什么。

接口的定义
每个接口类型由任意个方法签名组成,接口的定义格式如下:

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 的函数

学新通

不实现 interface 的 函数, 就报错

学新通

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

学新通

指针 给 接口

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

学新通
学新通

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

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

学新通

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

学新通
学新通

接口 的嵌套

学新通
学新通

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

所有类型 都满足
可以接受 任意数据
不需要 提前 定义, 直接使用 interface{}
学新通

Println ( a … interface{} )

学新通

变长的函数参数 func f1(parms …int){
变长的函数参数
package main
 
import (
	"fmt"
)
 
func f1(parms ...int){
    for i,v := range parms {
	    fmt.Printf("%v %v\n",i,v)
	}
}
 
func main() {
   f1(0,1,2)
}
学新通

学新通
等价于下面代码


package main
 
import (
	"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 main
 
import (
	"fmt"
)
 
func main() {
    var arr1 []int
    arr2 := []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 []byte
    arr3 = append(arr3,[]byte("hello")...)  
    fmt.Printf("%s\n",arr3)
}
学新通

学新通

底层实现

学新通

类型 断言 x.( string )

学新通

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

学新通

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

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

并发编程

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

channel 通信

goroutine

go关键字


Go语言中使用 goroutine 非常简单,只需要在函数或方法调用前加上go关键字就可以创建一个 goroutine ,从而让该函数或方法在新创建的 goroutine 中执行。

go f()  // 创建一个新的 goroutine 运行函数f
匿名函数也支持使用go关键字创建 goroutine 去执行。

go func(){
  // ...
}()
一个 goroutine 必定对应一个函数/方法,可以创建多个 goroutine 去执行相同的函数/方法。

等待

学新通

time.Sleep(time.Second)

学新通
学新通

wg.Wait()
package main

import (
	"fmt"
	"sync"
)

// 声明全局等待组变量
var wg sync.WaitGroup

func hello() {
	fmt.Println("hello")
	wg.Done() // 告知当前goroutine完成
}

func main() {
	wg.Add(1) // 登记1个goroutine
	go hello()
	fmt.Println("你好")
	wg.Wait() // 阻塞等待登记的goroutine完成
}
学新通

学新通

学新通

匿名函数

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

main 函数 i 都走到10000, 闭包 才想起 自己要做什么事情
学新通

传参

学新通

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

学新通
学新通

channel

make (chan int, 10)

学新通

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

学新通

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

学新通

长度、 容量、 len cap

学新通

ch1 ch2 同步三个

学新通

两种 从通道 取值 方式

学新通

单向通道

chan<-

学新通

<-chan

学新通

报错:

学新通

chan 异常 总结

学新通
关闭 后 , 读完数据, 返回 value, ok = ( int 0 , false ) = ( string ""空串 , false )

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

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

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

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

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgfbifk
系列文章
更多 icon
同类精品
更多 icon
继续加载