环境安装
go下载地址:golang.google.cn/dl/
goland下载地址:www.jetbrains.com.cn/go/
详细步骤是百度的,就不记录了。
开始学习
数据类型
基本数据类型
数值型
| 类型 | 描述 | 取值范围 |
|---|---|---|
| int | 有符号,32位或64位整型(根据操作系统而定) | 32位: -2^31 ~ 2^31-1 64位:-2^63 ~ 2^63-1 |
| int8 | 有符号,8位整型 | -2^7 ~ 2^7-1 |
| int16 | 有符号,16位整型 | -2^15 ~ 2^15-1 |
| int32 | 有符号,32位整型 | -2^31 ~ 2^31-1 |
| int64 | 有符号,64位整型 | -2^63 ~ 2^63-1 |
| uint | 无符号,32位或64位整型(根据操作系统而定) | 2位: 0 ~ 2^31-1 64位:0 ~ 2^63-1 |
| uint8 | 无符号,8位整型 | 0 ~ 2^7-1 |
| uint16 | 无符号,16位整型 | 0 ~ 2^15-1 |
| uint32 | 无符号,32位整型 | 0 ~ 2^31-1 |
| uint64 | 无符号,64位整型 | 0 ~ 2^63-1 |
| float32 | 有符号,单精度(转换为科学计数法后,精度大约为6位十进制数) | -3.403E38 ~ 3.403E38 |
| float64 | 有符号,双精度(转换为科学计数法后,精度大约为15位十进制数) | -1.798E308 ~ 1.798E308 |
| complex64 | 32位复数 | |
| complex128 | 64位复数 | |
| byte | 类似uint8 | |
| rune | 类似int32 | |
| uintptr | 无符号整型,用于存放一个指针 |
字符型
- 没有专门的字符型,一般用byte来保存单个字母字符
- 字符串:string
布尔型
- bool
复杂(派生)类型
指针
- 声明指针:
var name *type(name是指针变量名,type是指针类型,如int等) - 指针赋值:
name = &a(&a就是获取变量a的内存地址,即指针) - 使用指针:
fmt.Printf(*name)(*name访问name内存地址处的值) - 空指针:nil
- 指针数组:
var ptr [3]*int - 指向指针的指针:
var ptr **int
var a int
var ptr *int
var pptr **int
a = 100 // 变量
ptr = &a // 指针
pptr = &ptr // 指向指针的指针
- 指针作为函数参数:
func test(x, y *int) {}
数组
- 声明数组:
var name [SIZE]type(name是变量名,SIZE是数组长度,type是数组类型) - 数组赋值:
var a = [3]int{1, 2, 3} - 单个赋值:
a[0] = 4 - 不指定长度:
var a = []int{1, 2, 3}(根据赋值的长度确定数组长度,此处为3) - 取值:
a[0](a为变量名, 0是索引, 索引从0开始, 最多为数组长度 - 1) - 获取数组长度:
len(a) - 多维数组:
var name [SIZE1][SIZE2]...[SIZEN] type
var a = [3][2]int{{1, 2}, {3, 4}, {5, 6}} // 3行2列的数组
fmt.Printf("%d", a[0][0]) // 打印第一行第一列的值,1
- 向函数传递数组:
func main() {
var a = [5]int{1, 2, 3, 4, 5}
var b = []int{6, 7, 8}
foo(a)
bar(b)
}
func foo(param [5]int){ // 指定参数数组长度
fmt.Printf("%d\n", param[0])
}
func bar(param []int){ // 不指定参数数组长度
fmt.Printf("%d\n", param[0])
}
结构体
- 定义结构体:
type Test struct {
name string
age int
}
- 访问成员:
var test Test
test.name = "小明"
test.age = 20
fmt.Printf("姓名: %s, 年龄: %d", test.name, test.age)
- 结构体作为函数参数:
func testFunc(test Test){}
- 结构体指针
var ptr *Test
ptr = &test
fmt.Printf("姓名: %s, 年龄: %d", ptr.name, ptr.age)
函数
- 函数定义:
func function_name( [parameter list] ) [return_types]{ 函数体 }- function_name: 函数名;
- parameter list: 参数列表;
- return_types: 返回值类型;
- 函数体: 函数具体的实现
func add(a, b int) int {
return a + b
}
- 函数调用:
func main() {
var a, b int = 1, 2
var c = add(a, b)
fmt.Printf("%d", c)
}
- 函数返回多个值:
func swap(a, b int) (int, int) {
return b, a
}
func main() {
var a, b int = 1, 2
fmt.Printf("before swap: a=%d, b=%d\n", a, b)
a, b = swap(a, b)
fmt.Printf("after swap: a=%d, b=%d\n", a, b)
}
- 值传递和引用传递:go默认使用值传递(函数调用不会修改原来的值),如果需要使用引用传递,可以将参数设为指针
// 值传递
func swap(a, b int) {
var temp = a
a = b
b = temp
}
// 引用传递
func swap1(a, b *int) {
var temp = *a
*a = *b
*b = temp
}
func main() {
var a, b int = 1, 2
fmt.Printf("before swap: a=%d, b=%d\n", a, b)
swap(a, b)
// 此时打印的值跟上面是一样的
fmt.Printf("after swap: a=%d, b=%d\n", a, b)
swap1(&a, &b)
// 此时打印的是交换后的值
fmt.Printf("after swap: a=%d, b=%d\n", a, b)
}
- 函数作为值:
func main(){
i := 0
// 函数
increase := func() int {
i++
return i
}
/* 使用函数 */
fmt.Println(increase())
fmt.Println(increase())
fmt.Println(increase())
fmt.Println(increase())
// 打印结果: 1 2 3 4
}
- 闭包:匿名函数
// 返回一个匿名函数
func increase() (func() int) {
i := 0
return func() int {
i++
return i
}
}
func main() {
inc1 := increase()
fmt.Println(inc1())
fmt.Println(inc1())
fmt.Println(inc1())
// 打印结果 1 2 3
inc2 := increase()
fmt.Println(inc2())
fmt.Println(inc2())
// 打印结果 1 2
}
方法:Go 没有类。不过可以为结构体类型定义方法。方法就是带有接收者的函数。方法接收者在它自己的参数列表内,位于func关键字和方法名之间。
- 方法声明及调用:
// 定义结构体
type Circle struct {
radius float64
}
// 定义结构体的方法
func (c Circle) getArea() float64 {
//c.radius 即为 Circle 类型对象中的属性
return 3.14 * c.radius * c.radius
}
func main() {
var c1 Circle
c1.radius = 10.00
// 方法调用 c1.getArea()
fmt.Println("Area of Circle(c1) = ", c1.getArea())
}
切片
- 切片是对数组的抽象,可以理解为具有动态长度的数组
- 切片初始化:
// 直接初始化
s := []int{1,2,3}
// 引用数组初始化
// 将arr中从下标startIndex到endIndex-1 下的元素创建为一个新的切片,endIndex不能超过数组长度
s := arr[startIndex:endIndex]
// 从startIndex到len(arr) - 1
s := arr[startIndex:]
// 从0到endIndex
s := arr[:endIndex]
// 从0到len(arr) - 1
s := arr[:endIndex]
// 用make创建切片
// `make` 函数会分配一个元素为零值的数组并返回一个引用了它的切片,此切片未指定cap,即动态长度:len(a) = 5, cap(a) = 5, a = [0,0,0,0,0]
a := make([]int, 5)
// 要指定长度,可传入第三个参数:len(a) = 5, cap(a) = 10, a = [0,0,0,0,0]
// len是获取当前长度,cap是获取最大长度
a := make([]int, 5, 10)
- 空切片(nil):
var numbers []intlen(numbers) = 0, cap(numbers) = 0, numbers = [] - 追加:
func append(s []T, vs ...T) []T: 第一个参数 s 是一个元素类型为 T 的切片,其余类型为 T 的值将会追加到该切片的末尾,返回结果是一个包含原切片所有元素加上新添加元素的切片 - 复制:
func copy(dst, src []Type) int:第一个参数是新切片,第二个参数是原切片,代表将src的内容复制到dst,返回值是切片长度len(src) - 切片截取:
numbers[startIndex:endIndex]使用方法跟上面引用数组初始化一样
接口
- 接口就是一个抽象方法集(没有具体实现),其他类型实现了这些方法就相当于实现了接口,无需显式定义
type Phone interface {
call()
}
type NokiaPhone struct {
}
// 实现方法
func (nokiaPhone NokiaPhone) call() {
fmt.Println("I am Nokia, I can call you!")
}
type IPhone struct {
}
// 实现方法
func (iPhone IPhone) call() {
fmt.Println("I am iPhone, I can call you!")
}
func main() {
var phone Phone
phone = new(NokiaPhone)
phone.call()
phone = new(IPhone)
phone.call()
}
Map映射
- Map是用hash表实现的无序的键值对集合。
- 定义Map
// 使用map关键字,map1是变量名,string是key的数据类型,int是value的数据类型
var map1 map[string]int
// 使用make内建函数,map2是变量名,string是key的数据类型,int是value的数据类型
var map2 make(map[string]int)
// 以上两种方式都是创建了空(nil)Map,其中map关键字创建的是未初始化的,里面没有键值,也不能添加键;
// make会自动初始化,里面没有键,但可以添加键
// 初始化创建,等号前的类型可以忽略,go会进行类型推导
var map3 map[string]int = map[string]int{
"a": 1,
"b": 2,
}
- 元素操作
// 添加或修改
map[key] = value
// 删除
delete(map, key)
// 获取元素
var value = map[key]
// 判断是否存在:通过双赋值检测, 第二个参数ok为true则存在,否则不存在,默认是零值
// 零值: 数值类型为'0'; 布尔类型为'false'; 字符串类型为空字符串""
value, ok := map[key]
// 遍历:使用range关键字
for key := range map {
fmt.Println("key=%v, value=%v", key, map[key])
}
管道(channel)
- channel是go语言协程中数据通信的双向通道,类似于一个队列,遵循先进先出原则。管道涉及多线程的知识,这里先记录简单用法
- 管道定义
// 直接声明:管道类型就是管道保存的值的类型
var 变量名 chan 管道类型
// make声明
管道实例 := make(chan 数据类型)
// 直接声明的管道未初始化,无法使用,需要配合make函数初始化后才能使用
- 使用管道发送/接收数据: 一个协程发送数据,一个协程接收数据
c := make(chan int)
go func() {
for {
n := <- c //收数据
fmt.Println(n)
}
}()
c <- 1 //发数据
c <- 2
//防止数据还没有在协程里打印,main函数退出
time.Sleep(time.Millisecond)
- 关闭管道(关闭并非销毁):
close(c) - 判断管道是否关闭:
n, ok := <- cok为true是未关闭,否则已关闭for n := range c {}range在管道关闭时会自动退出循环
类型转换
表达式 T(v) 将值 v 转换为类型 T
i := 42
// 将i转换为float64类型
f := float64(i)
// 将f转换为uint类型
u := uint(f)
类型推导
在声明一个变量而不指定其类型时(即使用不带类型的 := 语法或 var = 表达式语法),变量的类型由右值推导得出。
// i为int
var i = 42
i := 42
// j 也为int
j := i
// k为float64
k := 3.14
// l为complex128
l := 0.867 + 0.5i // complex128
// 打印结果:i: int j: int k: int l: float64 m: complex128
fmt.Printf("i: %T\nj: %T\nk: %T\nl: %T\nm: %T", i, j, k, l, m)
常量声明
const关键字, 常量声明后无法再次修改其值
const a = 42