-
:= 不支持声明全局变量,作用域为局部变量。
-
iota,默认值为0,每行的iota都会累加1。
-
Golang的自增自减’++’ ‘–‘是语句而非表达式
a := 1 a ++ // 注意:不能写成 ++ a 或 -- a 必须放在右边使用 // b := a++ // 此处为错误的用法,不能写在一行,要单独作为语句使用
-
对外函数的函数名应该首字母大写,否则外部包无法调用该函数(类、属性亦然)。
-
在包中,任何顶级声明前面的注释都将作为该声明的文档注释。在程序中,每个可导出(首字母大写)的名称都应该有文档注释。
-
包应当以小写的单个单词来命名,不能使用下划线或驼峰。
-
import _ “fmt” : 给包起匿名别名,无法调用该包的方法但是可以执行当前包内部的init()方法,此即为副作用而导入。
-
import . “fmt” :将当前包的全部方法导入到当前包的作用中,fmt包中的全部方法可以直接使用API调用,不需要使用fmt.API来调用。
-
defer执行顺序:先写的defer先入栈(代码行在上部的先入栈),然后先进后出,后出栈。
-
defer和return的先后顺序:return语句先执行,当该函数体执行完毕后(当前作用域内),defer语句执行。这是由于在Go语言中,return 语句不是原子操作,分为赋值,和返回值两步操作,最先是所有结果值在进入函数时都会初始化为其类型的零值(姑且称为ret赋值),然后执行defer命令,最后才是return操作。如果是有名返回值,返回值变量其实可视为是引用赋值,可以能被defer修改。而在匿名返回值时,给ret的值相当于拷贝赋值,defer命令时不能直接修改。return最先执行,return负责将结果写入返回值中;接着defer开始执行一些收尾工作;最后函数携带当前返回值退出。
-
defer延迟调用与闭包:defer 调用会在当前函数执行结束前才被执行,这些调用被称为延迟调用。defer 中使用的匿名函数也是一个闭包。重要的是:当defer被声明时,其参数就会被实时解析。
func func1() { a := 1 defer func(r int) { fmt.Println(r) }(a) a = a + 100 fmt.Println(a) } func func2() { a := 1 defer func() { fmt.Println(a) }() a = a + 100 fmt.Println(a) }
func1输出结果:
$go run main.go 101 1
func2输出结果
$go run main.go 101 101
两个函数的差异在于,func1中的defer定义时就将a=1赋值给了defer,在执行defer函数时执行时用的a是在定义时对a的拷贝并非当前环境变量中的a值,即defer执行的是:
func (r int) { fmt.Println(r) }(1)
而在func2中,在defer定义时并没有完成任何赋值动作,只是注册了在执行完成后调用的函数,使用的a变量是当前环境的变量。
-
函数调用时,引用类型(slice, map, interface, channel)都默认使用传递指针的值传递——只是功能类似于引用传递罢了。详见Go语言引用传递与值传递
-
在Go中,结构体是一种聚合的数据类型,它包含零个或多个任意类型的值作为成员。由于结构体是值类型,当将一个结构体变量赋值给另一个变量时,会创建一个新的结构体实例,并将原始结构体的值拷贝给新实例。这意味着两个结构体变量将拥有独立的副本,对其中一个变量的修改不会影响另一个变量。
-
匿名函数的快速执行:
func(){ fmt.println("Hello") }() //这里加上()是定义并执行函数
-
闭包:简单来说,闭包 = 函数 + 引用环境
-
slice动态数组对其传递可以修改原slice数据是因为使用了slice这个引用类型的指针进行的传递,是值传递(传递指针)而不是引用传递——GO只存在值传递——map类型亦是如此。slice能够通过函数传参后,修改对应的数组值,是因为 slice 内部保存了引用数组的指针,并不是因为引用传递。
-
固定长度的数组在传参的时候是严格匹配数据类型的,e.g:[10]int的参数无法传到[5]int中,反之亦然,因为数组的大小是其类型的一部分。
-
var slice []int : 声明slice是一个切片,但是没有给其分配空间,修改其内部元素会报错index out;
可以通过
slice = make ([]int, 3)
来分配空间—— 一般通过
slice := make([]int, 3)
来声明一个切片并开辟内存空间。
切片保存了对底层数组的引用,若你将切片赋予另一个切片,它们会引用同一个数组。
-
var slice = make([]int, len, cap) :长度为len,容量为cap;
slice := arr[startIndex : endIndex] : 初始化切片slice为数组arr的引用。
-
slice中的len表示数组空间内存在有效值的数组长度,cap表示这个底层数组空间的大小,当len达到cap时,cap自动扩容一倍;在修改切片中的元素时,需要确保索引不超出切片的长度len范围。
如果期望容量大于当前容量的两倍就会使用期望容量; 如果当前切片的长度小于阈值(默认 256)就会将容量翻倍; 如果当前切片的长度大于等于阈值(默认 256),就会每次增加 25% 的容量,基准是 newcap + 3*threshold,直到新容量大于期望容量;
-
切片截取:slice[x : y] : 截取从索引x(包含)到索引y(不包含)。
-
使用 copy 函数要注意对于 copy(dst, src),要初始化 dst 的 len,否则无法复制。
-
切片就像数组的引用 切片并不存储任何数据,它只是描述了底层数组中的一段,更改切片的元素会修改其底层数组中对应的元素,和它共享底层数组的切片都会观测到这些修改。
-
var myMap map[int] string : 声明myMap是一种map类型,key是int型,value是string型,但是没有分配内存空间;
可以通过myMap = make(map[int]string, 10)来分配空间——一般通过
myMap := make(map[int]string) myMap2[1] = "java" myMap2[2] = "c++" myMap2[3] = "python" //或者 myMap := map[string]string{ "one": "php", "two": "c++", "three": "python", }
来进行声明+初始化。
-
哪些数据结构可以作为map的key?
具有可比较性:键的类型必须支持相等性比较;
可哈希性:键的类型必须支持哈希计算;
通常使用整数、字符串、浮点数、指针等基本类型作为 map 的键;
切片和函数不支持比较,不能用作map的键。
-
结构体指针:结构体调用成员变量可以使用 变量名.成员名、指针名.成员名 都可以,直接用指针名.成员名的方式,修改原地址的值。
-
Go 语言中的接口是隐式实现的,也就是说,如果一个类型实现了一个接口定义的所有方法,那么它就自动地实现了该接口——鸭子类型。
-
只包含一个方法的接口应当以该方法的名称加上 -er 后缀来命名。
-
Go没有显式的继承,而是通过组合实现继承,内嵌一个(或多个)包含想要的行为(字段和方法)的结构体;多重继承可以通过内嵌多个结构体实现。
-
所有的类型包括自定义类型都实现了空接口interface{},所以空接口interface{}可以被当做任意类型的数值/万能数据类型。
-
类型断言用于将接口类型转换为指定类型,其语法为:
value.(type) 或者 value.(T) str, ok := value.(string)
其中 value 是接口类型的变量,type 或 T 是要转换成的类型。
如果类型断言成功,它将返回转换后的值和一个布尔值,表示转换是否成功;如果断言失败,str将继续存在且为字符串类型,但将拥有零值——即空字符串。
-
断言成功的两种情况:两种动态类型是相同的;断言接口值实现了目标接口的所有方法(结构体类型的断言)。
-
[对于指针接收器和值接收器方法的一些拙见.md](C:\Users\GEORGE DING\Desktop!\BLOG\Golang\对于指针接收器和值接收器方法的一些拙见.md)
-
new 和 make 均是用于分配内存:new用于值类型的内存分配,并且置为零值。make只用于slice、map以及channel这三种引用数据类型的内存分配和初始化。new(T) 分配类型 T 的零值并返回其地址,也就是指向类型 T 的指针。make(T) 它返回类型T的值(不是* T)。
-
panic可以在任何地方引发,但是recover只能在defer的函数中有效。recover只能捕获同一goroutine内发生的panic,对于其他goroutine引发的panic无能为力。
Golang学习中遇到的那些坑
描述了一下本人学习Golang过程中遇到的问题
...