这是我参与「 第五届青训营 」伴学笔记创作活动的第 3 天
常用的包
包的概念就是我们程序中的目录,我们所写的所有代码都放在包中在定义的时候用package定义包, 然后使用 import 引入包。Go语言提供了很多内置包,例如:fmt、strings、strconv、os、io 等等。
strings包
strings 主要针对 utf-8 编码,实现一些简单函数。
strconv包
主要用于字符串和基本类型的数据类型的转换
time 包
time包操作的都是时间,时间的单位都包括年,月,日,时,分,秒,毫秒,微妙,纳秒,皮秒。
需要注意的是Go语言中时间的格式化,需要指定格式化时间的模板, 不管年月日的类型格式怎么写,但是具体的数值必须写成2006-01-02 15:04:05, 如果不是这个日期就不能够格式化,这个时间也是为了纪念Go语言诞生的时间。
包的声明
以上所讲内容都是Go语言中内置的包,我们自己开发的代码也是通过包的形式定义的,每一个包就相当于一个目录。在文档第一行声明package。
go自己会默认从GO的安装目录和GOPATH环境变量中的目录,检索 src 下的目录进行检索包是否存在。所以导入包的时候路径要从src目录下开始写。GOPATH 就是我们自己定义的包的目录。
程序在编写过程中往往会使用到除自己程序外的第三方包,可以使用Go命令 go get 来导入需要的第三方包到配置的GOPATH中。
defer 语句
defer 表示延时推迟的意思,在go语言中用来延时一个函数或者方法的执行。如果一个函数或者方法添加了defer关键字,表示则暂时不执行,等到主函数的所有方法都执行完后才开始执行。
当多个函数被defer的时候他们被添加到一个堆栈中,并且根据先进后出的原则执行。
panic recover语句
panic宕机 recover恢复 panic 表示恐慌。当程序遇到一个异常时候可以强制执行让程序终止操作,同时需要引入defer函数类延时操作后面的函数,在defer函数中通过recover来恢复正常代码的执行,因为defer是根据先入后出原则,所以先被defer的函数会放在最后执行,recover需要放在第一个被执行,当遇到panic时候恢复正常的代码逻辑。同时也可将错误信息通过recover获取panic传递的错误信息。
结构体
在Go语言中不存在Class类的概念,但是可以通过结构体struct来实现。结构体就是一种相同类型,或者不同类型的数据构成的数据的集合。里面的每一个变量叫做成员变量。也就是结构体的字段。每一个字段拥有自己的数据类型和数值。
定义
//定义结构体
type Person struct {
name string
age int
sex string
address string
}
初始化
p := Person{} //使用简短声明方式,后面加上{}代表这是结构体
//直接给成员变量赋值
p2 := Person{age: 2, address: "陕西", name: "老李头", sex: "女"}
在Go语言中有一个关键字new可以用来实例化结构体。本质上是分配了一个某种类型的内存空间,所以使用new关键字默认就会返回一个指针。使用new创建结构体,默认就是一个指针类型的结构体。
//1 使用结构体指针
var p *Person
//2 使用new 创建结构体指针
pnew := new(Person)
fmt.Println(pnew)
实例化
在Go语言中,使用&符号取地址时候,默认就对该类型进行了一次实例化操作。在开发过程中经常会以下面这种使用函数封装写法,来实例化一个结构体。
//定义结构体
type Person struct {
name string
age int
sex string
}
func main() {
p := newPerson("好家伙", 18, "男")
fmt.Println(p.name, p.age, p.sex)
}
//使用函数来实例化结构体
func newPerson(name string, age int, sex string) *Person {
return &Person{
name: name,
age: age,
sex: sex,
}
}
初始化时可以忽略成员内的字段名,但是必须初始化所有的字段,必须和结构体内字段顺序一致,并且不能和有字段的初始化方法混用。
默认值
结构体内的每一个字段,都有自己相应的数据类型,如果结构体被实例化后,字段的默认值就是该字段类型的零值,int就是0,string就是"",如果是指针类型,默认就是nil。
结构体与JSON相互转换
Go语言标准库中提供了json解析的包,使用之前导入包。
import"encoding/json"
结构体转JSON
需要注意的是将结构体转换为Json数据时候,定义结构体的字段必须首字母大写。否则无法正常解析。
常用的包
包的概念就是我们程序中的目录,我们所写的所有代码都放在包中在定义的时候用package定义包, 然后使用 import 引入包。Go语言提供了很多内置包,例如:fmt、strings、strconv、os、io 等等。
strings包
strings 主要针对 utf-8 编码,实现一些简单函数。
strconv包
主要用于字符串和基本类型的数据类型的转换
time 包
time包操作的都是时间,时间的单位都包括年,月,日,时,分,秒,毫秒,微妙,纳秒,皮秒。
需要注意的是Go语言中时间的格式化,需要指定格式化时间的模板, 不管年月日的类型格式怎么写,但是具体的数值必须写成2006-01-02 15:04:05, 如果不是这个日期就不能够格式化,这个时间也是为了纪念Go语言诞生的时间。
包的声明
以上所讲内容都是Go语言中内置的包,我们自己开发的代码也是通过包的形式定义的,每一个包就相当于一个目录。在文档第一行声明package。
go自己会默认从GO的安装目录和GOPATH环境变量中的目录,检索 src 下的目录进行检索包是否存在。所以导入包的时候路径要从src目录下开始写。GOPATH 就是我们自己定义的包的目录。
程序在编写过程中往往会使用到除自己程序外的第三方包,可以使用Go命令 go get 来导入需要的第三方包到配置的GOPATH中。
defer 语句
defer 表示延时推迟的意思,在go语言中用来延时一个函数或者方法的执行。如果一个函数或者方法添加了defer关键字,表示则暂时不执行,等到主函数的所有方法都执行完后才开始执行。
当多个函数被defer的时候他们被添加到一个堆栈中,并且根据先进后出的原则执行。
panic recover语句
panic宕机 recover恢复 panic 表示恐慌。当程序遇到一个异常时候可以强制执行让程序终止操作,同时需要引入defer函数类延时操作后面的函数,在defer函数中通过recover来恢复正常代码的执行,因为defer是根据先入后出原则,所以先被defer的函数会放在最后执行,recover需要放在第一个被执行,当遇到panic时候恢复正常的代码逻辑。同时也可将错误信息通过recover获取panic传递的错误信息。
结构体
在Go语言中不存在Class类的概念,但是可以通过结构体struct来实现。结构体就是一种相同类型,或者不同类型的数据构成的数据的集合。里面的每一个变量叫做成员变量。也就是结构体的字段。每一个字段拥有自己的数据类型和数值。
定义
//定义结构体
type Person struct {
name string
age int
sex string
address string
}
初始化
p := Person{} //使用简短声明方式,后面加上{}代表这是结构体
//直接给成员变量赋值
p2 := Person{age: 2, address: "陕西", name: "老李头", sex: "女"}
在Go语言中有一个关键字new可以用来实例化结构体。本质上是分配了一个某种类型的内存空间,所以使用new关键字默认就会返回一个指针。使用new创建结构体,默认就是一个指针类型的结构体。
//1 使用结构体指针
var p *Person
//2 使用new 创建结构体指针
pnew := new(Person)
fmt.Println(pnew)
实例化
在Go语言中,使用&符号取地址时候,默认就对该类型进行了一次实例化操作。在开发过程中经常会以下面这种使用函数封装写法,来实例化一个结构体。
//定义结构体
type Person struct {
name string
age int
sex string
}
func main() {
p := newPerson("好家伙", 18, "男")
fmt.Println(p.name, p.age, p.sex)
}
//使用函数来实例化结构体
func newPerson(name string, age int, sex string) *Person {
return &Person{
name: name,
age: age,
sex: sex,
}
}
初始化时可以忽略成员内的字段名,但是必须初始化所有的字段,必须和结构体内字段顺序一致,并且不能和有字段的初始化方法混用。
默认值
结构体内的每一个字段,都有自己相应的数据类型,如果结构体被实例化后,字段的默认值就是该字段类型的零值,int就是0,string就是"",如果是指针类型,默认就是nil。
结构体与JSON相互转换
Go语言标准库中提供了json解析的包,使用之前导入包。
import"encoding/json"
结构体转JSON
需要注意的是将结构体转换为Json数据时候,定义结构体的字段必须首字母大写。否则无法正常解析。
//结构体
type Prescription struct {
Name string
Unit string
Additive *Prescription
}
func main() {
p := Prescription{}
p.Name = "鹤顶红"
p.Unit = "1.2kg"
p.Additive = &Prescription{
Name: "砒霜",
Unit: "0.5kg",
}
buf, err := json.Marshal(p) //转换为json返回两个结果
if err != nil {
fmt.Println("err = ", err)
return
}
fmt.Println("json = ", string(buf))
}
打印结果看出其中json字符中每一个key的首字母也是大写,最后一个没有设置数据的字段的结果为null。那么如何强制将他变为小写的。并且将不需要显示的字段隐藏掉。就需要在结构体上添加标记。
//结构体
type Prescription struct {
Name string `json:"name"`
Unit string `json:"unit"`
Additive *Prescription
}
JSON转结构体
//结构体
type Prescription struct {
Name string `json:"name"` //重新指定json字段为小写输出
Unit string `json:"unit"`
Additive *Prescription `json:"additive,omitempty"`
}
func main() {
jsonstr := `{"name":"鹤顶红","unit":"1.2kg","additive":{"name":"砒霜","unit":"0.5kg"}}`
var p Prescription
if err := json.Unmarshal([]byte(jsonstr), &p); err != nil {
fmt.Println(err)
}
fmt.Println(p)
}
方法的使用
方法
在Go语言中方法和函数类似,也可以认为方法是特殊类型的函数,只不过方法限制了接收者,方法也可以说是包含了接收者的函数。
//创建一个结构体代表人物角色--任我行
type Role struct {
Name string //姓名
Ability string //技能
Level int //级别
Kill float64 //杀伤力
}
//创建一个方法,只要是Role结构体就能调用。
func (r Role) Kungfu() {
fmt.Printf("我是:%s,我的武功:%s,已经练到%d级了,杀伤力%.1f\n", r.Name, r.Ability, r.Level, r.Kill)
}
指针类型方法
方法的接收者可以是结构体类型,也可以是一个值,或者是一个指针类型。
func main() {
rwx := &Role{"任我行", "吸星大法", 8, 10}
rwx.Kungfu() //使用Role该类型的指针调用方法
zwj := &Role{"张无记", "九娘神功", 9, 12}
zwj.Kungfu2() //调用指针类型方法
}
//创建一个结构体代表人物角色--任我行或者张无忌
type Role struct {
Name string //姓名
Ability string //技能
Level int //级别
Kill float64 //杀伤力
}
func (r Role) Kungfu() {
fmt.Printf("我是:%s,我的武功:%s,已经练到%d级了,杀伤力%.1f\n", r.Name, r.Ability, r.Level, r.Kill)
}
//指针类型方法
func (r *Role) Kungfu2() {
fmt.Printf("我是:%s,我的武功:%s,已经练到%d级了,杀伤力%.1f\n", r.Name, r.Ability, r.Level, r.Kill)
}
方法和函数的区别
- 方法限制某个类别的行为,需要指定调用者。函数是一段独立的功能代码,可以直接调用。
- 方法名称可以相同,只要接收者不同就可以,函数命名上不能冲突。
Go语言实现面向对象
使用结构体实现封装
可以把struct结构体看成一个类,结构体如果用面向对象的思维来理解,结构体把字段封装到一起,数据被保护在结构体内部,程序需要访问字段的时候,需要通过结构体来访问。
// 定义结构体实现封装
type Haojiahuo struct {
Name string
Age int
}
//使用NewPerson方法创建一个对象
func NewPerson(name string) *Haojiahuo {
return &Haojiahuo{
Name: name,
}
}
继承的实现
继承可以解决代码复用的问题,结构体内嵌套一个匿名结构体,也可以嵌套多层结构体。
// 创建一个结构体起名 Ouyangcrazy 代表父类
type Ouyangcrazy struct {
Name string
Age int
Ability string
}
//创建一个结构体代表子类
type YangGuo struct {
Ouyangcrazy //包含父类所有属性
Address string //单独子类有的字段
}
// 父类的方法
func (o *Ouyangcrazy) ToadKongfu() {
fmt.Println(o.Name, "的蛤蟆功!")
}
//子类的方法
func (y *YangGuo) NewKongfu() {
fmt.Println(y.Name, "子类自己的新功夫!")
}
func main() {
o := &Ouyangcrazy{Name: "欧阳疯", Age: 70} //创建父类
o.ToadKongfu() //父类对象访问父类方法
y := &YangGuo{Ouyangcrazy{Name: "杨过", Age: 18}, "古墓"} //创建子类
fmt.Println(y.Name) //子类对象访问父类中有的字段
fmt.Println(y.Address) //子类访问自己的字段
y.ToadKongfu() //子类对象访问父类方法
y.NewKongfu() //子类访问自己的方法
//y.ToadKongfu() //如果存在自己的方法 访问自己重写的方法
}
接口
接口的意义是对其他类型的一个概括,接口内可以定义很多个方法,谁将这些方法实现,就可以认为是实现了该接口。Go语言的多态,主要是通过接口来实现。
//定义接口
type Kongfu interface {
Toad() //蛤蟆功
SixSwords() //六脉神剑
}
//实现类
type Haojiahuo struct {
name string
}
//实现类
type Laolitou struct {
name string
}
//实现方法
func (o Haojiahuo) Toad() {
fmt.Println(o.name, "实现了蛤蟆功..")
}
//实现方法
func (o Haojiahuo) SixSwords() {
fmt.Println(o.name, "实现了六脉神剑..")
}
//实现方法
func (f Laolitou) Toad() {
fmt.Println(f.name, "也实现了蛤蟆功..")
}
//实现方法
func (f Laolitou) SixSwords() {
fmt.Println(f.name, "也实现了六脉神剑.")
}
//实现自己的方法
func (f Laolitou) PlayGame() {
fmt.Println(f.name, "玩游戏..")
}
空接口
空接口就是不包含任何方法的接口,所有的类型都可以实现空接口,因此空接口可以实现存储任意类型的数据, 谁实现它就被看作是谁的实现类。
//空接口
type T interface {
}
GO语言中的错误
错误和异常不同,错误是在程序中正常存在的,可以预知的失败在意料之中。而异常通常指在不应该出现问题的地方出现问题,比如空指针,这在人们的意料之外。go语言没有 try......catch 这样的方式来捕获异常所以Go定义属于自己的一种错误类型,用error表示错误。