这是我参与「第五届青训营 」伴学笔记创作活动的第 1 天
一、本堂课重点内容:
- go的优势
- 开发配置
- 基础语法
二、详细知识点介绍:
go的优势
- 学习曲线容易
- 开发效率和运行效率高
- 自由高效
- 拥有强大的标准库、
- 部署方便
- 高度支持并发性
- 稳定性高
开发配置
基础语法
变量
声明变量可以用var
var a int
常用变量类型有
- 整数类型:(u)int,(u)int8,(u)int16,(u)int32,(u)int64,uintptr(指针),byte,rune
- 字符(串)类型:byte,rune,string
- 浮点型:float32,float64
- 布尔型:bool
条件控制语句
if{}
当条件表达式为True时,就会执行括号中的代码块。
if age > 15 {
fmt.Println("你的年龄大于15岁")
}
if else{}
当条件表达式为True时,否则执行代码块2。
if age > 15 {
fmt.Println("你的年龄大于15岁")
} else {
fmt.Println("你的年龄小于等于15岁")
}
if{} else if
- 先判断条件表达式1是否成立,如果为True,就执行代码块1
- 如果条件表达式1位False,就去判断条件表达式2是否成立,如果条件表达式2成立,就执行代码块2
- 依次类推,如果所有的条件表达式不成立,就执行else语句块
- else不是必须的
- 注意:多分支语句只能有一个执行入口
if age > 15 {
fmt.Println("你的年龄大于15岁")
} else if age < 10 {
fmt.Println("你的年龄小于10岁")
} else {
fmt.Println("你的年龄小于等于15岁大于等于10岁")
}
switch
- switch语句用于基于不同条件执行不同动作,每个case分支都是唯一的,从上往下逐一测试,直到匹配为止。注意,匹配项后面不需要再加break。
- switch的执行流程是先执行表达式,得到值,然后和case的表达式进行比较,如果相等,就匹配到;接着执行对应case的语句块,然后退出switch控制。
- 如果switch表达式的值没有和任何case的表达式匹配成功,则执行default的语句块,执行后退出switch的控制。
- golang中的case后的表达式可以有多个,使用逗号间隔。
- golang中的case语句块不需要写break,因为默认会有。即在默认情况下,当程序执行完case语句块后,就直接退出该switch控制结构。
switch key {
case '1':
fmt.Println("今天星期一")
case '2':
fmt.Println("今天星期二")
case '3':
fmt.Println("今天星期三")
case '4':
fmt.Println("今天星期四")
case '5':
fmt.Println("今天星期五")
case '6':
fmt.Println("今天星期六")
case '7':
fmt.Println("今天星期天")
default:
fmt.Println("输入有误....")
}
for循环
for i
str := "Hello, go"
for i := 0; i < len(str); i++ {
ch := str[i]
fmt.Println(i, ch, , string(ch))
}
for range
str := "Hello, go"
for i, ch := range str {
fmt.Println(i, ch, string(ch))
}
函数
基本概念
go语言函数的表达式 func 函数名(形参名1 类型1,形参名2 类型2,…)(返回类型1,返回类型2,…) { 函数体 } 注意:go语言的函数支持多个返回值。函数名大写表示该函数为Public可以被其它package调用,小写为private,不可以被其它包调用。
加粗部分为函数签名
func test(a int, b int) (int, int) {
return a+b, a-b
}
//_表示忽视该返回值
_, b := test(1,2)
细节
-
go函数不支持重载
-
函数也是一种数据类型,可以赋值给变量(参考匿名函数的第二种定义方法)
//函数类型为func<int, int> int func test(a int, b int) int { return a+b } a := tset res = a(1,2) -
函数可以作为型参类型
func myFun(fun func(int, int) int, a int, b int) int { return fun(a, b) } res := myFun(test, 1, 2) -
type自定义数据类型(和类型别名不同)
//自定义数据类型 type newFunc func(int, int) int //类型别名 type myFunc = func(int, int) int func main() { var a newFunc var b myFunc fmt.Printf("type of a:%T\n", a) //type of a:main.newFunc,表示main包下定义的newFunc类型。这是一个新的数据类型。 fmt.Printf("type of b:%T\n", b) //type of b:func<int, int> int,myFunc类型别名只会在代码中存在,编译完成时并不会有myFunc类型。 } -
对函数返回值命名
func fun(a int, b int) (res int) { res = a + b //return res return } -
使用_标识符(作为一个占位符)忽略返回值
func test(a int, b int) (int, int) { return a+b, a-b } //_表示忽视该返回值 _, b := test(1,2) -
支持可变参数(可以传0个或多个参数)
//args本质是切片(slice,一种可变数组),通过args[index]访问参数 func fun(a int, args... int) (sum int) { sum += a for i := 0; i < len(args); i++ { sum += args[i] } return } res = fun(1, 2, 3)
init函数
基本概念
每一个.go文件都可以有一个init函数,该函数会在main函数调用前被go运行框架调用,可用于初始化工作
func init() {
a = 1
}
细节
- 执行流程:导入包的内容->全局变量定义->init函数->main函数
匿名函数
可以用于函数内定义函数
定义后直接调用(仅使用一次)
res := func (a int, b int) int {
return a + b
}(1, 2)
赋给一个变量,然后调用变量来使用
//a的数据类型就是函数类型,类似于函数指针
a := func (a int, b int) int {
return a + b
}
res := a(1,2)
全局匿名函数
将一个匿名函数定义给一个全局变量
// 这种因式分解关键字的写法一般用于声明全局变量
var {
//全局匿名函数,如果是小写的fun则为私有
Fun = func (a int, b int) int {
return a + b
}
}
闭包
基本概念
一个函数和其他相关引用环境组合的一个整体(实体)(函数内部引用函数外的变量)
//这段代码实现了一个累加器,不像for循环那样限制了使用的位置
func AddUpper() func (int) int {
n := 10
return func(x int) int {
n += x
return n
}
}
func main() {
f1 := AddUpper
fmt.Println(f1(1))//11
fmt.Println(f1(2))//13
fmt.Println(f1(3))//16
f2 := AddUpper()
fmt.Println(f2(1))//11
fmt.Println(f1(4))//20
}
其中AddUpper函数内的代码就是闭包
n := 10
return func(x int) int {
n += x
return n
}
其返回的是一个匿名函数,但是该匿名函数引用到了外部的变量n,因此与其形成一个整体,构成闭包(类似一个类里的字段和操作)
注:string(36) -> $(36十进制对应的ascii码字符)
结构体
定义与声明结构体
结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:
type struct_variable_type struct {
member definition
member definition
...
member definition
}
一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:
variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
访问结构体成员
如果要访问结构体成员,需要使用点号 . 操作符,格式为:
结构体.成员名"
结构体指针
你可以定义指向结构体的指针类似于其他指针变量,格式如下:
var struct_pointer *Books
以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前:
struct_pointer = &Book1
使用结构体指针访问结构体成员,使用 "." 操作符:
struct_pointer.title
注意
struct作类
struct 类似于 java 中的类,可以在 struct 中定义成员变量。
要访问成员变量,可以有两种方式:
- 1.通过 struct 变量.成员 变量来访问。
- 2.通过 struct 指针.成员 变量来访问。
不需要通过 getter, setter 来设置访问权限。
type Rect struct{ //定义矩形类
x,y float64 //类型只包含属性,并没有方法
width,height float64
}
func (r *Rect) Area() float64{ //为Rect类型绑定Area的方法,*Rect为指针引用可以修改传入参数的值
return r.width*r.height //方法归属于类型,不归属于具体的对象,声明该类型的对象即可调用该类型的方法
}
结构体中属性的首字母大小写问题
- 首字母大写相当于 public。
- 首字母小写相当于 private。
注意: 这个 public 和 private 是相对于包(go 文件首行的 package 后面跟的包名)来说的。
敲黑板,划重点
当要将结构体对象转换为 JSON 时,对象中的属性首字母必须是大写,才能正常转换为 JSON。
示例一:
type Person struct {
Name string //Name字段首字母大写
age int //age字段首字母小写
}
func main() {
person:=Person{"小明",18}
if result,err:=json.Marshal(&person);err==nil{ //json.Marshal 将对象转换为json字符串
fmt.Println(string(result))
}
}
//输出
{"Name":"小明"} //只有Name,没有age
示例二:
type Person struct{
Name string //都是大写
Age int
}
//输出
{"Name":"小明","Age":18} //两个字段都有
那这样 JSON 字符串以后就只能是大写了么? 当然不是,可以使用 tag 标记要返回的字段名。
示例三:
type Person struct{
Name string `json:"name"` //标记json名字为name
Age int `json:"age"`
Time int64 `json:"-"` // 标记忽略该字段
}
func main(){
person:=Person{"小明",18, time.Now().Unix()}
if result,err:=json.Marshal(&person);err==nil{
fmt.Println(string(result))
}
}
//输出
{"name":"小明","age":18}
```go
if name == "config.ini" {
return nil
} else {
return errors.New("读取文件错误")
}
}
func test() {
err := readConf("config2.ini")
if err != nil {
//输出错误并终止程序
panic(err)
}
fmt.Println("test()")
}
func main() {
test()
fmt.Println("main()")
}
//panic: 读取文件错误