变量
// 声明格式
var 变量名 变量类型
var xxx string
a := fujingjie :=就是简略写法
_ 下划线就是匿名变量
常量和iota
用const定义,定义的时候必须赋值
iota是常量计数器,只能在常量表达式里用
const (
n1 = iota //0
n2 //1
n3 //2
n4 //3
)
if else
if 表达式1 {
分支1
} else if 表达式2 {
分支2
} else{
分支3
}
for
for 初始语句;条件表达式;结束语句{
循环体语句
}
func forDemo() {
for i := 0; i < 10; i++ {
fmt.Println(i)
}
}
// 初始语句和结束语句可以省略,类似while,省略初始语句;还得写
// for range遍历数组、切片、字符串、map 及通道(channel)
switch
s := "a"
switch {
case s == "a":
fmt.Println("a")
fallthrough
case s == "b":
fmt.Println("b")
case s == "c":
fmt.Println("c")
default:
fmt.Println("...")
}
// switch只能有一个default
// fallthrough执行满足条件的case的下一个case
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循环")
}
// goto 跳到指定位置
break
func breakDemo1() {
BREAKDEMO1:
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
break BREAKDEMO1
}
fmt.Printf("%v-%v\n", i, j)
}
}
fmt.Println("...")
}
// break 跳出循环
continue
func continueDemo() {
forloop1:
for i := 0; i < 5; i++ {
// forloop2:
for j := 0; j < 5; j++ {
if i == 2 && j == 2 {
continue forloop1
}
fmt.Printf("%v-%v\n", i, j)
}
}
}
// continue 继续下一次循环 只能在for循环中使用
数组
var 数组变量名 [元素数量]T
a := [...]int{1, 3} // 可以用... 来让编译器自行推导
// 二维数组
// 多维数组只有第一层可以使用...来让编译器推导数组长度
a := [3][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}
切片
var name []T
// 切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针、切片的长度(len)和切片的容量(cap)
// 要检查切片是否为空,请始终使用len(s) == 0来判断,而不应该使用s == nil来判断
// append方法,为切片添加元素
s := []int{} // 没有必要初始化
s = append(s, 1, 2, 3)
// 删除切片
// 切片a中删除索引为index的元素,操作方法是a = append(a[:index], a[index+1:]...)
func main() {
// 从切片中删除元素
a := []int{30, 31, 32, 33, 34, 35, 36, 37}
// 要删除索引为2的元素
a = append(a[:2], a[3:]...)
fmt.Println(a) //[30 31 33 34 35 36 37]
}
map
map[KeyType]ValueType
// map默认值为nil,要用make来分配
make(map[KeyType]ValueType, [cap])
// delete从map删除键值队
delete(map, key)
函数
func 函数名(参数)(返回值){
函数体
}
defer
// 先被defer的语句最后被执行,最后被defer的语句,最先被执行
func main() {
fmt.Println("start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("end")
}
panic和recover
// 类似python中的try except
func funcA() {
fmt.Println("func A")
}
func funcB() {
// defer在最后执行,下面先触发了panic,最后执行的defer
defer func() {
err := recover()
//如果程序出出现了panic错误,可以通过recover恢复过来
if err != nil {
fmt.Println("recover in B")
}
}()
panic("panic in B")
}
func funcC() {
fmt.Println("func C")
}
func main() {
funcA()
funcB()
funcC()
}
指针
可以理解为就是内存地址
- 函数是值拷贝操作,所以要修改值就需要用到指针操作
- 对变量进行取地址(&)操作,可以获得这个变量的指针变量。
- 指针变量的值是指针地址。
- 对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值。
func modify1(x int) {
x = 100
}
func modify2(x *int) {
*x = 100
}
func main() {
a := 10
modify1(a)
fmt.Println(a) // 10
modify2(&a)
fmt.Println(a) // 100
}
- new和make用来做内存分配的,因为Go语言中对于引用类型的变量,我们在使用的时候不仅要声明它,还要为它分配内存空间,否则我们的值就没办法存储,就会出发panic,所以要用new和make来做内存分配。
- 二者都是用来做内存分配的。
- make只用于slice、map以及channel的初始化,返回的还是这三个引用类型本身;
- 而new用于类型的内存分配,并且内存对应的值为类型零值,返回的是指向类型的指针。
- new返回的是一个指向该类型内存地址的指针,比如int就返回*int
- make返回类型本身
func main() {
var a *int
a = new(int)
*a = 100
fmt.Println(*a)
var b map[string]int
b = make(map[string]int, 10)
b["xxx"] = 100
fmt.Println(b)
}
结构体
结构体有点类似python中的类
package main
import (
"fmt"
"os"
)
func main() {
sm := NewStudentMan()
for {
ShowMenu()
var input int
fmt.Println("choose number")
fmt.Scanf("%d /n", &input)
switch input {
case 1:
stu := getInput()
sm.addStudent(stu)
case 2:
stu := getInput()
sm.modifyStudent(stu)
case 3:
sm.showStudentList()
case 4:
os.Exit(0)
}
}
}
// 获取用户的输入信息
func getInput() *Student{
var (
id int
name string
age int
score int
)
fmt.Println("please input student message")
fmt.Println("please enter id")
fmt.Scanf("%d \n", &id)
fmt.Println("please enter name")
fmt.Scanf("%s \n", &name)
fmt.Println("please enter age")
fmt.Scanf("%d \n", &age)
fmt.Println("please enter score")
fmt.Scanf("%d \n", &score)
stu := NewStudent(id, age, score, name)
return stu
}
// ShowMenu 展示菜单
func ShowMenu() {
fmt.Println("WelCome")
fmt.Println("Press 1 add student")
fmt.Println("Press 2 modify student")
fmt.Println("Press 3 show all student")
fmt.Println("Press 4 exit")
}
// Student 定义学生的结构体,结构体类似python中的类
type Student struct {
id int
name string
age int
score int
}
// NewStudent 定义Student的构造函数,构造函数就是类似python类中的__init__函数,用指针是因为struct是值类型,所以值拷贝的开销会比较大
func NewStudent(id,age,score int, name string) *Student{
return &Student{
id: id,
name: name,
age: age,
score: score,
}
}
// StudentMan 定义StudentMan的结构体,allStudents是一个 Student的指针切片
type StudentMan struct {
allStudents []*Student
}
// NewStudentMan StudentMan的构造函数,allStudents是一个切片,所以需要用make来初始化分配内存空间
func NewStudentMan() *StudentMan{
return &StudentMan{
allStudents: make([]*Student,0,50 ),
}
}
// 定义一个新增学生的方法,就是给StudentMan构造函数增加一个方法 go中的方法是一种叫做接收者的函数,类似python中的self,下面的s就是类似于self
func (s *StudentMan)addStudent(NewStu *Student) {
s.allStudents = append(s.allStudents, NewStu)
}
func (s *StudentMan)modifyStudent(NewStu *Student) {
for i, v :=range s.allStudents{
if NewStu.id == v.id{
s.allStudents[i] = NewStu
return
}
}
fmt.Println("未找到该学生")
}
func (s *StudentMan)showStudentList() {
for _, value := range s.allStudents{
fmt.Printf("学号: %d \n", value.id)
fmt.Printf("姓名: %s \n", value.name)
fmt.Printf("年龄: %d \n", value.age)
fmt.Printf("分数: %d \n", value.score)
}
}
接口
- 定义
type 接口类型名 interface{
方法名1( 参数列表1 ) 返回值列表1
方法名2( 参数列表2 ) 返回值列表2
…
}
- 接口用法,面向接口编程
package main
import "fmt"
// 定义支付宝的struct
type ZhiFuBao struct {
// 支付宝
}
// Pay 支付宝的支付方法
func (z *ZhiFuBao) Pay(amount int64) {
fmt.Printf("使用支付宝付款:%.2f元。\n", float64(amount/100))
}
// 定义微信的struct
type WeChat struct {
// 微信
}
// Pay 微信的支付方法
func (w *WeChat) Pay(amount int64) {
fmt.Printf("使用微信付款:%.2f元。\n", float64(amount/100))
}
// Payer 包含支付方法的接口类型
type Payer interface {
Pay(int64)
}
// Checkout 结账,参数是实现了payer接口的obj
func Checkout(obj Payer) {
// 支付100元
obj.Pay(100)
}
func main() {
Checkout(&ZhiFuBao{})
Checkout(&WeChat{})
}
- 空接口空接口是指没有定义任何方法的接口类型。因此任何类型都可以视为实现了空接口。也正是因为空接口类型的这个特性,空接口类型的变量可以存储任意类型的值。
// Any 不包含任何方法的空接口类型
type Any interface{}
error接口
// 返回了一个error
errors.New("报错啦")
并发
goroutine和wg
func main() {
// 来实现并发任务的同步执行
// wg 防止main的goroutine执行完了之后,自己定义的goroutine还没有执行完
var wg sync.WaitGroup
for i:=0; i<5; i++{
go func(index int) {
defer wg.Done() // defer会在最后执行 wg.Done 计数器减1
fmt.Println(index)
}(i)
wg.Add(1) // 计数器加1 这个数字是可以随便定义的,但是在Done里自己处理
}
wg.Wait() // 阻塞,直到计数器变为0
fmt.Println("meila")
}