1.1struct的简介
◼ Go通过结构体struct和interface实现oop(面向对象编程)
◼ struct的成员(也叫属性或字段)可以是任何类型,如普通类型、复合类型、函数、map、interface、struct等
1.2 struct详解
(1) struct定义
//示例
type Student struct {
name string
age int // 小写私有成员(对外不可见)
Class string // 首字母大写则该成员为公有成员(对外可见)
}
(2) struct声明与初始化
var stu1 Student //struct声明
var stu2 *Student= &Student{} //简写stu2 := &Student{}
var stu3 *Student = new(Student)//简写stu3 := new(Student)
(3) 自定义构造函数
// 通过工厂模式自定义构造函数方法
func Newstu(name1 string,age1 int,class1 string) *Student {
return &Student{name:name1,age:age1,Class:class1}
}
func main() {
stu1 := Newstu(“jacky",25,"math")
fmt.Println(stu1.name) // jacky
}
(4) struct tag–json应用场景
应用场景示例,json序列化操作(序列化演示)
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
var stu = Student{Name: "darren", Age: 34}
data, err := json.Marshal(stu)
if err != nil {
fmt.Println("json encode failed err:", err)
return
}
fmt.Println(string(data)) //{"name":"darren","age":34}
}
(5) struct匿名成员(字段、属性)
◼ 结构体中,每个成员不一定都有名称,也允许字段没有名字,即匿名成员。
◼ 匿名成员的一个重要作用,可以用来实现oop中的继承。
◼ 同一种类型匿名成员只允许最多存在一个。
◼ 当匿名成员是结构体时,且两个结构体中都存在相同字段时,优先选择最近的字段。
type Person struct {
Name string
Age int
}
type Student struct {
score string
Age int
Person // 匿名内嵌结构体
}
//代码:1-4.go
func main() {
var stu = new(Student)
stu.Age = 34 //优先选择Student中的Age
fmt.Println(stu.Person.Age, stu.Age) // 0,34
}
(6) struct-继承、多继承
◼ 当结构体中的成员也是结构体时,该结构体就继承了这个结构体,继承了其所有的方法与属性,当然有多个结构体成员也就是多继承。
◼ 访问父结构中属性也使用“.”,但是当子结构体中存在和父结构中的字段相同时候,只能使用:"子结构体.父结构体.字段"访问父结构体中的属性,如上面示例的stu.Person.Age
◼ 继承结构体可以使用别名,访问的时候通过别名访问,如下面示例man1.job.Salary
type Person struct {
Name string
Age int
}
type Teacher struct {
Salary int
Classes string
}
type man struct {
sex string
job Teacher //别名,继承Teacher 这个时候就不是匿名了
Person //继承Person
}
func main() {
var man1 = new(man)
man1.Age = 34
man1.Name = "darren"
man1.job.Salary = 100000
fmt.Println(man1, man1.job.Salary) //&{ {8500 } {darren 34}} 8500
}
(7) struct-结构体中的方法
方法是什么
◼ Go方法是作用在接受者(个人理解成作用对象)上的一个函数,接受者是某种类型的变量,因此方法是一种特殊类型的函数。
◼ 接收者可以是任何类型,不仅仅是结构体,Go中的基本类型(int,string,bool等)也是可以,或者说数组的别名类型,甚至可以是函数类型。但是,接受者不能是一个接口类型,因为接口是一个抽象的定义,方法是一个具体实现。
◼ 一个类型加上它的方法等价于面向对象中的一个类。一个重要的区别是:在 Go 中,类型的代码和绑定在它上面的方法的代码可以不放置在一起,它们可以存在在不同的源文件,唯一的要求是:它们必须是同一个包的。
◼ 类型 T(或 *T)上的所有方法的集合叫做类型 T(或 *T)的方法集。
◼ 因为方法是函数,所以同样的,不允许方法重载,即对于一个类型只能有一个给定名称的方法。但是如果基于接收者类型,是有重载的:具有同样名字的方法可以在 2 个或多个不同的接收者类型上存在,比如在同一个包里这么做是允许的
◼ 别名类型不能有它原始类型上已经定义过的方法(因为别名类型和原始类型底层是一样的)。
type Person struct {
Name string
Age int
}
func (p Person) Getname() string { //p代表结构体本身的实列,类似python中的self,这里p可以写为self
fmt.Println(p.Name)
return p.Name
}
func main() {
var person1 = new(Person)
person1.Age = 34
person1.Name = "darren"
person1.Getname() // darren
}
(8) struct-结构体中的方法-方法和函数的区别
◼ 方法只能被其接受者调用
◼ 接收者是指针时,方法可以改变接受者的值(或状态),函数也能做到
◼ 接受者和方法必须在同一个包内
(9) 方法的接受者是值或者指针的区别
◼ 当接受者是一个值的时候,这个值是该类型实例的拷贝
◼ 如果想要方法改变接受者的数据,就在接受者的指针类型上定义该方法。否则,就在普
通的值类型上定义方法。
**总结:**指针方法和值方法都可以在指针或者非指针上被调用。也就是说,方法接收者是
指针类型时,指针类型的值也是调用这个方法,反之亦然。
func (c Circle) expand() {
c.Radius *= 2 // 值方法不能改变值
}
func (c *Circle) expand2() {
c.Radius *= 2 // 指针方法可以改变值
}
func main() {
var c = Circle{Radius: 50}
fmt.Println(c.Area(), c.Circumference())
// 指针变量调用方法形式上是一样的
var pc = &c
pc.expand2()
fmt.Println(pc.Area(), pc.Circumference())
}
(10) struct-内存分布
go中的结构体内存布局和c结构体布局类似,每个成员的内存分布是连续的
type Student struct {
Name string // 16 byte
Age int64 // 8 byte
wight int64 // 8 byte
high int64 // 8 byte
score int64 // 8 byte
}
*什么是字节对齐:*访问特定类型变量时,需要访问特定内存地址,各种类型数据按照一定规则在空间排列,而不是顺序排放,这就是对齐。
*为什么要对齐:*不同的平台的寻址方式不同,有的需要从偶地址开始访问,如果从奇数地址访问,需要读取两次,再进行高低字节的整合,效率就下降了。
对齐规则:
1.结构体成员变量的偏移地址是其对齐字节数的整数倍
2.结构体成员变量的偏移地址能够被其自身整除
3.结构体的大小是最大类型成员长度的整数倍