示例:
package calc
func Add(a int, b int) int {
return a + b
}
package main
import (
"awesomeProject/calc"
)
func main() {
println(calc.Add(1, 2))
}
所有go源码以.go结尾
标识符以字母或下划线开头,大小写敏感
可以用 _ 来接收对象,它是特殊的标识符,用来忽略结果
_ = calc.Add(1, 2)
保留关键字:
break | default | func | interface | select |
---|---|---|---|---|
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthough | if | range | type |
continue | for | import | return | var |
说明:
var和const :变量和常量的声明
var varName type 或者 varName : = value
package and import: 包和导入
func: 用于定义函数和方法
return :用于从函数返回
defer someCode :在函数退出之前执行
go : 用于并行
select 用于选择不同类型的通讯
interface 用于定义接口
struct 用于定义抽象数据类型
chan用于channel通讯
type用于声明自定义类型
map用于声明map类型数据
range用于读取slice、 map、 channel数据
break、 case、 continue、 for、 fallthrough、 else、 if、 switch、 goto、 default 流程控制
fallthrough:
1.加了fallthrough后,会直接运行 紧跟的后一个 case或default语句,不论条件是否满足都会执行
2.加了fallthrough语句后, 紧跟的后一个 case条件不能定义常量和变量
3.执行完fallthrough后直接跳到下一个条件语句,本条件执行语句后面的语句不执行
str := "hello world,中国"
for i, v := range str {
fmt.Printf("index[%d] val[%c]\n", i, v)
}
package main
import “fmt”
func main() {
fmt.Println(“hello, world”)
}
任何一个代码文件隶属于一个包
import 关键字,引用其他包
golang可执行程序, package main,并且有且只有一个main入口函数
包中函数调用:同一个包中函数,直接调用;不同包中函数,通过 包名.函数名 进行调用
包访问控制规则:大写意味着这个函数/变量是可导出的;小写意味着这个函数/变量是私有的,包外部不能访问
常量使用const 修饰,代表永远是只读的,不能修改
const 只能修饰boolean, number(int相关类型、浮点类型、 complex)和string
语法: const identifier [type] = value,其中type可以省略
const b string = “hello world”
const b = “hello world”
const (
a = 0
b = 1
c = 2
)
const (
a = iota//0
b //1
c //2
)
变量定义语法: var identifier type
在函数内部,可以使用更简略的 := 方式声明并初始化变量,但是不能用于全局变量声明
var a string="hello"
var(
b int //默认为0
c string//默认为""
d bool //默认为false
)
在函数内部声明的变量叫做局部变量,生命周期仅限于函数内部
在函数外部声明的变量叫做全局变量,生命周期作用于整个包,如果是大写的,则作用于整个程序。
Go 是强类型语言,因此不会进行隐式转换,任何不同类型之间的转换都必须显式说明
Go 不存在像 Cpp 那样的运算符重载,表达式的解析顺序是从左至右
Go 对于值之间的比较有非常严格的限制,只有两个类型相同的值才可以进行比较,如果值的类型是接口,它们也必须都实现了相同的接口。如果其中一个值是常量,那么另外一个值的类型必须和该常量类型相兼容的。如果以上条件都不满足,则其中一个值的类型必须在被转换为和另外一个值的类型相同之后才可以进行比较。
只能存true和false
Go 也有基于架构的类型,例如:int
、uint
和 uintptr
。
这些类型的长度都是根据运行程序所在的操作系统类型所决定的:
int
和 uint
在 32 位操作系统上,它们均使用 32 位(4 个字节),在 64 位操作系统上,它们均使用 64 位(8 个字节)。uintptr
的长度被设定为足够存放一个指针即可。Go 中不允许不同类型之间的混合使用,但是对于常量的类型限制非常少,因此允许常量之间的混合使用
func main() {
var a int
var b int32
a = 15
//b = a + a // 编译错误
b = int32(a + a) // 编译ok
b = b + 5 // 因为 5 是常量,所以可以通过编译
}
注:如果你实际存的数字超出你要转换到的类型的取值范围的话,则会引发 panic
Go 语言中没有 float 类型。(Go语言中只有 float32
和 float64
)没有 double 类型。
float32
精确到小数点后 7 位,float64
精确到小数点后 15 位。由于精确度的缘故,你在使用 ==
或者 !=
来比较浮点数时应当非常小心。你最好在正式使用前测试对于精确度要求较高的运算。
尽可能地使用 float64
,因为 math
包中所有有关数学运算的函数都会要求接收这个类型。
通过增加前缀 0 来表示 8 进制数(如:077),增加前缀 0x 来表示 16 进制数(如:0xFF
),以及使用 e
来表示 10 的连乘(如: 1e3 = 1000,或者 6.022e23 = 6.022 x 1e23)
在格式化输出时,可以使用 %t
来表示你要输出的值为布尔型
在格式化字符串里,%d
用于格式化整数(%x
和 %X
用于格式化 16 进制表示的数字),%g
用于格式化浮点型(%f
输出浮点数,%e
输出科学计数表示法),%0nd
用于规定输出长度为 n 的整数,其中开头的数字 0 是必须的,%n.mg
用于表示数字 n 并精确到小数点后 m 位
%b
是用于表示位的格式化标识符
字符只是整数的特殊用例,byte
类型是 uint8
的别名
var ch byte = 'A' 或 var ch byte = 65 或 var ch byte = '\x41'
字符串是字节的定长数组
字符串表示方式:
var str = "hello world"
和 C/C++不一样,Go 中的字符串是根据长度限定,而非特殊字符 \0
注:获取字符串中某个字节的地址的行为是非法的,例如:&str[i]
按位与 &
:同为1相&结果为1
按位或 |
:有一个为1结果为1
按位异或 ^
:相异为1
位清除 &^
:将指定位置上的值设置为 0
在 Go 语言中,&&
和 ||
是具有快捷性质的运算符,当运算符左边表达式的值已经能够决定整个表达式的值的时候(&&
左边的值为 false
,||
左边的值为 true
),运算符右边的表达式将不会被执行。
逻辑运算符:==
、!=
、<
、<=
、>
、>=
数学操作符: +、 -、 *、 /
类型转换, type(variable), 比如: var a int=8; var b int32=int32(a)
值类型:变量直接存储值,内存通常在栈中分配
引用类型:变量存储的是一个地址,这个地址存储最终的值,内存通常在堆上分配,通过GC回收
值类型:基本数据类型int、 float、 bool、 string以及数组和struct
引用类型:指针、 slice、 map、 chan等都是引用类型
函数声明: func 函数名字 (参数列表) (返回值列表) {}
func Add(a int, b int) int {
return a + b
}
函数特点:
a. 不支持重载,一个包不能有两个名字一样的函数
b. 函数是一等公民,函数也是一种类型,一个函数可以赋值给变量
c. 多返回值
函数参数传递方式:值传递;引用传递
注:值传递是值的拷贝。引用传递是地址的拷贝,一般来说,地址拷贝更为高效
函数参数传递方式: 值传递;引用传递
注:map、 slice、 chan、指针、 interface默认以引用的方式传递
命名返回值的名字:return自动匹配返回对象的名字
func calc(a, b int) (sum int, avg int) {
sum = a + b
avg = (a +b)/2
return
}
_ 标识符,用来忽略返回值:
func main() {
sum, _ := calc(100, 200)
}
可变参数:
func add(arg…int) int {
}//0个或多个参数
func add(a int, arg…int) int {
}//1个或多个参数
注:其中arg是一个slice,我们可以通过arg[index]依次访问所有参数,通过len(arg)来判断传递参数的个数
defer用途:
当函数返回时,执行defer语句,因此可以用来做资源清理(文件,数据库,锁资源等)
多个defer语句,按先进后出的方式执行
defer语句中的变量,在defer声明时就决定了
close:主要用来关闭channel
len:用来求长度,比如string、 array、 slice、 map、 channel
new:用来分配内存,主要用来分配值类型,比如int、 struct。返回的是指针
make:用来分配内存,主要用来分配引用类型,比如chan、 map、 slice
append:用来追加元素到数组、 slice中
panic和recover:用来做错误处理
闭包:一个函数和与其相关的引用环境组合而成的实体
package main
import "fmt"
func Adder() func(int) int {
var x int//只初始化一次
return func(delta int) int {
x += delta
return x
}
}
//上述代码类似于cpp中在类中定义成员x,func函数对x进行+delta
func main() {
var f = Adder()
fmt.Println("1:", f(1))//0
fmt.Println("2:", f(20))//21
fmt.Println("3:", f(300))//321
}
数组:是同一种数据类型的固定长度的序列
数组定义: var a [len]int,比如: var a[5]int 一旦定义,长度不能变
长度是数组类型的一部分,因此, var a[5] int和var a[10]int是不同的类型
访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
数组是值类型,因此参数传递改变的是副本的值,不会改变本身的值
数组初始化及遍历:
多维数组及遍历:
var age [5][3]int
var f [2][3]int = [...][3]int{{1, 2, 3}, {7, 8, 9}}
for k1, v1 := range f {
for k2, v2 := range v1 {
fmt.Printf("(%d,%d)=%d ", k1, k2, v2)
}
fmt.Println()
}
切片定义:
切片:切片是数组的一个引用,因此切片是引用类型
切片的长度可以改变,因此, 切片是一个可变的数组
切片遍历方式和数组一样,可以用len()求长度
cap可以求出slice最大的容量, 0 <= len(slice) <= (array), 其中array是slice引用的数组
make创建切片:
slice := make([]type, len)
slice := make([]type, len, cap)
切片初始化:
var slice1 []int = arr[start:end]//包含start到end之间的元素,但不包含end,[start,end)
var slice2 []int = arr[0:end]//可以简写为 var slice []int=arr[:end]
var slice3 []int = arr[start:len(arr)]//可以简写为 var slice[]int = arr[start:]
var slice4 []int = arr[0, len(arr)]//可以简写为 var slice[]int = arr[:]
Slice = slice[:len(slice)-1] //最后一个元素去掉
切片的内存布局,类似C++ vector:
用append内置函数操作切片:
slice := make([]int, 10)
slice=append(slice, 10)//增添元素,更新
var a = []int{1, 2, 3}
var b = []int{4, 5, 6}
a = append(a, b...)//增添数组
For range 遍历切片:
for index, val := range slice {
}
切片resize:
var a = []int {1,3,4,5}
b := a[1:2]
b = b[0:3]
切片拷贝:
s1 := []int{1,2,3,4,5}
s2 := make([]int, 10)
copy(s2, s1)
string与slice:
string底层就是一个byte的数组,因此,也可以进行切片操作
str := “hello world”
s1 := str[0:5]
fmt.Println(s1)
s2 := str[5:]
fmt.Println(s2)
s := []byte(str)//修改str
s[0] = ‘o’
str = string(s)
string的底层布局:
数组和切片的区别:
数组:类型 [n]T 表示拥有 n 个 T 类型的值的数组,指定空间大小
切片:类型 []T 表示一个元素类型为 T 的切片(动态开辟数组),不指定大小
new和make的区别:
var i *int=new(int)
*i=10
var num []int = make([]int, 10)
num[1]=10
make也是用于内存分配的,但是和new不同,它只用于chan、 map以及切片的内存创建,而且它返回的类型就是这三个类型本身,而不是他们的指针类型,因为这三种类型就是引用类型,所以就没有必要返回他们的指针了
map是一种特殊的数据结构:一种元素对 (pair) 的无序集合,pair 的一个元素是 key,对应的另一个元素是 value,所以这个结构也称为关联数组或字典。这是一种快速寻找值的理想结构:给定 key,对应的 value 可以迅速定位。
map是引用类型,可以使用如下声明:
var map1 map[keytype]valuetype
var map1 map[string]int
key 可以是任意可以用 ==
或者 !=
操作符比较的类型,比如 string
、int
、float32(64)
。所以数组、切片和结构体不能作为 key ,但是指针和接口类型可以。如果要用结构体作为 key 可以提供 Key()
和 Hash()
方法,这样可以通过结构体的域计算出唯一的数字或者字符串的 key
value 可以是任意类型的;通过使用空接口类型,我们可以存储任意值,但是使用这种类型作为值时需要先做一次类型断言
map
是 引用类型 的: 内存用 make()
方法来分配
map
的初始化:
var map1 = make(map[keytype]valuetype)
你错误地使用 new()
分配了一个引用对象,你会获得一个空引用的指针
测试 map1
中是否存在 key1
:
if _, ok := map1[key1]; ok {
// ...
}
从 map1
中删除 key1
:
delete(map1, key1)
注:如果 key1
不存在,该操作不会产生错误
使用 for
循环读取 map
:
for key, value := range map1 {
...
}
只想获取 key
:
for key := range map1 {
fmt.Printf("key is: %d\n", key)
}
注意 map
不是按照 key 的顺序排列的,也不是按照 value 的序排列的
如果你想为 map
排序,需要将 key(或者 value)拷贝到一个切片,再对切片排序(使用 sort
包),然后可以使用切片的 for-range 方法打印出所有的 key 和 value
package main
import (
"fmt"
"sort"
var (
barVal = map[string]int{"alpha": 34, "bravo": 56, "charlie": 23,
"delta": 87, "echo": 56, "foxtrot": 12,
"golf": 34, "hotel": 16, "indio": 87,
"juliet": 65, "kili": 43, "lima": 98}
)
func main() {
fmt.Println("unsorted:")
for k, v := range barVal {
fmt.Printf("Key: %v, Value: %v / ", k, v)
}
keys := make([]string, len(barVal))
i := 0
for k, _ := range barVal {
keys[i] = k
i++
}
sort.Strings(keys)
fmt.Println()
fmt.Println("sorted:")
for _, k := range keys {
fmt.Printf("Key: %v, Value: %v / ", k, barVal[k])
}
}
实际项目开发中我们首先要在我们项目目录中用go mod命令生成一个go.mod文件管理我们项目的依赖
使用go mod命令生成一个go.mod文件
go mod init goProject
生成一个 go.mod 的文件,里面的内容是go版本,以及以后添加的包
module goProject
go 1.14
包(package)是多个Go源码的集合,一个包可以简单理解为一个存放多个.go文件的文件夹。该文件夹下面的所有go文件都要在代码的第一行添加如下代码,声明该文件归属的包
package 包名
注意事项
在Go 语言程序执行时导入包语句会自动触发包内部init()函数的调用。
需要注意的是:init() 函数没有参数也没有返回值。init()函数在程序运行时自动被调用执行,不能在代码中主动调用它。
包初始化顺序:
依赖
使用go mod命令生成一个go.mod文件
go mod init goProject
生成一个 go.mod 的文件,里面的内容是go版本,以及以后添加的包
module goProject
go 1.14
包(package)是多个Go源码的集合,一个包可以简单理解为一个存放多个.go文件的文件夹。该文件夹下面的所有go文件都要在代码的第一行添加如下代码,声明该文件归属的包
package 包名
注意事项
在Go 语言程序执行时导入包语句会自动触发包内部init()函数的调用。
需要注意的是:init() 函数没有参数也没有返回值。init()函数在程序运行时自动被调用执行,不能在代码中主动调用它。
包初始化顺序:
[外链图片转存中…(img-jR5UCw7D-1698326264819)]
[外链图片转存中…(img-OPYWhU6s-1698326264819)]