> For the complete documentation index, see [llms.txt](https://golang-2.gitbook.io/handbook/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://golang-2.gitbook.io/handbook/ji-chu/lei-xing/jie-gou-ti.md).

# 结构体

结构体是一种自定义类型，是实现面向对象编程的基础，我们可以通过结构体去描述一个事物所具有的各项特性，形成对于对象的描述。如定义一个人的类型，并指定其所具有的一些特征，诸如姓名，性别，年龄，身高等，也能为我们所定义的这个人类型赋予各种功能方法，如自我介绍，开车，跳舞等。

## 结构体定义

在结构体中可以自定义各种类型的字段，类似于树形的结构，用于存放结构复杂的数据，关键字`struct`

结构体的定义格式如下：

```
type {结构体名称} struct{
    {字段名} {字段类型}
}
```

例子：定义结构体类型

```go
// 定义一个名为Person的结构体
type Person struct{
    // 定义字符串类型字段Name，
    Name string
    // 定义整型字段Age
    Age int8
}
```

## 结构体创建

有了结构体类型后，就可以用于创建结构体，在结构体创建时可以进行初始化赋值，也可以通过`变量名.字段名`的方式去读取和修改字段值

例子：创建结构体

```go
package main

import (
	"fmt"
)

// 定义一个名为Person的结构体
type Person struct {
	// 定义字符串类型字段Name，
	Name string
	// 定义整型字段Age
	Age int8
	// 定义浮点型字段Height
	Height float32
}

func main() {
	// 定义一个Person类型的结构体变量,并初始化其中的字段值
	var me Person = Person{
		Name: "wj",
		Age:  26,
	}
	// 修改结构体变量中的字段值
	me.Height = 1.78

	// 获取并输出结构体变量中的字段值
	fmt.Println(me.Name)
	fmt.Println(me.Age)
	fmt.Println(me.Height)
}
```

以上代码的执行结果：

```
wj
26
1.78
```

## 字段可访问性

与包类似，结构体中的定义的字段是具有可访问性的。以小写开头的字段或方法在结构体外部是无法访问到的，相当于私有字段。代码编写时应当遵守最小权限原则，即只有确定是要被外部访问的字段，才会将其设置为大写开头的公开字段，这是为了保证代码的安全性。

例子：定义具有私有字段的结构体类型

```go
// 定义一个名为Person的结构体
type Person struct{
    // 定义字符串类型字段Name，
    Name string
    // 定义整型字段Age
    Age int8
    // 定义浮点型私有字段weight
    weight float32
}
```

## 结构体方法

在结构体类型中可以定义各种方法，称为类型方法。区别是否该把方法定义为类型方法还是普通方法的依据是，该方法是否在逻辑上是每个类型实体都具有的一个功能，并且与当前类型实体中的属性都是有关联的。

例子：结构体方法

```go
package main

import (
	"fmt"
)

// 定义一个名为Person的结构体
type Person struct {
	// 定义字符串类型字段Name，
	Name string
	// 定义整型字段Age
	Age int8
	// 定义私有浮点型字段weight
	weight float32
}

func (p Person) Introduce() {
	// 通过结构体方法访问私有字段
	fmt.Printf("My name is %s,weight %f", p.Name, p.weight)
}

func main() {
	// 定义一个Person类型的结构体变量,并初始化其中的字段值
	var p1 Person = Person{
		Name:   "tony",
		Age:    32,
		weight: 147.8,
	}

	// 调用结构体方法
	p1.Introduce()
}
```

以上代码的执行结果：

```
My name is tony,weight 147.800003
```

结构体方法在执行的时候，会在内存中创建一个新的结构体并执行其中的方法，也就是说，在结构体方法中修改原结构体的内容是不生效的。我们可以借助于指针的引用传递效果，定义一个结构体指针方法，在该指针上调用方法时，可以借助于指针寻址到源结构体，从而修改源结构体中的值，达到在结构体方法中修改结构体值的效果

例子：结构体指针方法

```go
package main

import (
	"fmt"
)

// 定义一个名为Person的结构体
type Person struct {
	// 定义字符串类型字段Name，
	Name string
	// 定义整型字段Age
	Age int8
	// 定义私有浮点型字段weight
	weight float32
}

func (p *Person) ChangeName(newName string) {
	p.Name = newName
}

func main() {
	// 定义一个Person类型的结构体指针,并初始化其中的字段值
	var p1 *Person = &Person{
		Name:   "tony",
		Age:    32,
		weight: 147.8,
	}

	fmt.Println(p1.Name)

	// 通过结构体指针方法修改结构体中的值
	p1.ChangeName("mike")

	fmt.Println(p1.Name)
}
```

以上代码的执行结果：

```
tony
mike
```

{% hint style="info" %}
结构体指针方法只能通过结构体指针调用
{% endhint %}

结构体指针也可以调用结构体方法，在调用时，首先会把结构体方法转换成结构体指针方法，再对该转换后的结构体指针方法进行调用

## 结构体继承

结构体是每个面向对象语言都具有的特性，在golang中同样支持结构体继承，只是实现上没有那么复杂，其本质是对于结构体的层层嵌套，通过语法糖的形式缩写调用语句。

例子：结构体继承

```go
package main

import (
	"fmt"
)

// 定义结构体类型Ship
type Ship struct{}

// 为结构体类型Ship定义方法Ship()
func (b Ship) Sail() {
	fmt.Println("sailing the ship")
}

// 定义结构体类型Battleship
type Battleship struct {
	// 继承Ship
	Ship
}

func main() {
	// 定义Ship类型的变量s1
	var s1 Ship
	// 定义Battleship类型的变量s2
	var s2 Battleship

	// 直接调用s1的方法
	s1.Sail()
	// 通过语法糖隐式调用继承方法
	s2.Sail()
	// 显式调用继承方法
	s2.Ship.Sail()
}
```

以上代码的执行结果：

```
sailing the ship
sailing the ship
sailing the ship
```

{% hint style="info" %}
语法糖指的是开发语言为了便利编写者而支持的一些简易语法
{% endhint %}
