25个关键字
程序声明:import, package
程序实体声明和定义:chan, const, func, interface, map, struct, type, var
程序流程控制:go, select, break, case, continue, default, defer, else, fallthrough, for, goto, if, range, return
类型
18个基本类型:bool, string, rune, byte, int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, float32, float64, complex64, complex128
7个复合类型:array, struct, function, interface, slice, map, channel
其中,切片、字典、通道类型都是引用类型
类型的声明一般以 type 关键字开始,然后是自定义的标识符名称,然后是基本类型的名称或复合类型的定义。
Unicode字符rune类型是和int32等价的类型,通常用于表示一个Unicode码点。这两个名称可以互换使用。同样byte也是uint8类型的等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。
最后,还有一种无符号的整数类型uintptr,没有指定具体的bit大小但是足以容纳指针。uintptr类型只有在底层编程是才需要,特别是Go语言和C语言函数库或操作系统接口相交互的地方。
一个float32类型的浮点数可以提供大约6个十进制数的精度,而float64则可以提供约15个十进制数的精度;通常应该优先使用float64类型,因为float32类型的累计计算误差很容易扩散,并且float32能精确表示的正整数并不是很大
操作符
列举一些特殊的操作符,注意下面的位操作符
& 位运算 AND | 位运算 OR ^ 位运算 XOR &^ 位清空 (AND NOT) << 左移 >> 右移
可以通过 Printf 函数的 %b 参数来输出二进制格式的数字。
特殊的空标识符
下划线 _ 作为一个特殊的标识符,可以用于 import 语句中,仅执行导入包中的 init 方法。也可以作为赋值语句的左边,表示该变量并不关心且不使用。
此外,标识符首字母的大小写,在GO语言中被用来控制变量或函数的访问权限,类似于其它语言的 publicprivate。
类型断言
比较特殊的表达式有类型断言,如果判断一个表达式 element 的类型是 T 的话,表达式为 element.(T),意思是 element 不为 nil 且存储在其中的值是T类型。这里有两种情况,如果 T 不是一个接口类型,则 element 必须要为接口类型的值,比如判断 100 是 int 型,不能使用 100.(int),而要用 interface{}(100).(int) ; 如果T 是一个接口类型,表示断言 element 实现了 T 这个接口。如果函数的参数是一个空接口,则必须断言传入的接口实现类型,才能使用其对应的方法。
可变参函数
最后一个参数为 ...T 的形式的函数即为可变参函数,意味着可变参数都是 T 类型(或实现了T的类型)如:func CallFunction(first string, t ...string),GO语言会在调用可变参函数时,创建一个切片,然后将这些可变参数放入切片中,但如果传入的可变参部分就是一个元素类型为T的切片的话,则直接把传入切片赋值给创建的切片,且在调用写法上也有区别,为: CallFunction("hello", []string{"x","y"}...)
数组类型
array: 声明一个长度为 n 、元素类型为 T 的数组为: [n]T, 元素类型可以为基本类型也可以为复合类型,也可以不指定 n ,由推导得出,如: [...]string{"a","b"} , 数组长度 n = len([...]string{"a","b"}),另外如果指定了数组长度,但定义的数组长度小于声明的长度,则以声明长度为准,不足的元素补默认值。同一元素类型,但数组长度不同,则视为不同类型。
切片类型
slice: 切片类型的声明为 []T数组,切片类型里没有关于长度的规定,其它跟数组一样,切片类型的零值是 nil。切片总是对应于一个数组,其对应的数组称为底级数组。切片和其底层数组的关系是引用关系,如果有修改都会影响到对方。
切片的数据结构包含了指向其底层数组的指针、切片长度和切片容量。切片的长度很容易理解,切片的容量是什么呢,它是切片第一个元素到底层数组最后一个元素的长度。
字典类型
map: 定义一个哈希表的格式为 map[K]V,其中 K 表示键的类型,V表示值的类型,如: map[string]bool{"IsOK":true, "IsError":false}
接口类型
定义了一组方法声明,接口中可以包含接口
GO语言对接口类型的实现是非侵入式的(备注:侵入式是指用户代码与框架代码有依赖,而非侵入式则没有依赖,或者说耦合),只要一个类型定义了某个接口中声明的所有方法,就认为它实现了该接口。
一个特殊的接口: interface{} 是一个空接口,不包含任何方法声明,所以GO语言所有的类型都可以看成是它的实现类型,我们就可以使用它实现类似其它语言中的公共基类的功能。比如声明一个字典,键是字符串,值是不确定类型,就可以使用 map[string]interface{}
判断一个类型是否实现了一个接口,可以通过类型断言来确定: _, ok := interface{}(MyType{}).(MyInterface)
函数与方法
GO语言中,函数跟方法是有区别的,函数就是我们通常理解的函数,而方法是附属于某个自定义数据类型的函数,即存在某个接收者。
func (self MyType) Len() int {} 这里的 (self MyInterface) 是表示方法接收者。
值方法和指针方法,值方法是指接收者是一个对象,而指针方法是指接收者是一个对象指针。两者的区别是,值方法中对接收者的改变是副本级别的,而指针方法中对接收者的改变是地址级别的。所以其实一般都推荐使用指针方法,因为大多数情况下我们在方法内部修改接收者,都是为了真实的改变它,而不是改变一个副本。但是,对于引用类型的接收者来说,两者并无区别。
匿名函数由函数字面量表示,函数是作为值存在的,可以赋给函数类型的变量。如:
var myfunc func(int, int) int myfunc = func(x, y int) (result int) { result = x + y return } log.Println(myfunc(3, 4))
一个方法的类型是一个函数类型,即把接收者放到函数的第一个参数位置即可。
非常遗憾,GO语言不支持函数和方法重载。
结构体
可以包含字段,也可以包含匿名字段,一般匿名字段是接口类型,这样就相当于该结构体包含了该接口中的方法,另外也可以在结构里重写隐藏接口中的方法。
指针
有一个专门用于存储内存地址的类型 unitptr,它与 int/unit 一样属于数值类型,存储32/64位无符号整数。
可以在一个类型前面使用*号,来表示对应的指针类型,也可以在可寻址的变量前面使用&,来获取其地址。
常量
定义常量和多个常量如下:
const PP = iota //0 const QQ = iota //0 const ( A = 1 B = 2 C D = iota E F ) log.Print(A, B, C, D, E, F) //输出是: 1 2 2 3 4 5
注意,iota 只能在 const 里使用,它是 const 的行数索引器,其值是所在const组中的索引值,单常量定义时 iota 就等于 0。另外,const组中某一项如果不赋值,则默认和上一个值一样,但如果前面是 iota ,则为上一个值+1。使用 iota 可以实现其它语言中的枚举。
变量
变量的声明语句以 var 开始,声明多个变量时,和声明多个const的方法相同。
var x string = "df" var x = "df" x := "df" //此为简写形式。
数据初始化
GO语言的数据初始化有两种方法,一是使用 new ,一是使用 make ,其中 new 是为某个类型分配内存,并设置零值后返回地址,如 new(T) 返回的就是指向T类型值指针值,即*T。如 new([3]int) 其实相当于 [3]int{0,0,0},所以它是一种很干净的内存分配策略。
make 只用于创建切片类型、字典类型和通道类型(注意这三个类型的特点,都是引用类型),它对这些类型创建之后,还会进行必要的初始化,与 new 不同,它返回的就是指T类型的值,而不是指针值。
定义常量的方式是使用 const ,如 const PI = 3.14,如果定义多个常量可以使用
编译器会自动选择在栈上还是在堆上分配局部变量的存储空间,但可能令人惊讶的是,这个选择并不是由用var还是new声明变量的方式决定的。 如果一个函数里声明一个局部变量,但是将其指针赋给一个全局变量,那么则不能将此局部变量放在栈中,而只能放在堆中,我们可以称之为该局部变量逃逸了,所以关于变量是分配在栈上还是堆上,是由编译器根据情况来选择的。
内建函数
close 只接受通道类型的值
len函数,可以应用于字符串、切片、数组、字典、通道类型
cap函数,可以应用于切片、数组、通道类型
new函数和make函数
append函数和copy函数,应用于切片
delete函数,根据字典的键删除某一项
complex ealimag函数,复数相关
panic 函数,异常相关,它的参数一般是某个error接口的某个实现类型;recover 函数,不接受任何参数,返回 interface{} 类型,也就是意味着,它可以返回任意类型。recover返回的内容是与panic相关的,就是panic的参数内容。
printprintln 函数,这两个函数支持基本类型参数,且是可变参数。但输出格式是固定的,且GO语言不保证以后会保留这两个函数,所以知道就好,不推荐使用。可以使用 fmt.Print 和 fmt.Println 来代替,效果更佳。
合并书写:
和 var/const 类似,多个 type 定义可合并成组,如下:
type ( Person struct { Name string Age int32 } myfunc func(string) bool )
尤其是在函数内部定义一堆临时类型时,可集中书写,可读性更好。
自增/自减运算符不同于其它语言,不能用作表达式,只能当独立的语句使用,且不能前置,只有后置形式,以尽可能避免该运算符带的复杂性问题。
unsafe.Pointer 与 uintptr 的区别:
前者类似 C 语言中的 void* 万能指针,能安全持有对象,但后者不行,后者只是一种特殊整型,并不引用目标对象,无法阻止垃圾回收器回收对象内存。