第四篇:结构体
第四篇:结构体
目录
一、结构体的使用
结构体是一系列属性的集合。【没有方法】
1 有名结构体
// 定义结构体【在entity包中定义】
type Person struct {
Name string // 大写,外部包可以进行引用;小写,仅表明内部包可以使用
Age int //
Sex string
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
import (
"day01/entity"
"fmt"
)
// 1 结构体的基本使用【访问结构体字段使用".",但是注意大小写】
func main() {
var per entity.Person // 生成一个结构体对象 【包名.结构体名】(大写代表外部包可以进行引用,小写仅表明内部包可以使用)
fmt.Println(per) // { 0 } 打印结构体对象
per.Name = "yangyi" // 结构体属性赋值
per.Age = 18
per.Gender = "male"
fmt.Println(per) // {yangyi 18 male}
}
// 2 结构体定义并附初值
var person1 entity.Person = entity.Person{Age: 18, Name: "yangyi"} // 指名道姓,可以不按位置,并且少传,其余为传递的参数默认为零值
person2 := entity.Person{"yangyi", 18, "male"} // 不指名道姓,必须按位置
在python每个文件就是一个模块。而go中的一个包可以想象成一个大的文件,在其中的名字不能重复。
2 匿名结构体
// 匿名结构体(定义在内部【函数,结构体】,仅使用一次。就像python中的匿名函数一样)
// 有什么用?
// 假如说有多个变量,一个一个用,还不如放到匿名结构体中,一个代表多个
school := struct { // 将匿名结构体赋值给一个变量【因为没有名字,变量只能用类型推导和简略声明两种方式】
SchoolId int64
SchoolName string
}{100, "ccut"} // 实例化匿名结构体
school.SchoolId = 99 // 修改匿名函数的值
3 结构体零值
属性的零值,值类型,参数传递属于copy传递【函数传参,修改,不会改变原来的】。简单来说,结构体传递属于值传递。
var person entity.Person = entity.Person{"yangyi", 18, "male"}
fmt.Println(person) // {yangyi 18 male}
test(person) // {lei 18 male}
fmt.Println(person) // {yangyi 18 male}
func test(person entity.Person) {
person.Name = "lei"
fmt.Println(person)
}
4 结构体指针
// 1 定义结构体指针
var person *entity.Person // 结构体指针
fmt.Println(person) // <nil> 指针的零值
// 2 定义并初始化
var person *entity.Person = &entity.Person{"yangyi", 18, "male"} // 定义一个结构体指针
person1 := &entity.Person{"yangyi", 18, "male"}
fmt.Println(person) // &{yangyi 18 male}
fmt.Println(person1) // &{yangyi 18 male}
// 修改属性值
// 第一种方式: 不推荐
(*person).Name = "lei"
// 第二种方式: 推荐【语言层面自动进行处理】
person.Age = 20
fmt.Println(person) // &{lei 20 male}
5 匿名字段
匿名字段,内含匿名字段(字段没有名字),匿名字段的类型就是字段名,所以类型不能重复。
作用:变量提升。【其实就是面向对象的继承】
// 匿名字段,内含匿名字段(字段没有名字)
type Person struct {
string // 字段没有名字
Age int
Gender string
}
// 实例化匿名字段结构体对象
// 第一种方式: 按位置
person := Person{"yangyi", 18, "male"}
// 第二种方式: 按名字
person1 := Person{string:"yangyi", Age: 18, Gender: "male"} // 如果字段匿名,类型就是字段名【这个string有点过分了呀】
fmt.Println(person1.string) // yangyi 通过类型可以取到值。【哈哈】
6 结构体嵌套
结构体中套用结构体。
type Person struct {
Name string
Age int
Gender string
Hobby Hobby // 属性名是Hobby,属性类型为Hobby结构体
}
type Hobby struct {
HobbyId int
HobbyName string
}
// 按位置传递
// 第一种方式: 按位置
var person Person = Person{"yangyi", 18, "male", Hobby{2, "song"}}
// 第二种方式: 按名字
var person Person = Person{Name: "yangyi", Age: 18, Gender: "male", Hobby: Hobby{HobbyId: 2, HobbyName: "song"}}
// 结构体使用
fmt.Println(person.Name) // yangyi
fmt.Println(person.Hobby.HobbyName) // song
7 字段提升
字段提升像面向对象的继承。可以简单认为,字段提升就是面向对象的继承。【面向对象的继承: 子类继承父类,子类可以直接调用父类的方法】
type Person struct {
Name string
Age int
Gender string
Hobby // 这里将Hobby设置为匿名字段,此处仅为Hobby类型
}
type Hobby struct {
HobbyId int
HobbyName string
}
// 第一种方式: 按位置
var person Person = Person{"yangyi", 18, "male", Hobby{2, "song"}}
// 第二种方式: 按名字【此时Hobby: 中的Hobby是字段名】
var person Person = Person{Name: "yangyi", Age: 18, Gender: "male", Hobby: Hobby{HobbyId: 2, HobbyName: "song"}}
// 此处体现了字段提升的效果【匿名字段+结构体嵌套】
fmt.Println(person.Hobby.HobbyName) // song person.Hobby类似于面向对象中的super【java python中皆有】
fmt.Println(person.HobbyName) // song
如果结构体嵌套中,属性名重复,则可认为是派生,子类重写了父类中的方法。【不严谨的说明】
8 导出结构体和字段
大写字母开头,在外部包可以使用。
golang中根据首字母的大小写来确定可以访问的权限。
无论是
方法名、常量、变量名还是结构体的名称
,如果首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用。
9 结构体相等性
- 结构体都是值类型
- 如果结构体中的每一个字段都是可比较的,则该结构体也是可比较的。
- 如果两个结构体变量的对应字段相等,则这两个变量也是相等的。
- 如果结构体包含不可比较字段,则结构体变量也不可比较。
// 属性全是值类型,可以进行比较
type Person struct {
Name string
Age int
Gender string
}
// 值类型直接==比较,引用类型只能跟nil用==比较,且引用类型不能匿名
person1 := Person{"yangyi"}
person2 := Person{"yangyi"}
fmt.Println(person1 == person2) // true
person1 := Person{"yangyi"}
person2 := Person{"yangyi", 18}
fmt.Println(person1 == person2) // false
二、方法的使用
1 方法的定义和使用
方法其实就是一个函数,在func
这个关键字和方法名中间加入了一个特殊的接收器类型。接收器可以是结构体类型或者是非结构体类型。接收器可以在方法的内部调用。
// 1) 定义一个结构体
type Person struct {
Name string // 字段没有名字
Age int
Gender string
}
// 2) 给结构体绑定一个方法【绑定给对象的方法】
func(p Person) tellName(){ // p就是python中的self,绑定给Person的结构体对象
fmt.Println(p.Name)
}
// python写法
def tell_name(self): // 其实p就相当于python中的self。方法就是绑定给类或者结构体的
pass
/* 方法和函数的区别 */
// 方法【对象.方法调用】
func (p Person)TellName() {
// 该方法类可以使用p
fmt.Println(p.Name)
}
// 函数【正常调用】
func TestName(p Person) {
fmt.Println(p.Name)
}
1.1 值类型接收器
// 修改名字
// 1)修改名字【错误,其实名字并没有改,因为结构体是值类型】
func (p Person) changeName(name string) {
p.Name=name
fmt.Println(p)
}
person.changeName("lei") // 即可修改名字【并没有修改】
1.2 指针类型接收器
func (p *Person) changeName(name string) {
(*p).Name=name // 第一种方式
p.Name=name // 第二种方式【C中指针是 p->name = name】
fmt.Println(p)
}
当拷贝一个结构体的代价过于昂贵时,这种情况下就需要使用指针类型接收器,只会传递一个指针到方法内部使用。
2 匿名字段方法提升
方法提升。【结构体嵌套+字段提升,其中嵌套的结构体中绑定了方法,就能实现方法提升】。如果方法名重复,优先使用子结构体的。
type Person struct {
Name string
Age int
Gender string
Hobby // 这里将Hobby设置为匿名字段,此处仅为Hobby类型
}
type Hobby struct {
HobbyId int
HobbyName string
}
// 给结构体绑定方法
func (p *Person) tellName(){
fmt.Println((*p).Name)
}
func (p *Hobby) tellHobbyName(){
fmt.Println((*p).HobbyName)
}
func main() {
person := Person{"yangyi", 18, "male", Hobby{222, "song"}}
person.tellName() // yangyi
person.tellHobbyName() // song 方法提升
person.Hobby.tellHobbyName() // song
}
// 在方法中使用值接收器 与 在函数中使用值参数
type Person2 struct {
Name string
Age int
Gender string
}
// 1)在方法中使用值接收器
func (p Person)printName() {
fmt.Println(p.Name)
}
// 2)在函数中使用值参数
func printName(p Person) {
fmt.Println(p.Name)
}
// 在方法中使用指针接收器 与 在函数中使用指针参数
type Person struct {
Name string
Age int
Gender string
}
// 给结构体绑定方法
func (p Person) tellName(){ // 此时结构体是值传递
fmt.Println(p.Name)
}
person:=&Person{Name: "yangyi"} // 生成一个结构体对象指针,指针也可以直接点击方法【或属性】,进行调用
person.tellName() // yangyi
// 值类型接收器: 可以接受值类型;也可以接收指针类型;
// 函数的传参: 必须值对值,指针对指针
// 总结: 1 不管是结构体对象,还是结构体指针对象,都可以直接使用.来进行属性和方法的调用。2 方法的类型接收器,只是决定方法中使用的是值或者指针【是值类型的话,不进行修改;是指针类型的话,进行修改】
简单总结:不管是值类型接收器还是指针类型接收器,值和指针都能进行调用。如果是值类型接收器,不可以对结构体内容进行修改;如果是指针类型接收器,可以对结构体内容进行修改。
3 在非结构体上使用方法
// 在int类型上绑定一个add方法【不允许在非结构体上绑定方法】
func (i int) add() {
i = i + 1
// i++
}
可以在自定义的类型上绑定方法【不只是在结构体上可以绑定方法】
type Myint int
func (p *Myint)add() { // 指针类型接收器,可以进行原值修改
*p += 1 // (*p) += 1
}
func main() {
//person:=Person{Name: "yangyi"}
//person.tellName()
//fmt.Println(person.Name)
var num Myint = 1
num.add()
fmt.Println(num) // 2
num.add()
fmt.Println(num) // 3
num.add()
fmt.Println(num) // 4
}