# 基本类型

bool string

有符号整数

int8,int16,int32,int64

无符号整数

uint8,uint16,uint32,uint64

byte =uint8的别名

rune =int32的别名,表示一个Unicode码点

平台相关性类型

类型 32位系统 64位系统
int int32 int64
uint uint32 uint64
uintptr uint32 uint64

浮点数类型

float32

float64

复合类型

complex64,complex128

结构体

type [名称] struct{}

自定义类型

type CusInt int

指针类型

*type

数组

[n]T ,go语言中数组是固定大小的

切片

[]T,切片与数组几乎一样,唯一不同的是切片大小是不固定的,数组可以很容易转换成切片,反过来则不行

# 方法和接口

方法即函数

无论是面向对象还是面向过程都离不开方法,在go中定义个方法很简单:

// 再任何一个go文件中如下代码就定义了一个方法
func Method(){
}
//go支持方法有多个返回值,当你不关心其中某个返回值时可以使用_忽略它
func Method() (string,error){
    return "",nil
}
// 忽略返回值
_,_:=Method()
1
2
3
4
5
6
7
8
9
10
11

可变参数的方法

// ...type表示方法接收一个type类型的数组,且数组的大小是可变的,由调用者传入参数的个数决定
func Method(args ...type){
    print(len(args))   
}
1
2
3
4

接口

虽然go没有class的概念,但interface的概念得以保留,接口类型通常用于行为约束,在go中也不例外,不过接口在go中一个很有用的地方就是它可以作为任意类型(空接口)使用,如下:

// 直接使用interface关键字定义任意类型变量
var i interface{}=1
var s interface{}="string"(i)
//但i,s变量并不能直接使用,以下是错误用法
print(i)
print(s)
//正确的用法如下:
i:=i.(int)  //这种方式在go中叫做断言,如果i不是int类型则导致程序崩溃(在switch语句中除外)
//通常,如下的方法是更安全的做法,参数ok用来表明断言是否成功
i,ok:=i.(int)
if(ok){
    print(i)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

在go中用interface{}声明的变量,返回的是变量的地址即指针类型,如果要获得指针指向的具体值需要通过i.(type)即断言的方式。

声明一个接口类型

在go中编译器并不强制子类实现接口,也就是说这一实现过程是隐式完成的

// 这里定义了一个People接口类型,并指定该接口有一个Name方法的约束
type People interface{
    Name()
}
1
2
3
4

实现接口

type ManPeople struct{}
func (people ManPeople) Name(){}
// 如果ManPeople没有实现People的Name方法则不能通过编译前代码检查
var people People=ManPeople{}
1
2
3
4
5

通过接口实现方法可变任意类型的参数

// 该Method方法将接收任意个任意类型的参数
func Method(args ...interface{}){
}
// 该Method仅接收任意个数的People类型的参数
func Method(args ...People){
}
1
2
3
4
5
6
7
8

面向对象

在go中没有class的概念,通常我们的代码都是从struct开始的,模拟一个类在go中也是很容易的

// 首先我们定义一个结构体,结构体描述了数据在内存中是如何存储的
type People struct{
    Name string
}
// 通过如下放方式我们可以创建一个实例
people:=People{}
people.Name="Name"
1
2
3
4
5
6
7

# 并发

goroutine协程

go中没有提供线程的概念,而是提供了协程的概念,协程是运行再线程上的最小执行单元

协程优点:比线程省资源,协程由go内部自己协调调用

原理:用少量的线程模拟大量的协程

# go中创建一个协程是非常简单的
go func(){}
# 创建一个协程并持续监听协程内的状态变化
func main() {
	c := func () chan int  {
		cTemp:= make(chan int)
		go func ()  {
			for i := 0; i < 10; i++ {
				cTemp<-i
			}
		}()
		return cTemp
	}()
	for cv:=range c{
		log.Println(cv)
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

信道

信道类似现实生活中的管道,一段进一端出,go中如何定义信道

# 语法
make(chan type,size)
# 定义一个int类型信道
c:=make(chan int)
# 定义一个int类型且带缓冲区的信道
c:=make(chan int,10)
# 将数据写入信道
c<-1
# 从新到读取
cv<-c
# 从信道读取,如果信道未关闭,如果isOpen为true则可以读取cv的值,为false说明信道关闭了
cv,isOpen<-c
1
2
3
4
5
6
7
8
9
10
11
12

range,close

使用for range我们可以持续从信道中读取数据,使用close关闭信道

func main() {
	c := make(chan int)
	go func(cTemp chan int) {
		for index := 0; index < 10; index++ {
			// 将数据持续写入信道
			cTemp <- index
			time.Sleep(time.Second)
		}
		// 关闭信道
		close(cTemp)
	}(c)
	// 持续接收信道内的值
	for v := range c {
		log.Println(v)
	}
	log.Println("信道被关闭,结束程序")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

select

select用于从多个信道中读取数据,或者说根据不同的信道触发不同的代码逻辑

func main() {
	c1 := make(chan int)
	c2 := make(chan string)
	go func(cTemp chan int) {
		for index := 0; index < 10; index++ {
			cTemp <- index
			time.Sleep(time.Second)
		}
	}(c1)
	go func(cTemp chan string) {
		for index := 0; index < 10; index++ {
			cTemp <- fmt.Sprintf("%d", index)
			time.Sleep(time.Second)
		}
	}(c2)
	for {
		// 使用select选择信道
		select {
		case cv1 := <-c1:
			log.Println("c1:", cv1)
		case cv2 := <-c2:
			log.Println("c2:", cv2)
		// 当没有信道可选择时,执行default
		default:
			log.Println("default")
			time.Sleep(time.Second)
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

sync.Mutex

并发编程中的同步,更其它语言一样go也提供了同步操作,而且非常简单

var mutex sync.Mutex
var index int
func main() {
	for i := 0; i < 10; i++ {
		go addIndex()
	}
}
func addIndex() {
	// 使用锁,保证一个时刻只能一个协程修改index的值
	mutex.Lock()
	defer mutex.Unlock()
	index++
	log.Println(index)
	time.Sleep(time.Second)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18