Go Study Day04 - 结构体和包

2019/7/20 go学习提升

# 结构体

Go语言中的基础数据类型可以表示一些事物的基本属性,但是当我们想表达一个事物的全部或部分属性时,这时候再用单一的基本数据类型明显就无法满足需求了,Go语言提供了一种自定义数据类型,可以封装多个基本数据类型,这种数据类型叫结构体,英文名称struct。 也就是我们可以通过struct来定义自己的类型了。

Go语言中通过struct来实现面向对象。

# 结构体定义

type 结构体名 struct{
    字段1 字段1的类型
    字段2 字段2的类型
    ...
} 
// 如
type person struct {
	name string
	city string
	age  int8
}
1
2
3
4
5
6
7
8
9
10
11

结构体是值类型,需要注意的是,由于结构体中可能包含指针类型的成员,因此在复制结构体时,仅会复制指针的值,而不会复制指针所指向的内存地址和数据。这意味着在复制结构体时,新的结构体和原始结构体可能会共享某些数据。

# 结构体初始化

# 先声明再赋值

var p person // 声明一个person类型的变量p
p.name = "元帅"
p.age = 18
fmt.Println(p)
1
2
3
4

# 声明同时初始化

  • 键值对初始化
// 键值对初始化
var p2 = person{
	name: "冠华",
	age:  15,
}
fmt.Println(p2)
1
2
3
4
5
6
  • 值列表初始化
// 值列表初始化
var p3 = person{
	"理想",
	100,
}
fmt.Println(p3)
1
2
3
4
5
6

注意事项:

  1. 两者不能混用
  2. 没有赋值的字段会使用对应类型的零值.

# 匿名结构体

在定义一些临时数据结构等场景下还可以使用匿名结构体。

package main
     
import (
    "fmt"
)
     
func main() {
    var user struct{Name string; Age int}
    user.Name = "小王子"
    user.Age = 18
    fmt.Printf("%#v\n", user)
}
1
2
3
4
5
6
7
8
9
10
11
12

# 结构体指针

结构体是值类型,赋值的时候都是拷贝.

当结构体字段较多的时候,为了减少内存消耗可以传递结构体指针.

我们还可以通过使用new关键字对结构体进行实例化,得到的是结构体的地址。 格式如下:

var p2 = new(person)
fmt.Printf("%T\n", p2)     //*main.person
fmt.Printf("p2=%#v\n", p2) //p2=&main.person{name:"", city:"", age:0}
1
2
3

# 面向对象

# 构造函数

返回一个结构体变量的函数,为了实例化结构体的时候更省事儿.

func newPerson(name string, age int)person{
    return person{
        name: name,
        age: age,
    }
}
1
2
3
4
5
6

# 方法

方法是作用于特定类型的函数.

方法的定义:(万变不离其宗)

func (接收者变量 接收者类型)方法名(参数)返回值{
    // 方法体
}
1
2
3

# 接收者

接收者通常使用类型首字母的小写,不建议使用诸如thisself这样的.

# 值接收者和指针接收者的区别

使用值接收者的方法不能修改结构体变量

// SetAge2 设置p的年龄
// 使用值接收者
func (p Person) SetAge2(newAge int8) {
	p.age = newAge
}

