- 程序实体
任何Go语言源码文件都由若干个程序实体组成的,再Go语言中,变量,常量,函数,结构体和接口统称为“程序实体”,而它们的名字统称为“标识符”。
标识符可以是任何unicode编码可以表示的字母字符,数字以及下划线“_”。不过,首字母不能是数字或下划线。
- 关键字
- 变量和常量
变量使用关键字 var ,常量使用关键字const,常量只能被赋予基本数据类型本身
var num int = 1 //初始化赋值
var num int //只声明不赋值
var num1, num2 int = 1,2 //平行赋值
var( //多行赋值
num1 int = 1
num2 int = 2
)
- 整数的命名与宽度
1 byte = 8 bit
- 浮点数
浮点数类型有两个,float32和float64,分别需要的空间为4个字节和8个字节,在go语言里,浮点数相关部分只能由10进制表示法表示,而不能由6进制或者16进制来表示。
- 复数
复数类型同样有两个,即complex64和complex128.存储这两个类型的值得空间分别需要8个字节和16个字节,实际上,complex64类型的值会由两个float32类型的值分别表示复数的实数部分和虚数部分。而complex128的类型值会由两个float64类型的值分别表示复数的实数部分和虚数部分。
- byte 与 rune
byte与rune类型有一个共性,即:它们都属于别名类型。byte是uint8的别名类型,而rune则是int32的别名类型。
- 字符串
- 数组
一个数组(array)就是一个可以容纳若干类型相同的元素的容器。这个容器的大小(即数组的长度)是固定的,且是 体现在数组的类型字面量之中的。
声明: type myNumbers [3]int //由关键字type(var)、类型名称、和字面量组成
len是go语言的内建名称。该函数用于获取类型的长度,可以直接在go语言源码文件中直接使用它。
- 切片
- 切片(Slice)与数组一样,也是可以容纳若干类型相同的元素的容器。与数组不同的是,无法通过切片类型来确定其值得长度。每个切片值都会将数组作为其数据结构,我们也把这样的数组称为切片的底层数组。
- 切片类型的字面量如: []int or []string
- 切片表达式一般由字符串、数组或切片的值以及由方括号包裹且由英文冒号“:”分隔的两个正整数组成。这两个正整数分别表示元素下界索引和元素上界索引。被“切下”的部分不包含元素上界索引指向的元素。另外,切片表达式的求值结果会是切片类型的,且其元素类型与被“切片”的值的元素类型一致。
- 为了获取数组、切片或通道类型的值的容量,我们可以使用内建函数cap();
- 切片类型属于引用类型。它的零值即为
nil
,即空值。如果我们只声明一个切片类型的变量而不为它赋值,那么该变量的值将会是nil。
- test[1:4:4]: 这第三个正整数被称为容量上界索引。它的意义在于可以把作为结果的切片值的容量设置得更小。换句话说,它可以限制我们通过这个切片值对其底层数组中的更多元素的访问。
- 虽然切片值在上述方面受到了其容量的限制,但是我们却可以通过另外一种手段对其进行不受任何限制地扩展。这需要使用到内建函数
append
。append
会对切片值进行扩展并返回一个新的切片值。 - slice = append(slice, 6, 7) slice的值由 []int{2,3,4} 变为了 []int{2, 3, 4, 6, 7},一旦扩展操作超出了被操作的切片值的容量,那么该切片的底层数组就会被自动更换。这也使得通过设定容量上界索引来对其底层数组进行访问控制的方法更加严谨了。
copy():
该函数接受两个类型相同的切片值作为参数,并会把第二个参数值中的元素复制到第一个参数值中的相应位置(索引值相同)上。这里有两点需要注意: 1. 这种复制遵循最小复制原则,即:被复制的元素的个数总是等于长度较短的那个参数值的长度。2. 与append
函数不同,copy
函数会直接对其第一个参数值进行修改。
- 字典
- Go语言的字典(Map)类型其实是哈希表(Hash Table)的一个实现。字典用于存储键值对的无序集合。同一字典的键都是唯一的。如果放入名称相同的键,则与此键相关联的值会被替换。
- 字面量:map[K]T //K为键的类型,T为值得类型 具体声明为:map[int]string 字典的键类型必须是可以比较的,否则会引起错误。也就是说,它不能是切片、字典、函数类型,对于字典值来说,如果其中不存在索引表达式欲取出的键值对,那么就以它的值类型的空值(或称默认值)作为该索引表达式的求值结果。由于字符串类型的空值为
"",与切片类型相同,字典类型属于引用类型,它的零值为nil
。 - 字典初始化: mm := map[int]string{1:"a",2:"b",3:"c"}
- 赋值:给指定键赋值 mm[2] = "bb" 则键2对应的值替换为 "bb"
- 添加: mm[4] = "" 新加键值对 4:""
- 判断键上是否存在值: e,ok:mm[5] ok为bool类型 ok ? “存在”:“不存在”
- 删除键值: delete(mm,4) //有则删除 无则不操作
- 通道
- 通道(Channel)是Go语言中一种非常独特的数据结构。它可用于在不同Goroutine之间传递类型化的数据,并且是并发安全的。相比之下,我们之前介绍的那些数据类型都不是并发安全的。这一点需要特别注意。
- Goroutine(也称为Go程序)可以被看做是承载可被并发执行的代码块的载体。它们由Go语言的运行时系统调度,并依托操作系统线程(又称内核线程)来并发地执行其中的代码块。至于怎样编写这样的代码块以及怎样驱动这样的代码块执行。
- 声明:chan T //
chan为关键字,
而右边则是代表该通道类型允许传递的数据的类型(或称通道的元素类型) - 初始化:make(chan int,5) //
make
函数也可以被用来初始化切片类型或字典类型的值。通道值的长度应该被称为其缓存的尺寸。换句话说,它代表着通道值中可以暂存的数据的个数。注意,暂存在通道值中的数据是先进先出的,即:越早被放入(或称发送)到通道值的数据会越先被取出(或称接收)。 - 初始化: ch1 := make(chan string,5)
- 发送: ch1 <- "value1" //向通道
ch1
发送字符串"value1"
- 接收: value, ok := <- ch1 // 通道值的接收操作也可以有第二个结果bool值,这样做的目的同样是为了消除与零值有关的歧义。这里的变量
ok
的值同样是bool
类型的。它代表了通道值的状态,true
代表通道值有效,而false
则代表通道值已无效(或称已关闭)。更深层次的原因是,如果在接收操作进行之前或过程中通道值被关闭了,则接收操作会立即结束并返回一个该通道值的元素类型的零值。按照上面的第一种写法,我们无从判断接收到零值的原因是什么。不过,有了第二个结果值之后,这种判断就好做了。 - 关闭:close(ch1) //请注意,对通道值的重复关闭会引发运行时恐慌。这会使程序崩溃。所以一定要避免这种情况的发生。另外,在通道值有效的前提下,针对它的发送操作会在通道值已满(其中缓存的数据的个数已等于它的长度)时被阻塞。而向一个已被关闭的通道值发送数据会引发运行时恐慌。另一方面,针对有效通道值的接收操作会在它已空(其中没有缓存任何数据)时被阻塞。除此之外,还有几条与通道的发送和接收操作有关的规则。不过在这里我们记住上面这三条就可以了。最后,与切片和字典类型相同,通道类型属于引用类型。它的零值即为
nil
。 - 上面8条说的带缓冲的通道,或简称为缓冲通道。 通道有带缓冲和非缓冲之分。我们已经说过,缓冲通道中可以缓存N个数据。我们在初始化一个通道值的时候必须指定这个N。相对的,非缓冲通道不会缓存任何数据。发送方在向通道值发送数据的时候会立即被阻塞,直到有某一个接收方已从该通道值中接收了这条数据。
- 非缓冲通道初始化:make(chan int, 0)
- 我们还可以以数据在通道中的传输方向为依据来划分通道。默认情况下,通道都是双向的,即双向通道。如果数据只能在通道中单向传输,那么该通道就被称作单向通道。我们在初始化一个通道值的时候不能指定它为单向。
接收通道:type Receiver <-chan int 发送通道:type Sender chan<- int
单向通道的主要作用是约束程序对通道值的使用方式。比如,我们调用一个函数时给予它一个发送通道作为参数,以此来约束它只能向该通道发送数据。又比如,一个函数将一个接收通道作为结果返回,以此来约束调用该函数的代码只能从这个通道中接收数据。这属于API设计的范畴。
-
函数
在Go语言中,函数是一等(first-class)类型。这意味着,我们可以把函数作为值来传递和使用。函数代表着这样一个过程:它接受若干输入(参数),并经过一些步骤(语句)的执行之后再返回输出(结果)。特别的是,Go语言中的函数可以返回多个结果。
func(input1 string ,input2 string) string
函数类型的字面量由关键字func、由圆括号包裹参数声明列表、空格以及可以由圆括号包裹的结果声明列表组成。
- 结构体和方法
- 结构体(struct)
Go语言的结构体类型(Struct)比函数类型更加灵活。它可以封装属性和操作。前者即是结构体类型中的字段,而后者则是结构体类型所拥有的方法。
type Person struct { //结构体
Name string
Gender string
Age uint8
}
p := struct { //匿名结构体
Name string
Gender string
Age uint8
}{"Robert", "Male", 33}
包含若干字段和方法的结构体类型就相当于一个把属性和操作封装在一起的对象。不过要注意,与对象不同的是,结构体类型(以及任何类型)之间都不可能存在继承关系。
package main
import "fmt"
type Person struct {
Name string
Gender string
Age uint8
Address string
}
func (person *Person) Move(str string) string {
old := person.Address
person.Address = str
return old
}
func main() {
p := Person{"Robert", "Male", 33, "Beijing"}
oldAddress := p.Move("San Francisco")
fmt.Printf("%s moved from %s to %s.\n", p.Name, oldAddress, p.Address)
}
- 接口
在Go语言中,一个接口类型总是代表着某一种类型(即所有实现它的类型)的行为。一个接口类型的声明通常会包含关键字type
、类型名称、关键字interface
以及由花括号包裹的若干方法声明。
type Animal interface {
Grow()
Move(string) string
}
- 指针
当地址操作符&
被应用到一个值上时会取出指向该值的指针值,而当地址操作符*
被应用到一个指针值上时会取出该指针指向的那个值。它们可以被视为相反的操作。
方法的接收者标识符所代表的是该方法当前所属的那个值的一个副本,而不是该值本身。
一个指针类型拥有以它以及以它的基底类型为接收者类型的所有方法,而它的基底类型却只拥有以它本身为接收者类型的方法。