基础组成
- 包声明
package main - 引入包
import "fmt" - 函数
- 变量
- 语句 & 表达式
- 注释
标记
一行代表一个语句结束。不像 C 以分号 ; 结尾
第一个字符必须是字母或下划线而不能是数字
//单行注释;/**/多行注释
字符串连接
字符串连接可以通过 + 实现:
package main
import "fmt"
func main() {
fmt.Println("hello" + "world")
}
格式化字符串
使用 fmt.Sprintf 或 fmt.Printf 格式化字符串并赋值给新串:
- Sprintf 根据格式化参数生成格式化的字符串并返回该字符串。
- Printf 根据格式化参数生成格式化的字符串并写入标准输出。
package main
import "fmt"
func main() {
// %d 表示整型数字,%s 表示字符串
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"//不需要&a,&会输出
var target_url=fmt.Sprintf(url,stockcode,enddate)
fmt.Println(target_url)
}
//输出:Code=123&endDate=2020-12-31
package main
import "fmt"
func main() {
// %d 表示整型数字,%s 表示字符串
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"
fmt.Printf(url,stockcode,enddate)
}
//输出:Code=123&endDate=2020-12-31
类型
var b bool=true//==bool b=true;
| append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
| copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
| int32 | int64 | iota | len | make | new | nil | panic | uint64 |
| println | real | recover | string | true | uint | uint8 | uintptr |
变量
声明变量
var identifier1, identifier2 type = value1, value2如果没有赋类型,会根据value自行判断类型 如果没有初始化,则变量默认为零值 如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误intVal := 1相等于:var intVal int =1
- 数值类型(包括complex64/128)为 0
- 布尔类型为 false
- 字符串为 ""(空字符串)
- 以下几种类型为 nil: var a *int var a []int var a map[string] int var a chan int var a func(string) int var a error // error 是接口
声明的值必须使用,但是全局变量允许声明但不使用 多变量可以在同一行进行赋值,如:
a, b, c := 5, 7, "abc"空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7中被抛弃。
常量
const identifier [type] = value
const (
Unknown = 0
Female = 1
Male = 2
)
iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。 iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
iota 可以被用作枚举值: const ( a = iota b = iota c = iota ) 第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
<<n==*(2^n)
运算符
- 同C语言
由上至下代表优先级由高到低:
| 优先级 | 运算符 |
|---|---|
| 5 | * / % << >> & &^ |
| 4 | + - | ^ |
| 3 | == != < <= > >= |
| 2 | && |
| 1 | | |
条件语句
if switch select 不支持 ?: 形式的条件判断
select
select 是 Go 中的一个控制结构,类似于 switch 语句。 select 语句只能用于通道操作,每个 case 必须是一个通道操作,要么是发送要么是接收。 select 语句会监听所有指定的通道上的操作,一旦其中一个通道准备好就会执行相应的代码块。 如果多个通道都准备好,那么 select 语句会随机选择一个通道执行。如果所有通道都没有准备好,那么执行 default 块中的代码。
Go 编程语言中 select 语句的语法如下:
select {
case <- channel1:
// 执行的代码
case value := <- channel2:
// 执行的代码
case channel3 <- value:
// 执行的代码
// 你可以定义任意数量的 case
default:
// 所有通道都没有准备好,执行的代码
}
以下描述了 select 语句的语法:
- 每个 case 都必须是一个通道
- 所有 channel 表达式都会被求值
- 所有被发送的表达式都会被求值
- 如果任意某个通道可以进行,它就执行,其他被忽略。
- 如果有多个 case 都可以运行,select 会随机公平地选出一个执行,其他不会执行。
否则:- 如果有 default 子句,则执行该语句。
- 如果没有 default 子句,select 将阻塞,直到某个通道可以运行;Go 不会重新对 channel 或值进行求值。
循环语句(For)
和 C 语言的 for 一样:
for init; condition; post { }
和 C 的 while 一样:
for condition { }
和 C 的 for(;;) 一样:
for { }
- init: 一般为赋值表达式,给控制变量赋初值;
- condition: 关系表达式或逻辑表达式,循环控制条件;
- post: 一般为赋值表达式,给控制变量增量或减量。
sum := 0
for i := 0; i <= 10; i++ {
sum += i
}
Go 语言 for 循环 | 菜鸟教程 (runoob.com) //没看完
函数
Go 语言函数定义格式如下:
func function_name( [parameter list] ) [return_types] {
函数体
}
- func:函数由 func 开始声明
- function_name:函数名称,参数列表和返回值类型构成了函数签名。
- parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
- 函数体:函数定义的代码集合。
切片
Go语言中的数组是值类型,当将一个数组赋值给另一个数组时,会将整个数组内容复制一份。这可能导致在处理大型数组时出现性能问题。 为了避免这种问题,可以使用切片(slice),切片是对数组的引用,它更灵活、更高效。
切片是对底层数组的一个视图,它包含了指向底层数组的指针、切片的长度和容量信息。由于切片是对数组的引用,因此修改切片中的元素会影响原始数组,反之亦然。切片支持自动扩容,当切片的容量不够时,Go会自动为其分配更大的底层数组,并将原有的数据复制到新的数组中。
创建切片有多种方式,其中最常见的是通过make函数:
slice := make([]int, 5) // 创建一个初始长度为5的整数切片
通过make函数,我们可以方便地创建指定类型和长度的切片,初始值都是该类型的零值。
另外,我们还可以通过切片字面量创建切片:
slice := []int{1, 2, 3, 4, 5} // 创建一个包含5个整数的切片
slice := []int{1, 2, 3, 4, 5}:这是一个切片的创建方式。在初始化时没有指定长度,只有一个空的方括号[],表示创建一个切片。切片的长度是根据初始值的个数来确定的,所以这里的切片slice长度是5,包含5个整数元素。
intArray := [5]int{10, 20, 30, 40, 50}:这是一个数组的创建方式。在初始化时,使用了固定长度的方括号[5],表示创建一个长度为5的整数数组。数组的长度在创建时就确定,无法更改。
切片可以通过索引来访问和修改元素,例如:
slice[0] = 10 // 修改第一个元素为10
fmt.Println(slice[3]) // 访问第四个元素的值
使用append追加元素:
slice := []int{1, 2, 3} slice = append(slice, 4)
通过append函数,我们可以向切片中追加新的元素。如果切片的容量不足,会进行扩容,返回一个新的切片,因此需要将append的结果赋值给原切片。
切片的切割操作:
slice := []int{1, 2, 3, 4, 5} subSlice := slice[1:4] // 取出第二个到第四个位置的元素,得到[2, 3, 4]
切片支持Python风格的切片操作,可以方便地截取出子切片。
==切片的优势:==
- 动态长度:切片的长度可以根据需要动态增加或缩减,相比于数组的静态长度,切片在处理不确定长度的数据时更为方便。
- 函数传参:切片在函数参数传递时,不会像数组一样复制整个数据,而是复制指向底层数组的指针、长度和容量信息,这样可以避免大量数据的复制,节省内存和提高性能。
- 灵活的使用方式:切片支持切割、追加、复制和连接等操作,使得切片在各种场景下都有广泛的应用。
另外:
- 切片的长度和容量: 切片的长度表示当前包含的元素个数,而容量表示切片扩容前的底层数组可以容纳的元素个数。切片的容量是自动扩容的,具体扩容策略是在长度不断增加时,当容量不够时,Go会自动为其分配更大的底层数组,并将原有的数据复制到新的数组中。
- 切片的底层原理: 切片是对底层数组的一个引用,它包含了指向底层数组的指针、切片的长度和容量信息。因此,当多个切片引用同一个底层数组时,它们会共享相同的数据。
map
也叫哈希或者字典
当我们创建一个 map 时,需要有两个类型,第一个是 key 的类型,第二个是 value 的类型。我们可以从里面去存取键值对,也可以用 delete 来删除键值对。Go语言中的 map 是完全无序的,因此遍历的时候不会按照顺序来遍历,而是随机的。
下面是 map 的使用记录。
map的创建和初始化:
在Go语言中,可以使用内置的make函数来创建和初始化map。
make函数接受两个参数:map的类型和初始容量(可选)。
例如:
// 创建一个键类型为string,值类型为int的空map
m := make(map[string]int)
// 创建一个键类型为string,值类型为string的map,并指定初始容量为10
m := make(map[string]string, 10)
- 存取键值对:
可以使用索引操作符[]来存取map中的键值对。例如:
m := make(map[string]int)
m["apple"] = 1 // 存储键值对
m["orange"] = 2
fmt.Println(m["apple"])
// 获取键值对中键为"apple"的值
fmt.Println(m["orange"])
// 获取键值对中键为"orange"的值
- 删除键值对:
使用内置的delete函数可以删除map中的键值对。例如:
m := make(map[string]int)
m["apple"] = 1
m["orange"] = 2
delete(m, "apple") // 删除键为"apple"的键值对
- 遍历
map:
map是无序的,因此遍历时无法保证顺序,用range来遍历map
m := map[string]int{
"apple": 1,
"orange": 2,
}
// 使用 range 关键字来迭代 Map,从而在每次循环迭代中获取键和对应的值。
for key, value := range m {
// 每次迭代会获取 Map 中的一个键值对,此时打印键值对的值
fmt.Println(key, value)
}
函数传指针才能改原来的值
func add2ptr(n *int) {
*n += 2
}
错误处理
在Go语言中,错误处理是一种常见的编程习惯,并且Go语言提供了一种简洁且明确的错误处理机制。
不同于 Java 使用的异常。go 语言的处理方式,能够很清晰地知道哪个函数返回了错误,并且能用简单的 if else 来处理错误
来看下面这个 findUser 函数:
import (
"errors"
"fmt"
)
type user struct {
name string
password string
}
func findUser(users []user, name string) (v *user, err error) {
for _, u := range users {
if u.name == name {
return &u, nil
}
}
return nil, errors.New("not found")
}
这个函数findUser接受一个users切片和name字符串作为参数,并返回一个*user类型的指针和一个error类型的错误。函数首先遍历users切片,查找是否有与name匹配的用户。如果找到了,就返回指向该用户的指针,同时error为nil表示没有错误。如果没有找到,就返回nil指针和一个自定义的not found错误。
错误处理示例:
u, err := findUser([]user{{"wang", "1024"}}, "wang")
if err != nil {
fmt.Println(err)
return
}
fmt.Println(u.name) // wang
if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
fmt.Println(err) // not found
return
} else {
fmt.Println(u.name)
}
这里展示了两种错误处理的情况:
第一种情况,调用findUser函数查找name为"wang"的用户。因为存在该用户,所以函数返回了指向该用户的指针,并且err为nil表示没有错误。所以输出结果为wang。 第二种情况,调用findUser函数查找name为"li"的用户。由于不存在该用户,所以函数返回了nil指针和一个自定义的错误not found。因此,在if err != nil条件下,我们输出了错误信息not found。