func main() {
	p1 := NewPerson("小王子", 25)
	p1.Dream()
	fmt.Println(p1.age) // 25
	p1.SetAge2(30) // (*p1).SetAge2(30)
	
1
2
3
4
5
6
7
8
9
10
11
12

使用指针接收者的方法可以修改结构体的变量

// SetAge 设置p的年龄
// 使用指针接收者
func (p *Person) SetAge(newAge int8) {
	p.age = newAge
}
1
2
3
4
5

调用该方法:

func main() {
	p1 := NewPerson("小王子", 25)
	fmt.Println(p1.age) // 25
	p1.SetAge(30)
	fmt.Println(p1.age) // 30
}
1
2
3
4
5
6
  1. 需要修改接收者中的值
  2. 接收者是拷贝代价比较大的大对象
  3. 保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。

我们应该尽量使用指针接收者.

# 匿名字段

没有名字的字段.

//Person 结构体Person类型
type Person struct {
	string
	int
}

func main() {
	p1 := Person{
		"二狗",
		18,
	}
	fmt.Printf("%#v\n", p1)        //main.Person{string:"北京", int:18}
	fmt.Println(p1.string, p1.int) //北京 18
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 嵌套结构体

type address struct {
	province string
	city     string
}

type company struct {
	name string
	addr address // 嵌套
}

1
2
3
4
5
6
7
8
9
10

# 匿名嵌套结构体

type address struct {
	province string
	city     string
}

type company struct {
	name string
	address // 嵌套匿名结构体
}
1
2
3
4
5
6
7
8
9

# 匿名嵌套结构体的字段冲突

先在自己结构体找这个字段,找不到就去匿名嵌套的结构体中查找该字段

type address struct {
	province string
	city     string
}

type workPlace struct {
	province string
	city     string
}

type person struct {
	name    string
	age     int
	address   // 匿名嵌套结构体
	workPlace // 匿名嵌套结构体
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 结构体的“继承”

Go语言中使用结构体也可以实现其他编程语言中面向对象的继承。

//Animal 动物
type Animal struct {
	name string
}

func (a *Animal) move() {
	fmt.Printf("%s会动!\n", a.name)
}

//Dog 狗
type Dog struct {
	Feet    int8
	*Animal //通过嵌套匿名结构体实现继承
}

func (d *Dog) wang() {
	fmt.Printf("%s会汪汪汪~\n", d.name)
}

func main() {
	d1 := &Dog{
		Feet: 4,
		Animal: &Animal{ //注意嵌套的是结构体指针
			name: "乐乐",
		},
	}
	d1.wang() //乐乐会汪汪汪~
	d1.move() //乐乐会动!
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 结构体与JSON

JSON:是一种跨语言的数据格式.多用于在不同语言间传递数据

// 1.序列化:   把Go语言中的结构体变量 --> json格式的字符串
// 2.反序列化: json格式的字符串   --> Go语言中能够识别的结构体变量

type person struct {
	Name string `json:"name" db:"name" ini:"name"`
	Age  int    `json:"age"`
}

func main() {
	p1 := person{
		Name: "周林",
		Age:  9000,
	}
	// 序列化
	b, err := json.Marshal(p1)
	if err != nil {
		fmt.Printf("marshal failed, err:%v", err)
		return
	}
	fmt.Printf("%v\n", string(b))
	// 反序列化
	str := `{"name":"理想","age":18}`
	var p2 person
	json.Unmarshal([]byte(str), &p2) // 传指针是为了能在json.Unmarshal内部修改p2的值
	fmt.Printf("%#v\n", p2)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 包(package)

包的定义 -->package关键字,包名通常是和目录名一致,不能包含-

  • 一个文件夹就是一个包
  • 文件夹里面放的都是.go文件

包的导入 --> import

  • 包的路径从GOPATH/src后面的路径开始写起,路径分隔符用/
  • 想被别的包调用的标识符都要首字母大写!
  • 单行导入和多行导入
  • 导入包的时候可以指定别名
  • 导入包不想使用包内部的标识符,需要使用匿名导入sql包导入时会讲这个
  • 每个包导入的时候会自动执行一个名为init()的函数,它没有参数也没有返回值也不能手动调用
  • 包导入的时候会自动执行
  • 一个包里面只有一个init()
  • 多个包中都定义了init()函数,则它们的执行顺序见下图:

1564219349069

# 定义包

package packagename
1

# 包的引入

要在当前包中使用另外一个包的内容就需要使用import关键字引入这个包,并且import语句通常放在文件的开头,package声明语句的下方。完整的引入声明语句格式如下:

import importname "path/to/package"
1
  • importname:引入的包名,通常都省略。默认值为引入包的包名。

  • path/to/package:引入包的路径名称,必须使用双引号包裹起来。

  • Go语言中禁止循环导入包。

    批量引入

    import (
        "fmt"
      	"net/http"
        "os"
    )
    
    1
    2
    3
    4
    5

# init函数

在每一个Go源文件中,都可以定义任意个如下格式的特殊函数:

func init(){
  // ...
}
1
2
3

这种特殊的函数不接收任何参数也没有任何返回值,我们也不能在代码中主动调用它。当程序启动的时候,init函数会按照它们声明的顺序自动执行。

一个包的初始化过程是按照代码中引入的顺序来进行的,所有在该包中声明的init函数都将被串行调用并且仅调用执行一次。每一个包初始化的时候都是先执行依赖的包中声明的init函数再执行当前包中声明的init函数。确保在程序的main函数开始执行时所有的依赖包都已初始化完成。

# go module介绍

Go module 是 Go1.11 版本发布的依赖管理方案,从 Go1.14 版本开始推荐在生产环境使用,于Go1.16版本默认开启。Go module 提供了以下命令供我们使用:

命令 介绍
go mod init 初始化项目依赖,生成go.mod文件
go mod download 根据go.mod文件下载依赖
go mod tidy 比对项目文件中引入的依赖与go.mod进行比对
go mod graph 输出依赖关系图
go mod edit 编辑go.mod文件
go mod vendor 将项目的所有依赖导出至vendor目录
go mod verify 检验一个依赖包是否被篡改过
go mod why 解释为什么需要某个依赖

# GOPROXY

GOPROXY 的默认值是:https://proxy.golang.org,direct,由于某些原因国内无法正常访问该地址,所以我们通常需要配置一个可访问的地址。目前社区使用比较多的有两个https://goproxy.cnhttps://goproxy.io,当然如果你的公司有提供GOPROXY地址那么就直接使用。设置GOPAROXY的命令如下:

go env -w GOPROXY=https://goproxy.cn,direct
1

# GOPRIVATE

设置了GOPROXY 之后,go 命令就会从配置的代理地址拉取和校验依赖包。当我们在项目中引入了非公开的包(公司内部git仓库或 github 私有仓库等),此时便无法正常从代理拉取到这些非公开的依赖包,这个时候就需要配置 GOPRIVATE 环境变量。GOPRIVATE用来告诉 go 命令哪些仓库属于私有仓库,不必通过代理服务器拉取和校验。

GOPRIVATE 的值也可以设置多个,多个地址之间使用英文逗号 “,” 分隔。我们通常会把自己公司内部的代码仓库设置到 GOPRIVATE 中,例如:

$ go env -w GOPRIVATE="git.mycompany.com"
1

# 使用

go mod init 项目名

Last Updated: 2023/4/27
只爱西经
林一