# 下载与安装
大家可以在 Go 语言官网下载 Windows 系统下的 Go 语言开发包。
下载 Windows 版本的 Go 语言开发包时尽量选择 MSI 格式,因为它可以直接安装到系统,不需要额外的操作。
# Hello World
# 脚本
| package main |
| import "fmt" |
| func main() { |
| fmt.Println("hello,world!") |
| } |
package
类似于 c# 中的 namespace
import
类似于 c# 中的 using
# 编译与运行
# 基础语法
# 数据类型
类型 | 描述 |
---|
布尔型 | 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
数字类型 | 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
字符串类型 | 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
派生类型 | (a) 指针类型(Pointer) (b) 数组类型 (c) 结构化类型 (struct) (d) Channel 类型 (e) 函数类型 (f) 切片类型 (g) 接口类型(interface) (h) Map 类型 |
# 数据声明
# 变量声明
类型在变量名之后
注意:函数内声明的变量如果没使用的话会导致编译报错
如果暂时用不到的话可以写 _=x
避免报错
# 函数声明
- 函数可以没有参数或接受多个参数,类型也在变量名之后,返回值也放在最后
| func add(x int, y int) int { |
| return x + y |
| } |
| func swap(x, y string) (string, string) { |
| return y, x |
| } |
| a, b := swap("hello", "world") |
简洁赋值语句 :=
用于类型推导,等价于 var
, 但只能在函数内使用
- Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。
| func split(sum int) (x, y int) { |
| x = sum * 4 / 9 |
| y = sum - x |
| return |
| } |
- 函数作为实参
可以声明一个变量为函数类型,作用于回调,与 js 类似
| |
| type cb func(int) int |
| func main() { |
| testCallBack(1, callBack) |
| testCallBack(2, func(x int) int { |
| fmt.Printf("我是回调,x:%d\n", x) |
| return x |
| }) |
| } |
| |
| func testCallBack(x int, f cb) { |
| f(x) |
| } |
| |
| func callBack(x int) int { |
| fmt.Printf("我是回调,x:%d\n", x) |
| return x |
| } |
- 闭包
闭包是指有权访问另一个函数作用域中变量的函数,并一直保存在闭包函数的内存中,与 js 的闭包一致
| func add() func() int{ |
| i:=0 |
| return func() int{ |
| i++; |
| return i |
| } |
| } |
| func main(){ |
| addNum1 := add() |
| fmt.Println(addNum1()) |
| fmt.Println(addNum1()) |
| |
| addNum2 := add() |
| fmt.Println(addNum2()) |
| fmt.Println(addNum2()) |
| } |
- defer 关键字
defer 语句会将函数推迟到外层函数返回之后执行。
| func main() { |
| defer fmt.Println("world") |
| |
| fmt.Println("hello") |
| } |
| |
| |
| |
defer 栈
推迟的函数调用会被压入一个栈中。当外层函数返回时,被推迟的函数会按照后进先出的顺序调用
| func main() { |
| fmt.Println("counting") |
| |
| for i := 0; i < 10; i++ { |
| defer fmt.Println(i) |
| } |
| |
| fmt.Println("done") |
| } |
| |
# 结构体声明
| type Books struct { |
| title string |
| author string |
| subject string |
| book_id int |
| } |
| |
| |
| Books{"aaa", "bbb", "ccc", 1} |
| Books{title: "aaa", author: "bbb", subject: "ccc", book_id: 1} |
| Books{title: "aaa", author: "bbb"} |
| |
| var Book1 Books |
| Book1.title = "aaa" |
| Book1.author = "bbb" |
| Book1.subject = "ccc" |
| Book1.book_id = 1 |
| |
| func (c Books) getName() string { |
| return c.title |
| } |
| |
| func main() { |
| var c1 Books |
| fmt.Println(c1.getName()) |
| } |
# 枚举声明
| const ( |
| Unknown = iota |
| Female |
| Male |
| ) |
- iota 的用法
每当 iota 在新的一行被使用时,它的值都会自动加 1
| const ( |
| a = iota |
| b |
| c |
| d = "ha" |
| e |
| f = 100 |
| g |
| h = iota |
| i |
| ) |
有趣的的 iota 实例
| const ( |
| i=1<<iota |
| j=3<<iota |
| k |
| l |
| ) |
# 条件语句
| if xxx{ |
| ... |
| }else if xxx{ |
| ... |
| }else{ |
| ... |
| } |
| switch var1 { |
| case val1: |
| ... |
| case val2: |
| ... |
| default: |
| ... |
| } |
go 的 case 结尾不需要加 break 也能跳出,与 c# 不同
fallthrough
关键字
使用 fallthrough 会强制执行后面的 case 语句,fallthrough 不会判断下一条 case 的表达式结果是否为 true。
| a := 1 |
| switch a { |
| case 1, 2, 3: |
| fmt.Println("true") |
| fallthrough |
| case 4: |
| fmt.Println("true") |
| default: |
| } |
| |
# 循环语句
| sum := 0 |
| for i := 0; i <= 10; i++ { |
| sum += i |
| } |
| for true { |
| if xxx |
| break |
| } |
- for 循环的遍历,range 格式可以对 slice、map、数组、字符串等进行迭代循环
看这
# 数组
| |
| balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} |
| |
| balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0} |
| |
| balance := [5]float32{1:2.0,3:7.0} |
| |
| values := [][]int{} |
| |
| row1 := []int{1, 2, 3} |
| row2 := []int{4, 5, 6} |
| values = append(values, row1) |
| values = append(values, row2) |
| |
| fmt.Println(values[0]) |
| fmt.Println(values[1]) |
| fmt.Println(values[0][0]) |
# 切片 (Slice)
切片是对数组的抽象,使数组长度可以动态变化,类似于 c# 中的 List
| slice1 := make([]type, len, capacity) |
len
: 数组长度, capacity
: 总容量,为可选参数
| |
| s :=[] int {1,2,3 } |
| |
| s := make([]int,3,5) |
| |
| var numbers []int |
| numbers := []int{0,1,2,3,4,5,6,7,8} |
| |
| fmt.Println(numbers[1:4]) |
| |
| fmt.Println(numbers[:3]) |
| |
| fmt.Println(numbers[2:]) |
用于获取切片的 len 和 capacity
| numbers := []int{0,1,2,3,4,5,6,7,8} |
| |
| numbers = append(numbers, 1) |
| |
| |
| numbers1 := make([]int, len(numbers), (cap(numbers))*2) |
| |
| copy(numbers1,numbers) |
# 集合 (map)
| |
| var map1 map[string]string |
| |
| var map1 = make(map[string]string) |
- delete () 函数
delete () 函数用于删除集合的元素,参数为 map 和其对应的 key
# 接口 (interface)
用于定义共性方法,具体实现交由不同结构体实现,类似于 c# 中的接口,但 go 中不存在 class,都交由 struct 实现
| type Person interface{ |
| getSex() int |
| } |
| |
| type Man struct{ |
| |
| } |
| |
| type Woman struct{ |
| |
| } |
| |
| func (man Man) getSex() int{ |
| return 1 |
| } |
| |
| func (woman Woman) getSex() int{ |
| return 0 |
| } |
| |
| func main(){ |
| var person Person |
| |
| person = new(Man) |
| person.getSex() |
| |
| person = new(Woman) |
| person.getSex() |
| } |
# 范围 (range)
range 关键字用于 for 循环中迭代数组 (array)、切片 (slice)、通道 (channel) 或集合 (map) 的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。
# map 遍历
| for key, value := range oldMap { |
| newMap[key] = value |
| } |
| |
| for key := range oldMap{ |
| |
| } |
| |
| for _, value := range oldMap{ |
| |
| } |
# slice 遍历
| var pow = []int{1, 2, 4, 8, 16, 32, 64, 128} |
| |
| for i, v := range pow { |
| fmt.Printf(i, v) |
| } |
# string 遍历
| str:="abc" |
| |
| for i, c := range str { |
| fmt.Println(i, c) |
| } |
# channel 遍历
| func fibonacci(n int, c chan int) { |
| x, y := 0, 1 |
| for i := 0; i < n; i++ { |
| c <- x |
| x, y = y, x+y |
| } |
| close(c) |
| } |
| func main() { |
| c := make(chan int, 10) |
| go fibonacci(cap(c), c) |
| |
| |
| |
| |
| for i := range c { |
| fmt.Println(i) |
| } |
| } |
# 指针
基本使用与 C++ 一致 (不想写了)
| var ptr *int |
| |
| a:=20 |
| ptr = &a |
| fmt.Printf(*ptr) |
# 类型转换
表达式 T (v) 将值 v 转换为类型 T。
| i := 42 |
| f := float64(i) |
| u := uint(f) |
# 并发
# goroutine
goroutine 是轻量级线程 (协程与线程的处理方式,详情自行百度 GMP), 由 Golang 运行时进行管理调度。
| func say(s string) { |
| for i := 0; i < 5; i++ { |
| time.Sleep(100 * time.Millisecond) |
| fmt.Println(s) |
| } |
| } |
| |
| func main() { |
| go say("a") |
| say("b") |
| } |
| |
# channel
通道(channel)是用来传递数据的一个数据结构,可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送 (c <-chan) 或接收 (c chan<-)。如果未指定方向,则为双向通道 (在作为函数形参时用来确保既能发送又能接收)
| func sum(s []int, c chan int) { |
| sum := 0 |
| for _, v := range s { |
| sum += v |
| } |
| c <- sum |
| } |
| |
| func main() { |
| s := []int{7, 2, 8, -9, 4, 0} |
| c := make(chan int) |
| go sum(s[:len(s)/2], c) |
| go sum(s[len(s)/2:], c) |
| x, y := <-c, <-c |
| fmt.Println(x, y, x+y) |
| |
| } |
- 通道缓冲区
缓存区数量的意义是通道接收到数据的数量,带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。
v, ok := <-ch
这个 ok 值判断通道是否关闭