第14节 Go中的结构体

136 阅读4分钟
  • 结构体就是一种自定义的数据结构,它可以用来描述万事万物,比如用结构体来描述一个人所具有的特性,比如名字、年龄、住址等特性
// 用结构体来表示一个人所具有的特性
type Person struct {
    // 名字
    Name string
    // 年龄
    Age int
}
  • 结构体是 Go 语言支持面向对象编程(OOP)特性的重要手段。Go 中没有类(Class)的概念,可以理解 Go 中的结构体就等同于其他高级语言中的类(Class)
  • Go 的面向对象编程特性非常简洁,没有其它面向对象编程语言中的方法重载、构造函数、this指针等等;但Go 中仍然有继承、封装和多态的特性,只是实现方式有别于其它高级语言

1. 声明结构体

type 结构体名称 struct {
    属性字段1  数据类型 
    属性字段2  数据类型
}
  • 属性字段的数据类型可以是:基本类型引用类型。如果字段属性没有被赋值,那么都有默认值,布尔类型是 false、数值类型是 0、字符串是 "";切片、指针和map的默认值都是 nil(表示还未分配空间)
type Student struct {
	// 学生姓名
	Name string
	// 年龄
	Age int
	// 各科考试成绩
	TestScoreMap map[string]float64	
}

1.1 声明结构体

1.1.1 方式一

var student Student
func main() {

	var student Student

	student.Name = "Kate"
	student.Age = 25

	m := map[string]float64{
		"语文" : 98,
		"数学" : 100,
	}
	student.TestScoreMap = m
	// student= {Kate 25 map[数学:100 语文:98]}
	fmt.Println("student=",student)
}

1.1.2 方式二

使用{}来声明

var s Student = Student{}
func main() {
	var s Student = Student{}
	// { 0 map[]}
	fmt.Println(s)
        if(s.TestScoreMap == nil) {
		fmt.Println("TestScoreMap is nil")
	}

	m2 := map[string]float64{
		"英语" : 98,
		"数学" : 100,
	}
	// {Jack 30 map[数学:100 英语:98]}
	s = Student{"Jack", 30, m2}
	fmt.Println(s)
}

1.1.3 方式三

利用指针的方式来声明,指针的相关介绍请参考笔记:第8节 Go中的指针

var s3 *Student = new(Student)
var s5 *Student = &Student{}
func main() {
     var s3 *Student = new(Student)
     (*s3).Name = "Tom"
     (*s3).Age = 35

     // &{Tom 35 map[]}
     fmt.Println(s3)

     // Go语言设计者为了开发者方便,将 (*s3).Name = "Tom" 可以简写成 s3.Name = "Lucy"
     s3.Name = "Lucy"
     s3.Age = 22
     // {Lucy 22 map[]}
     fmt.Println(*s3)
     
     m3 := map[string]float64{
		"英语" : 98,
		"数学" : 100,
     }
     var s5 *Student = &Student{"韩梅梅", 21, m3}
     // obj={韩梅梅 21 map[数学:100 英语:98]},name=韩梅梅
     fmt.Printf("obj=%v,name=%v\n",*s5,(*s5).Name)
}

注意:这里 (*s5).Name 不能写出 *s5.Name 会编译报错,因为 . 运算符的优先级比 * 高 !!!

1.2 结构体的细节

  • 结构体的所有属性字段内存分配空间是连续的,可以打印内存地址查看,可通过 & 查看变量的内存地址
  • 结构体中的属性字段,可以给该字段打 Tag什么是Go语言结构体的 Tag

2. 方法

2.1 方法的声明

func (recevier type) methodName (参数列表) (返回值列表) {
    
    return 返回值
}
  • type :表示数据类型,可以是结构体,也可以是其他自定义的数据类型
  • recevier :type 数据类型的一个变量

2.1.1 结构体方法

package main

import "fmt"

type Person struct {
	Name string
}

func (p Person) getName() {
	fmt.Println("name=",p.Name)
}

func main() {
	var p Person = Person{"大飞"}
	p.getName()
}
  • 上面这段代码中,getName 方法绑定在 Person 结构体类型上,也就是该结构体的方法
  • getName 方法只能通过 Person 类型变量来调用
  • 变量调用方法时,比如p.getName,该变量本身也会作为一个参数传递到getName方法中,如果变量是值类型,则进行只拷贝;如果变量是引用类型,则进行行内存地址拷贝
package main

import "fmt"

type Person struct {
	Name string
}

func (p Person) getName() {
        // 这里修改了 名字,但是对 main 函数中的 p 变量没有影响
	p.Name = "aaaa"
	fmt.Println("name=",p.Name)
}

func main() {
	var p Person = Person{"大飞"}

	p.getName()
	fmt.Println("main name=",p.Name)
}

image.png

  • 如果希望调用结构体方法时修改结构体的属性字段值,可以通过结构体指针的方式,实际开发中也推荐这种方式
package main

import "fmt"

type Person struct {
	Name string
}

func (p *Person) setName() {
	p.Name = "bbb"
	fmt.Println("name=",p.Name)
}

func main() {
	var p Person = Person{"大飞"}

	p.setName()
	fmt.Println("set name=",p.Name)
}

image.png 注意:(1)因为这里是指针,所以标准写法是 (*p).Name = "bbb",go底层做了优化 p.Name = "bbb" 等价于 (*p).Name = "bbb"
(2)方法调用处要传递变量地址,(&p).setName(),go底层做了优化,p.setName() 等价于 (&p).setName()

  • 方法的访问权限,方法首字母小写,只能本包访问,首字母大写,可以在本包和其他包访问
  • 如果一个数据类型实现了String()方法,那么 fmt.Println 会默认调用这个变量的String方法输出
package main

import "fmt"

type Person struct {
	Name string
	Age int
}

 func (p *Person) String()string {
 	return fmt.Sprintf("Person [name=%v,age=%v]",p.Name, p.Age)
}

func main() {
	var p Person = Person{"大飞", 12}
        // Person [name=大飞,age=12]
	fmt.Println(&p)
}