1、GO学习之Hello World
2、GO学习之入门语法
3、GO学习之切片操作
4、GO学习之 Map 操作
5、GO学习之 结构体 操作
按照公司目前的任务,go 学习是必经之路了,虽然行业卷,不过技多不压身,依旧努力!!!
对于切片(slice)和 Map,开发中经常用到这两种类型,逃不过的,当然针对于一些简单的 API 开发来说,但是在大部分情况下还需要自己定义数据结构,开发语言自身提供的这种还是无法满足复杂的业务场景的,此篇博客就来了解 go 语言中是如何进行自定义类型的,就像 JAVA 中可以定一个实体 Bean (class Persion)来封装数据。
Go 语言中没有 “类” 的概念,也就不能像 JAVA 中那样自己定义类来封装数据,Go 语言中通过 结构体 的内嵌来实现自定义数据结构,这样比面向对象更具有扩展性和灵活性。
在 Go 语言中有自带的数据类型,如 string、整型、浮动型、布尔等基本的数据类型, Go 语言可以使用 type 关键字来定义自定义类型。
自定义类型是个新的类型,我们可以基于内置的类型来定义,也可以通过 struct 来定义。
package main
import "fmt"
func main() {
type myInt int
var aa myInt
aa = 100
fmt.Println(aa)
}
类型别名是 Go1.9 版本添加的新功能,类型别名知识类型的别名,本质上是同一个类型。
package main
import "fmt"
// 类型定义
type myInt int
// 类型别名
type myInt2 = int
func main() {
var a myInt
var b myInt2
fmt.Printf("type of a:%T\n", a)
fmt.Printf("type of b:%T\n", b)
}
运行结果:
PS D:\workspaceGo> go run customType.go
type of a:main.myInt
type of b:int
从运行结果可以看出,a 的类型是 main.MyInt,而 b 的类型是 int,所以类型别名 MyInt2 只会存在于代码中,编译完成并不会存在。
Go 语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体(struct)。
- 使用 type 和 struct 关键字来定义结构体;
- 类型名:自定义结构体的名称,同一个包内不能重复,就像JAVA 的类名,同一个包下不能类名重复;
- 字段名:每个结构体的字段名必须唯一,就像JAVA的一个实体,字段不能重复;
- 字段类型:结构体字段的具体类型;
package main
import "fmt"
func main() {
// 声明一个 person 变量
var p person
p.id = 1
p.name = "张三"
p.age = 28
p.gender = 1
fmt.Printf("p的值为:%+v\n", p)
fmt.Printf("p的值为:%#v\n", p)
// 通过 new 来实例化,指针类型结构体
var p2 = new(person)
p2.id = 2
p2.name = "王老五"
p2.age = 48
p2.gender = 0
fmt.Printf("p2的值为:%T\n", p2)
fmt.Printf("p2的值为:%#v\n", p2)
}
// 定义 person 的自定义类型
type person struct {
id int64
name string
age, gender int8
}
运行结果:
PS D:\workspaceGo> go run customType.go
p的值为:{id:1 name:张三 age:28 gender:1}
p的值为:main.person{id:1, name:"张三", age:28, gender:1}
p2的值为:*main.person
p2的值为:&main.person{id:2, name:"王老五", age:48, gender:0}
通过运行结果可以看出,我们成功的 定义了一个 person 类型,声明一个变量 p 并且赋值给每个 person 的字段,也可以通过 new 关键词来实例化,这样得到的是 p2 指针,我们照样可以通过指针来访问结构体成员。
在需要一些临时数据结构的场景中,自个自定义类型只需要使用一次或者不需要在外部访问,这时候可以使用匿名结构体更加方便。
package main
import "fmt"
func main() {
//定义一个 user 结构体
var user struct {
id int64
name string
age int8
}
user.id = 1
user.name = "李四"
user.age = 30
fmt.Printf("user的值为:%+v\n", user)
fmt.Printf("user的值为:%#v\n", user)
}
运行结果:
PS D:\workspaceGo> go run customType.go
user的值为:{id:1 name:李四 age:30}
user的值为:struct { id int64; name string; age int8 }{id:1, name:"李四", age:30}
从运行结果看出,自定义结构体赋值给 user 变量,但是这个结构体没有名字。
package main
import "fmt"
func main() {
// 获取到 person 的实例地址
p := &person{}
fmt.Printf("%T\n", p)
fmt.Printf("%#v\n", p)
p.id = 3
p.name = "赵四"
p.gender = 2
p.age = 56
fmt.Printf("%#v\n", p)
}
// 定义 person 的自定义类型
type person struct {
id int64
name string
age, gender int8
}
运行结果:
PS D:\workspaceGo> go run customType.go
*main.person
&main.person{id:0, name:"", age:0, gender:0}
&main.person{id:3, name:"赵四", age:56, gender:2}
p.name = “赵四” 其实是 (*p).name = “赵四”。
package main
import "fmt"
func main() {
p := person{
id: 1,
name: "翠花",
gender: 0,
age: 25,
}
fmt.Printf("初始化的 p 值为:%+v\n", p)
// 也可以对结构体指针进行键值初始化
p2 := &person{
id: 2,
name: "燕子",
gender: 0,
}
fmt.Printf("通过结构体指针初始化 p2 值为:%+v\n", p2)
}
// 定义 person 的自定义类型
type person struct {
id int64
name string
age, gender int8
}
运行结果:
PS D:\workspaceGo> go run customType.go
初始化的 p 值为:{id:1 name:翠花 age:25 gender:0}
通过结构体指针初始化 p2 值为:&{id:2 name:燕子 age:0 gender:0}
初始化结构体的时候可以简写,就是不写键,直接写值,使用值的列表方式初始化需要注意以下几点:
- 必须初始化结构体的所有字段
- 初始化值的填充顺序与字段在结构体中的声明顺序要一致
- 值列表初始化不能和键值对初始化混用
package main
import "fmt"
func main() {
p := &person{
1,
"翠花",
0,
25,
}
fmt.Printf("初始化的 p 值为:%+v\n", p)
}
// 定义 person 的自定义类型
type person struct {
id int64
name string
age, gender int8
}
运行结果:
PS D:\workspaceGo> go run customType.go
初始化的 p 值为:&{id:1 name:翠花 age:0 gender:25}
方法的定义格式如下:
func(参数名 参数类型) 方法名(参数列表)(放回参数){
函数体
}
- 接受的参数名,官方建议类型名第一个字母
- 参数类型,可以是指针类型 和 非指针类型
- 方法名、参数列表、放回参数 格式与函数定义相同
package main
import "fmt"
func main() {
p1 := NewPerson(1, "phen")
p1.drame()
}
// 返回指针类型 person
func NewPerson(id int64, name string) *person {
return &person{
id: id,
name: name,
}
}
// person 的方法
func (p person) drame() {
fmt.Printf("%s 的梦想是变有钱!!!", p.name)
}
type person struct {
id int64
name string
}
运行结果:
PS D:\workspaceGo> go run customType.go
phen 的梦想是变有钱!!!
方法与函数的区别是,函数不属于任何类型,方法属于特定的类型。
方法的定义格式如下:
func(参数名 *参数类型) 方法名(参数列表)(放回参数){
函数体
}
package main
import "fmt"
func main() {
p1 := NewPerson(1, "phen")
p1.setAge(26)
fmt.Printf("%s的年纪是 %d\n", p1.name, p1.age)
}
// 返回指针类型 person
func NewPerson(id int64, name string) *person {
return &person{
id: id,
name: name,
}
}
// 使用指针接受参数,设置 age 字段的值
func (p *person) setAge(age int8) {
p.age = age
}
type person struct {
id int64
name string
age int8
}
运行结果:
PS D:\workspaceGo> go run customType.go
phen的年纪是 26
当方法使用值类型接受参数时,Go 语言会在代码运行时将接受者的值复制一份,修改操作只是针对副本,无法修改接受者变量本身。
package main
import "fmt"
func main() {
p1 := NewPerson(1, "phen", 18)
fmt.Printf("修改前%s的年纪是 %d\n", p1.name, p1.age)
p1.setAge(26)
fmt.Printf("修改后%s的年纪是 %d\n", p1.name, p1.age)
}
// 返回指针类型 person
func NewPerson(id int64, name string, age int8) *person {
return &person{
id: id,
name: name,
age: age,
}
}
// 使用指针接受参数,设置 age 字段的值
func (p person) setAge(age int8) {
p.age = age
fmt.Printf("这里%s的年纪是 %d\n", p.name, p.age)
}
type person struct {
id int64
name string
age int8
}
运行结果:
PS D:\workspaceGo> go run customType.go
修改前phen的年纪是 18
这里phen的年纪是 26
修改后phen的年纪是 18
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成,通常是用的最多的一种数据传递方式,接口中返回 JSON 格式,数据转换用 JSON 格式,那 Go 中必然也可以结构体与 JSON 相互转换。
package main
import (
"encoding/json"
"fmt"
)
// 定义学生
type Student struct {
Id int
Name string
Score float32
}
// 定义班级
type Class struct {
Title string
Students []*Student
}
func main() {
// 初始化数据
c := &Class{
Title: "帅哥靓女一班",
Students: make([]*Student, 0, 50),
}
for i := 1; i < 10; i++ {
stu := &Student{
Id: i,
Name: fmt.Sprintf("stu%02d", i),
Score: 100.0,
}
c.Students = append(c.Students, stu)
}
// JSON 序列化
data, err := json.Marshal(c)
if err != nil {
fmt.Println("json marshal failed")
return
}
fmt.Printf("转换的JSON为:%s\n", data)
// JSON 反序列化
str := `{"Title":"帅哥靓女二班","Students":[{"Id":1,"Name":"王帅","Score":100},{"Id":9,"Name":"陈美丽","Score":100}]}`
c1 := &Class{}
err = json.Unmarshal([]byte(str), c1)
if err != nil {
fmt.Println("json unmarshal failed")
return
}
fmt.Printf("反序列化的班级为:%+v\n", c1)
}
运行结果:
转换的JSON为:{"Title":"帅哥靓女一班","Students":[{"Id":1,"Name":"stu01","Score":100},{"Id":2,"Name":"stu02","Score":100},{"Id":3,"Name":"stu03","Score":100},{"Id":4,"Name":"stu04","Score":100},{"Id":5,"Name":"stu05","Score":100},{"Id":6,"Name":"stu06","Score":100},{"Id":7,"Name":"stu07","Score":100},{"Id":8,"Name":"stu08","Score":100},{"Id":9,"Name":"stu09","Score":100}]}
反序列化的班级为:&{Title:帅哥靓女二班 Students:[0xc000054560 0xc0000545a0]}
在Go 语言中,接受者的类型可以是任一类型,不仅仅是结构体,任何类型都可以拥有方法,上面的例子中是以结构体 person 来添加方法,我们可以通过 p.drame() 调用,就类似 JAVA 中的一个 public void drame(){} 方法,可以在其他类中调用本类的 drame() 方法。
当然结构体的声明还有别的方式,比如说结构体中可以用匿名字段,或者嵌套结构体,嵌套匿名结构体等,这个在项目实战中如果遇到,如果需要可以用一下。