Go语言22讲
01基本go程序
package main //属于哪个包
import "fmt" //导入包
func main() {
fmt.Println("Hello, 世界")
}
环境变量设置
- GOPATH:代表 Go 语言项目的工作目录
- GOBIN:代表 Go 编译生成的程序的安装目录
02变量
var 变量名 类型 = 表达式
//声明一个变量
var i int = 10
//一次声明多个变量
var (
j int = 0
k int = 1
)
//自动推导
var (
j = 0
k = 1
)
//简短声明
i := 10
bf := false
str := "Hello"
基础类型
整形:
- 有符号整型:如 int、int8、int16、int32 和 int64。
- 无符号整型:如 uint、uint8、uint16、uint32 和 uint64。
浮点数:float32 和 float64
布尔型:true 和false
字符串:string
指针:pi := &i
常量:
const name = "Hello"
const (
one = 1
two = 2
three = 3
four = 4
)
//iota等价写法
const (
one = iota + 1
two
three
four
)
未初始化的值会设为零值
变量之间的转换
i2s := strconv.Itoa(i)
s2i,err := strconv.Atoi(i2s)
fmt.Println(i2s,s2i,err)
i2f:=float64(i)
f2i:=int(f64)
fmt.Println(i2f,f2i)
string包
//判断s1的前缀是否是H
fmt.Println(strings.HasPrefix(s1,"H"))
//在s1中查找字符串o
fmt.Println(strings.Index(s1,"o"))
//把s1全部转为大写
fmt.Println(strings.ToUpper(s1))
03控制结构
if
if i > 10 {
} else if {
} else {
}
if i := 6; i > 10 {
} else if {
} else {
}
switch
switch i := 6 {
case i > 10 :
...
case i > 5 && i <= 10:
...
default:
...
}
switch j := 1; j {
case 1 :
...
case 2 :
...
default:
...
}
switch 2>1 {
case true:
fmt.Println("2>1")
case false:
fmt.Println("2<=1")
}
for
sum := 0
for i := 1; i <= 100; i++ {
sum+=i
}
sum := 0
i := 1
for i <= 100 {
sum += i
i++
}
sum := 0
i := 1
for {
sum += i
i++
if i > 100 {
break
}
}
04集合
数组
array: = [5]string{"a","b","c","d","e"}
array := [...]string{"a","b","c","d","e"}
for i, v := range array{
fmt.Printf("数组索引:%d,对应值:%s\n", i, v)
}
for _, v := range array{
fmt.Printf("对应值:%s\n", v)
}
//二维数组
aa := [3][3]int{}
切片
slice:=array[start : end]
array:=[5]string{"a","b","c","d","e"}
slice:=array[2:5]
array[:4] //等价于 array[0:4]。
array[1:] //等价于 array[1:5]。
array[:] //等价于 array[0:5]。
//追加一个元素
slice2 := append(slice1,"f")
//多加多个元素
slice2 := append(slice1,"f","g")
//追加另一个切片
slice2 := append(slice1,slice...)
map
相当于unordered_map
nameAgeMap := make(map[string]int)
nameAgeMap["飞雪无情"] = 20
nameAgeMap := map[string]int{"飞雪无情":20}
//添加键值对或者更新对应 Key 的 Value
nameAgeMap["飞雪无情"] = 20
//获取指定 Key 对应的 Value
age:=nameAgeMap["飞雪无情"]
age,ok:=nameAgeMap["飞雪无情1"]
if ok {
fmt.Println(age)
}
for k,v:=range nameAgeMap{
fmt.Println("Key is",k,",Value is",v)
}
string和[]byte
//使用 [] 操作符获取指定索引的字节值
s := "Hello飞雪无情"
bs := []byte(s)
函数和方法
函数
**func funcName(params) result { body }
func sum(a, int, b, int) int {
return a + b
}
res := sum(1 , 2)
函数的多值返回
第一个值返回函数的结果,第二个值返回函数出错的信息,这种就是多值返回的经典应用。
func sum(a, b int) (int, error) {
if a < 0 || b < 0 {
return 0, error.New("a或者b不能是负数")
}
return a + b, nil
}
res, err := sum(1, 2)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(res)
}
//不使用err
res, _ := sum(1, 2)
命名返回参数
func sum( a, b int) (sum int, err, error) {
if a < 0 || b < 0 {
return 0, errors.New("a或者b不能是负数")
}
sum = a + b
err = nil
return
}
可变参数
可变参数的类型其实就是切片,如果你定义的函数中既有普通参数,又有可变参数,那么可变参数一定要放在参数列表的最后一个,比如 sum1(tip string,params …int).
func sum1(params ... int) int {
sum := 0
for _, i := range params {
sum += i
}
return sum
}
包级函数
函数名称的首字母要大写,比如 Println
- 函数名称首字母小写代表私有函数,只有在同一个包中才可以被调用;
- 函数名称首字母大写代表公有函数,不同的包也可以调用;
- 任何一个函数都会从属于一个包。
匿名函数和闭包
有了匿名函数,就可以在函数中再定义函数(函数嵌套)
//sum2是函数类型的变量
sum2 := func(a, b int) int {
return a + b;
}
fmt.Println(sum2(1, 2))
方法
Go 语言中,方法和函数是两个概念,但又非常相似,不同点在于方法必须要有一个接收者,这个接收者是一个类型,这样方法就和这个类型绑定在一起,称为这个类型的方法。
type Age uint 表示定义一个新类型 Age,该类型等价于 uint,可以理解为类型 uint 的重命名(C++ 中的typedef)。示例中方法 String() 就是类型 Age 的方法,类型 Age 是方法 String() 的接收者。
和函数不同,定义方法时会在关键字 func 和方法名 String 之间加一个接收者 (age Age) ,接收者使用小括号包围。
type Age uint
func (age Age) String() {
fmt.Println("the age is", age)
}
age := Age(25)
age.String()
在调用方法的时候,传递的接收者本质上都是副本,只不过一个是这个值副本,一是指向这个值指针的副本。
06struct和interface
type person struct {
name string
age uint
}
var p1 person
p2 := person{"飞雪无情", 30}
p3 := person{age:30, name:"飞雪无情"}
p4 := person{age:30}
fmt.Println(p2.name, p.age)
结构体嵌套
type person struct {
name string
age uint
addr address
}
type addr struct {
province string
city sstring
}
p := person {
age : 30,
name : "飞雪无情"
addr : addr {
province : "北京"
city : "北京"
}
}
fmt.Println(p.addr.city)
接口
接口是和调用方的一种约定,它是一个高度抽象的类型,不用和具体的实现细节绑定在一起。接口要做的是定义好约定,告诉调用方自己可以做什么,但不用知道它的内部实现,这和我们见到的具体的类型如 int、map、slice 等不一样。
接口的定义和结构体稍微有些差别,虽然都以 type 关键字开始,但接口的关键字是 interface,表示自定义的类型是一个接口。也就是说 Stringer 是一个接口,它有一个方法 String() string,整体如下面的代码所示:
package main
import "fmt"
type Sleeper interface {
Sleep() //声明一个Sleep()方法
}
type Dog struct {
Name string
}
type Cat struct {
Name string
}
func (d Dog) Sleep() {
fmt.Printf("Dog %s is sleeping\n", d.Name)
}
func (c Cat) Sleep() {
fmt.Printf("Cat %s is sleeping\n", c.Name)
}
func AnimalSleep(s Sleeper) {
s.Sleep()
}
func main() {
var s Sleeper
dog := Dog{"xiaohei"}
cat := Cat{"kitty"}
s = dog
AnimalSleep(s)
s = cat
AnimalSleep(s)
sleepList := []Sleeper{Dog{"xiaobai"}, Cat{"xiaohei"}}
for _, s := range sleepList {
s.Sleep()
}
}
工厂函数
工厂函数一般用于创建自定义的结构体,便于使用者调用
func NewPerson(name string) * person {
return &person{name: name}
}
p1:=NewPerson("张三") //创建一个*person 类型的变量 p1
我定义了一个工厂函数 NewPerson,它接收一个 string 类型的参数,用于表示这个人的名字,同时返回一个*person。
通过工厂函数创建自定义结构体的方式,可以让调用者不用太关注结构体内部的字段,只需要给工厂函数传参就可以了。