变量声明
Go语言中的变量需要声明以后才可以使用(需要提前定义变量)并且声明后必须使用(不适用会报错)
标准声明
var 变量名 变量类型
example:
var name string
var id int
var isOk bool
多变量声明可以整合在一起
var (
name string
id int
isOk bool
)
变量初始化
Go语言在声明变量的时候,会自动对变量对应的内存区进行初始化操作。
var 变量名 变量类型 = 表达式
example:
var name string = "A2rcher"
var id int = 123
类型推断
Go语言提供了一种简易的方式,不需要提供变量类型,可以根据后面的初始化操作后自动推断其变量类型
var name = "A2rcher"
var id = 123
短变量声明
:=
package main
import (
"fmt"
)
var m = 100 //全局变量
func main(){
n:=100
m:=300//声明并初始化局部变量
fmt.Println(m, n)
}
匿名变量
_
package main
import "fmt"
func foo() (string, int) {
return "A2rcher", 20
}
func main() {
x, _ := foo()
_, y := foo()
fmt.Println("x=", x, "y=", y)
}
常量
const
const p = 3.1415
const e = 2.7182
多常量声明可以整合在一起
const (
p = 3.1415
e = 2.7182
)
iota常量计算器
iota是Go语言中的常量计算器,在常量表达式中使用。
package main
import "fmt"
const (
n1 = iota
n2
n3
n4
)
func main() {
fmt.Println(n1)//0
fmt.Println(n2)//1
fmt.Println(n3)//2
fmt.Println(n4)//3
}
iotaiota
_
package main
import "fmt"
const (
n1 = iota//0
_ //1
n2 //2
n3 //3
n4 //4
)
func main() {
fmt.Println(n1)//0
fmt.Println(n2)//2
fmt.Println(n3)//3
fmt.Println(n4)//4
}
数据类型
整型
类型 | 描述 |
---|---|
uint8 | 无符号 8位整型 (0 到 255) |
uint16 | 无符号 16位整型 (0 到 65535) |
uint32 | 无符号 32位整型 (0 到 4294967295) |
uint64 | 无符号 64位整型 (0 到 18446744073709551615) |
int8 | 有符号 8位整型 (-128 到 127) |
int16 | 有符号 16位整型 (-32768 到 32767) |
int32 | 有符号 32位整型 (-2147483648 到 2147483647) |
int64 | 有符号 64位整型 (-9223372036854775808 到 9223372036854775807) |
特殊整型
uint32uint64int32int64
浮点型
float32float64IEEE 754float323.4e38math.MaxFloat32float641.8e308math.MaxFloat64
fmt%f
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("%f\n", math.Pi)
fmt.Printf("%.2f\n", math.Pi)
}
复数
complex64和complex128
var c1 complex64
c1 = 1 + 2i
var c2 complex128
c2 = 2 + 3i
fmt.Println(c1)
fmt.Println(c2)
复数有实部和虚部,complex64的实部和虚部为32位,complex128的实部和虚部为64位。
布尔
字符串
byte和rune
运算符
运算符 | 描述 |
---|---|
+ | 相加 |
- | 相减 |
* | 相乘 |
/ | 相除 |
% | 求余 |
运算符 | 描述 |
---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 |
运算符 | 描述 |
---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则为 True,否则为 False。 |
|| | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则为 True,否则为 False。 |
! | 逻辑 NOT 运算符。 如果条件为 True,则为 False,否则为 True。 |
运算符 | 描述 |
---|---|
& | 参与运算的两数各对应的二进位相与。 (两位均为1才为1) |
| | 参与运算的两数各对应的二进位相或。 (两位有一个为1就为1) |
^ | 参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (两位不一样则为1) |
<< | 左移n位就是乘以2的n次方。 “a<<b”是把a的各二进位全部左移b位,高位丢弃,低位补0。 |
>> | 右移n位就是除以2的n次方。 “a>>b”是把a的各二进位全部右移b位。 |
运算符 | 描述 |
---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 |
+= | 相加后再赋值 |
-= | 相减后再赋值 |
*= | 相乘后再赋值 |
/= | 相除后再赋值 |
%= | 求余后再赋值 |
<<= | 左移后赋值 |
>>= | 右移后赋值 |
&= | 按位与后赋值 |
|= | 按位或后赋值 |
^= | 按位异或后赋值 |
流程控制
if else
for 初始语句;条件表达式;结束语句{
循环语句
}
example:
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}
;
package main
import "fmt"
var i = 0
func main() {
for ; i < 10; i++ {
fmt.Println(i)
}
}
for循环的结束语句也是可以省略掉的
package main
import "fmt"
var i = 0
func main() {
for i < 10 {
fmt.Println(i)
i++
}
}
for range(键值循环)
键值循环可以用作与遍历,切片,map还有channel。遵循一下规律
- 数组、切片、字符串返回索引和值。
- map返回键和值。
- 通道(channel)只返回通道内的值。
switch
goto(跳转指定标签)
goto的使用可以这么理解,在一个循环中当运行到某一个位置时我不想让他继续运行下去而是跳到其他的地方,这个时候就可以使用goto,用来简化一些代码的实现过程。
func gotoDemo1() {
var breakFlag bool
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
breakFlag = true
break
}
fmt.Printf("%v-%v\n", i, j)
}
// 外层for循环判断
if breakFlag {
break
}
}
}
goto
func gotoDemo2() {
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
goto breakTag
}
fmt.Printf("%v-%v\n", i, j)
}
}
return
// 标签
breakTag:
fmt.Println("结束for循环")
}
break
跳出循环
continue
继续下次循环
数组
数组定义
var 变量名 [元素数量]T
var a [3]int
var b [4]string
数组初始化
package main
import "fmt"
func main() {
var testArray [3]int
var numArray = [3]int{1, 2, 3}
var strArray = [3]string{"a", "b", "c"}
fmt.Println(testArray)
fmt.Println(numArray)
fmt.Println(strArray)
}
使用[...]可以根据初始值自动判断元素有多少
package main
import "fmt"
func main() {
var testArray [3]int
var numArray = [...]int{1, 2, 3,4,5}
var strArray = [...]string{"a", "b", "c", "d","f","v"}
fmt.Println(testArray)
fmt.Println(numArray)
fmt.Println(strArray)
}
数组遍历
for循环遍历
package main
import "fmt"
func main() {
var testArray [3]int
var numArray = [...]int{1, 2, 3, 4, 5}
var strArray = [...]string{"a", "b", "c", "d", "f", "v"}
fmt.Println(testArray)
fmt.Println(numArray)
fmt.Println(strArray)
for i := 0; i < len(numArray); i++ {
fmt.Println(i)
}
for v := 0; v < len(strArray); v++ {
fmt.Println(strArray[v])
}
}
for range遍历
package main
import "fmt"
func main() {
var testArray [3]int
var numArray = [...]int{1, 2, 3, 4, 5}
var strArray = [...]string{"a", "b", "c", "d", "f", "v"}
fmt.Println(testArray)
fmt.Println(numArray)
fmt.Println(strArray)
for i, v := range numArray {
fmt.Println(i, v)
}
fmt.Println("----------------------------------------------------")
for m, n := range strArray {
fmt.Println(m, string(n))
}
}
运行结果可以明显看出他两个是有区别的。
for循环遍历的结果并没有把角标打印出来,而是直接出的结果。
for range循环遍历的结果时把对应的角标也打印了出来,所以我们可以改善一下,使用匿名变量可以让for range的结果for循环的结果一样。
package main
import "fmt"
func main() {
var testArray [3]int
var numArray = [...]int{1, 2, 3, 4, 5}
var strArray = [...]string{"a", "b", "c", "d", "f", "v"}
fmt.Println(testArray)
fmt.Println(numArray)
fmt.Println(strArray)
for _, v := range numArray {
fmt.Println(v)
}
fmt.Println("----------------------------------------------------")
for _, n := range strArray {
fmt.Println(string(n))
}
}
同样的原理,可以对多为数组进行遍历。
切片
切片跟数组很像,但是切片相当于在数组类型的基础上做了一层封装,相较于数组可以更快的操作一块数据集合。
切片定义
var 变量名 [元素个数]T
可以看到他的定义方式跟数组一模一样,所以他的初始化也是一样的。
切片长度和容量
用内置函数len(),cap()可以求出切片的长度容量
package main
import "fmt"
func main() {
var test1Slice [3]int
fmt.Println(len(test1Slice))
fmt.Println(cap(test1Slice))
var test2Slice = [...]string{"a", "b", "c", "d", "e", "f"}
fmt.Println(len(test2Slice))
fmt.Println(cap(test2Slice))
}
表达式
切片表达式中有low和high两个定义来表示切片中的界限值
func main() {
a := [5]int{1, 2, 3, 4, 5}
s := a[1:3] // s := a[low:high]
fmt.Printf("s:%v len(s):%v cap(s):%v\n", s, len(s), cap(s))
}
a[2:] // 等同于 a[2:len(a)]
a[:3] // 等同于 a[0:3]
a[:] // 等同于 a[0:len(a)]
make()函数构造切片
make([]T,元素数量,切片容量)
example:
func main(){
a := make([]int,2,10)
}
参考
判断切片是否为空
len(s)==0
append()方法添加元素
append()方法为切片同台添加元素,可以一次添加一个或者多个,还可以添加另一个其他切片中的元素。
package main
import "fmt"
func main() {
var s []int
s = append(s, 1)
s = append(s, 2, 3, 4)
s2 := []int{2, 3, 4}
s = append(s, s2...)
fmt.Println(s)
}
map使用
key-value
map定义
map[keyType]valueType
- KeyType:表示键的类型。
- ValueType:表示键对应的值的类型。
map初始化
map默认初始值为nil,使用make()函数分配内存初始化:
make(map[keyType]valueType,[cap])
其中cap表示map的容量,该参数虽然不是必须的,但是我们应该在初始化map的时候就为其指定一个合适的容量。
map使用
package main
import "fmt"
func main() {
sourceMap := make(map[string]int, 20)
sourceMap["A2rcher"] = 20
sourceMap["emo"] = 30
fmt.Println(sourceMap)
fmt.Printf("type %T\n", sourceMap)
}
map可以在声明的时候就填充元素:
package main
import "fmt"
func main() {
sourceMap := make(map[string]int, 20)
sourceMap["A2rcher"] = 20
sourceMap["emo"] = 30
fmt.Println(sourceMap)
fmt.Printf("type %T\n", sourceMap)
fmt.Println("***************")
sourceTest := map[string]int{
"lucher": 20,
"xs":10,
}
fmt.Println(sourceTest)
}
判断键值对是否存在
package main
import "fmt"
func main() {
sourceMap := make(map[string]int, 20)
sourceMap["A2rcher"] = 20
sourceMap["emo"] = 30
fmt.Println(sourceMap)
fmt.Printf("type %T\n", sourceMap)
fmt.Println("***************")
sourceTest := map[string]int{
"lucher": 20,
"xs": 10,
}
fmt.Println(sourceTest)
fmt.Printf("type %T\n", sourceTest)
fmt.Println("______________________________")
v, ok := sourceMap["emo"]
if !ok {
fmt.Println("查无此人")
} else {
fmt.Println(v)
}
}
map遍历
for range遍历
package main
import "fmt"
func main() {
sourceMap := make(map[string]int, 20)
sourceMap["a"] = 90
sourceMap["b"] = 100
sourceMap["c"] = 60
for k, v := range sourceMap {
fmt.Println(k, v)
}
}
如果只想遍历前面的key时,可以把v省略。但要是想遍历value时,就需要匿名变量的帮助了。
package main
import "fmt"
func main() {
sourceMap := make(map[string]int, 20)
sourceMap["赵"] = 90
sourceMap["钱"] = 100
sourceMap["孙"] = 60
for k, v := range sourceMap {
fmt.Println(k, v)
}
for k := range sourceMap {
fmt.Println(k)
}
for _, m := range sourceMap {
fmt.Println(m)
}
}
delete()函数删除键值对
元素为map类型的切片
这个可以理解为在切片里面,各个元素得类型是map。
例如:
var a = make([]map[string]int,3)
值为切片类型的map
这个可以理解为在map函数里面key值是切片。
例如:
var a1 = make(map[string][]int,3)
函数
函数.最重要的一部分
函数定义
func 函数名(参数)(返回值){
函数体
}
example:
func TestFunction(x int,y,int)int{
return x + y
}
对于参数部分如果两个参数类型是一样的可以简化
func TestFunction(x,y int)int{
return x + y
}
可变参数
...
返回值
()
func calc(x, y int) (int, int) {
sum := x + y
sub := x - y
return sum, sub
}
返回值命名
return
例如:
func calc(x, y int) (sum, sub int) {
sum = x + y
sub = x - y
return
}
返回值补充
当我们的一个函数返回值类型为slice时,nil可以看做是一个有效的slice,没必要显示返回一个长度为0的切片。
func someFunc(x string) []int {
if x == "" {
return nil // 没必要返回[]int{}
}
...
}
全局变量
在函数外定义的变量,函数内可以访问到全局变量
局部变量
在函数内定义的变量,只能在函数内访问得到。
注意:
如果局部变量和全局变量成名,有限访问局部变量。
定义函数类型
type 函数类型 func(int ,int) int
函数作为参数
package main
import "fmt"
func add(x, y int) int {
return x + y
}
func calc(x, y int, op func(int, int) int) int {
return op(x, y)
}
func main() {
ret2 := calc(10, 20, add)
fmt.Println(ret2)
}
函数作为返回值
func do(s string) (func(int, int) int, error) {
switch s {
case "+":
return add, nil
case "-":
return sub, nil
default:
err := errors.New("无法识别的操作符")
return nil, err
}
}
匿名函数
匿名函数就是没有名字的函数,可以把函数赋值给变量,也可以把函数作为返回值。
func(参数)(返回值){
函数体
}
匿名函数没有办法像普通函数那样子调用,他需要保存在某一个变量中(就是赋值),然后在执行。
package main
import "fmt"
func test() {
func () {
fmt.Println("匿 名 函 数")
}()
}
func main() {
test()
}
匿名函数执行:
func test() {
func () {
fmt.Println("匿 名 函 数")
}()//在后面加上括号就相当于执行
}
或者赋值:
package main
import "fmt"
func test() func() {
return func() {
fmt.Println("匿 名 函 数")
}
}
func main() {
r := test()
r()
}
闭包
闭包=函数+引用环境
package main
import "fmt"
//闭包简单示例 //闭包概念 闭包=函数+外层变量的引用
func test() func() {
name := "A2rcher"
return func() {
fmt.Println("匿 名 函 数",name) // 如果在匿名函数中找不到调用的变量,他就会向外层去找
} //这个外层变量并不是全局变量,而是函数test()中的局部变量
}
func main() {
r := test() // r引用了函数test()中的变量还有匿名函数 ,可以说r此时就是一个闭包
r()
}
闭包还可以这么写(比较典型的一个例子)
package main
import "fmt"
//闭包简单示例 //闭包概念 闭包=函数+外层变量的引用
func test(name string) func() {
return func() {
fmt.Println("匿 名 函 数", name)
}
}
func main() {
r := test("A2rcher")
r()
}
example:
panic & recover & defer
panic/recover:可以理解为异常处理模式(但是Go语言中并没有异常处理机制,只是这样方便理解),
package main
import "fmt"
//panic and recover
func a() {
fmt.Println("func is a")
}
//recover必须配合defer使用,而且defer一定要在panic前定义。
func b() {
defer func() {
err := recover()
if err != nil { //如果err不等于nil,说明这个程序出错
fmt.Println("func b is err ")
}
}()
panic("func is b")
}
func c() {
fmt.Println("func is c")
}
func main() {
a()
b()
c()
}
funcBpanicrecover
内置函数
内置函数 | 介绍 |
---|---|
close | 主要用来关闭channel |
len | 用来求长度,比如string、array、slice、map、channel |
new | 用来分配内存,主要用来分配值类型,比如int、struct。返回的是指针 |
make | 用来分配内存,主要用来分配引用类型,比如chan、map、slice |
append | 用来追加元素到数组、slice中 |
panic和recover | 用来做错误处理 |
指针
&*
结构体
类型别名和自定义类型
type定义关键字类型
type myInt int
//将myInt定义成int类型
结构体定义
type 类型名 struct{
字段名 字段类型
字段名 字段类型
...
}
struct
- 类型名:标识自定义结构体的名称,在同一个包内不能重复。
- 字段名:表示结构体字段名。结构体中的字段名必须唯一。
- 字段类型:表示结构体字段的具体类型。
很简单的例子:
type Person struct{
name string
address string
age int
sex string
}
自定义一个Person类型,他有name,address,age,sex四个字段分别代表个自内容。如果我要使用Person中某一个字段,我可以直接调用Person就行了。
结构体实例化
结构体实例化后才可以分配内存使用(实例化后才可以使用自定义类型)
由于结构体本身就是一共类型,所以在声明的时候可以像声明变量一样声明结构体
var 结构体实例 结构体类型
example:
package main
import "fmt"
type Person struct {
name string
age int
}
func main() {
var p1 Person
//通过.来访问结构体的字段(成员变量),例如p1.name和p1.age等。
p1.name = "A2rcher"
p1.age = 20
fmt.Println(p1)
}
匿名结构体
匿名结构体是用来处理一些临时的数据,比如说我现在A,B两个函数,但是我临时需要使用c数据,这个时候可以用到匿名结构体。
package main
import "fmt"
type Person struct {
name string
age int
}
func main() {
var p1 Person
p1.name = "A2rcher"
p1.age = 20
var user struct {
Name string `json:"name"`
Age int `json:"age"`
}
user.Age = 30
user.Name = "emo"
fmt.Println(p1)
fmt.Println(user)
}
指针类型结构体
new