Go 基础语法 | 青训营笔记

119 阅读13分钟

这是我参与「 第五届青训营 」伴学笔记创作活动的第 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表示错误。