一、变量定义
学习链接
var a int
var b bool
var c int8
var (
m int
n string
)
var name string = "china"
func main() {
age := 18 //生命变量age同时初始化;编译器会根据右边的初始值推断出age是什么类型
}
二、常量定义
const PI = 3.1415926
const KB = 1024
iota
Go中借助iota来实现枚举
① iota在const关键字出现时将被重置为0
② const中每新增一行常量生命将使iota累加一次
③ const生命的常量后面没有内容默认就跟上一行一致
三、基本数据类型
String
使用双引号表示字符串 "hello"
使用单引号表示字符 'h'
整形
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64
浮点型
float32 float64
浮点数永远都是不准确
处理浮点数的方法:
① 转换成字符串去做运算
② 整体放大多少倍转换成整数进行运算
复数
complex64 complex128
布尔
var a bool //默认是false
var b = true
Go语言中布尔值不能和其他类型做转换
byte和rune
英文字符用byte(ASCII码能表示)
rune(中文,UTF-8编码)
字符串中存在中文的时候用rune
四、流程控制
if
for
switch和case
以下了解为主
goto+label
continue+label
break+label
五、格式化打印参数
学习链接
六、数组
学习链接
七、切片
切片与数组的区别是没有固定的长度,切片本质就是一个数组,是语言层面对数组的封装;
切片三要素:
① 地址 (切片中第一个元素指向的内存空间)
② 大小 (切片中目前元素的个数) len()
③ 容量 (底层数组最大能存放的元素的个数) cap()
得到切片的三种方式:
① 直接声明 a := []int{1, 2, 3} len:3 cap:3
② 基于数组得到切片 m := [5]int b:= m[:3] len:3 cap:5
③ 基于切片再得到切片 b := a[:2] len:2 cap:3
切片支持自动扩容,扩容策略分为三种:
① 每次只追加一个元素,每一次的容量是上一次的2倍;
② 追加的超过原来容量的1倍,就等于原来的容量+扩容元素个数的最接近的偶数;
③ 如果切片的容量大于了1024,后续就每次扩容0.25倍;
make初始化切片:
make:用来给引用类型(常用引用类型:切片和map)做初始化(申请内存空间);
切片必须初始化,或者用append才能使用;
var s []int
s = make([]int, 大小, 容量)
八、指针
指针和地址的区别:
地址:就是内存地址(用字节来描述的内存地址)
指针:指针是带类型的。
$和*
$ : 表示取地址
* : 根据地址取值
九、结构体
1. 结构体的所有字段在内存中是连续的
2. 结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型)
3. 结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转
4. struct的每个字段上,可以写上一个tag,该tag可能通过反射机制获取,常见的使用场景就是序列化和反序列化.
5. 如果1个struct内部字段存储的数据量很大,重复copy造成内存开销过大,自定义1个构造函数返回指针类型的struct
十、接口
1. 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例),即可以把struct变量付给interface变量
2. 接口中所有的方法都没有方法体,即都是没有实现的方法。
3. 在 Golang 中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。
4. 一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
5. 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
6. Golang 接口中不能有任何变量
7. interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil
8. 空接口 interface{} 没有任何方法,所以所有类型都实现了空接口, 即我们可以把任何一个变量赋给空接口.
十一、goroutine
1. 并发和并行
2. goroutine是用户态线程(程序员通过写代码控制);
3. 启动goroutine:
① 使用go关键字
② 一个goroutine对应一个函数,这个函数就是要做的事情;
4. goroutine的原理
① M:N 将m个goroutine调度到n个操作系统线程上。n默认是操作系统的逻辑核心数;
5. goroutine和OS线程的区别:
① goroutine是用户态的线程,初始开销大小很小(2KB),可以随着需求扩充,最大能达1G;
② 一个OS线程是很重量级的,通常内存开销达2MB;
③ 通过runtime.GOMAXPROCS() 用来设置Go并发使用的CPU核数,1.5版本之后默认跑满CPU;
6. goroutine的特点:
① 一个goroutine对应一个函数,这个函数就是要做的事情;
② main函数就是一个goroutine;
③ 当goroutine对应的函数返回的时候goroutine就结束了;
④ main函数所在的goroutine结束了,由它启动的那些goroutine也就结束了
7. sync.WaitGroup
① 它是一个结构体类型 var wg sync.WaitGroup
② 三个方法:
wg.Add(n)
wg.Done()
wg.Wait()
十二、channel
1. channel是一种类型,一种引用的类型;
2. channel的声明和初始化:
var ch chan int
channel声明之后要用make函数初始化之后才能使用,ch = make(chan int, [cap])
3. channel的三个操作:
发送:ch <- 100
接收;<- ch 可以使用变量接收值: a := <- ch 也可以直接丢弃:<- ch
关闭:chose(ch)
关闭后的通道还是可以取值,取完之后返回的类型零值;
关闭的通道不能再发送值;
关闭的通道不能再关闭;
4. 无缓冲区和有缓冲区
无缓冲区channel又称为同步channel,必须有接收才能发送;
有缓冲区的channel,超过容量就阻塞;
5. 优雅地从通道取值(能判断通道是否被关闭)
v, ok := <- ch 如果通道被关闭ok返回的事false
for v := range {}
6. 单向通道
只能接收(<-ch)或者只能发送(ch<-)的通道;
多用在函数传参的时候,限制某个通道在函数中只能做什么类型的操作;
7. select多路复用
同一时刻可以对多个通道做操作;
十三、并发控制与锁
1. 很多并发的场景下需要gouroutine之间做协同处理;
2. 如果多个goroutine操作同一个全局变量的时候,就会出现数据竞争问题;
3. 互斥锁
① sync.Mutex 它是一个结构体类型
② 声明锁
var lock sync.Mutex
③ 操作
加锁:lock.Lock()
解锁: lock.Unlock()
4. 读写锁
① sync.RWLock() 适用于读多写少的场景,类比网站数据库的读完分离;
② 声明读写锁
var rwLock sync.RWMutex()
③ 操作
加读锁: rwLock.RLock()
解读锁: rwLock.Runlock()
加写锁: rwLock.Lock()
解写锁: rwLock.Unlock()
5. sync.Map
① 内置的map不是并发安全的;
② 并发场景下推荐使用sync.Map
6. sync.Once
① 闭包使用
其他
1、 那些数据类型使用for range
① 字符串
② 数组(包括切片)
③ map
2、变量定义过程
① 变量命名
② 变量初始化,用于申请内存
3、make和new
make:用来给引用类型(常用引用类型:切片slice、map、chan)做初始化(申请内存空间);
切片必须初始化,或者用append才能使用;
var s []int
s = make([]int, 大小, 容量)
new: 用来创建值类型;比如int struct。返回的是指针
var a = new(int)
4、GoLang与PHP接口的区别:
① PHP中通过实例类或抽象类来显式实现implements接口interface(接口中定义了要实现的方法);
② Golang中通过隐式实现接口,即只要一个类型实现了接口中规定的所有方法(实际是结构体接收者方法),那么它就实现了这个接口;
5、结构体初始化方法
type Rect struct {
x, y float64
width, height float64
}
① rect1 := new(Rect)
② rect2 := &Rect{}
③ rect3 := &Rect{0, 0, 100, 200}
④ rect4 := &Rect{width:100, height:200}