- 结构体就是一种自定义的数据结构,它可以用来描述万事万物,比如用结构体来描述一个人所具有的特性,比如名字、年龄、住址等特性
// 用结构体来表示一个人所具有的特性
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. 方法
- Go语言的方法必须绑定到自定义的数据类型上
- 在 Go 中 方法 和 函数 是两个概念的东西。
- 函数的定义参考文章:第10节 Go 中的函数
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)
}
- 如果希望调用结构体方法时修改结构体的属性字段值,可以通过结构体指针的方式,实际开发中也推荐这种方式
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)
}
注意:(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)
}