【11.0】Go语言基础之结构体
【一】什么是结构体
- 结构体是用户定义的类型,表示若干个字段(Field)的集合。
- 有时应该把数据整合在一起,而不是让这些数据没有联系。
- 这种情况下可以使用结构体。
- 例如,一个职员有firstName 、 lastName和age 三个属性,而把这些属性组合在一个结构体employee中就很合理。
【二】结构体的定义和使用
【1】结构体语法
type 名字 struct{
字段1 类型
字段2 类型
}
// 定义了一个结构体,用户自定义的类型
type Person struct {
name string
age uint8
sex string
hobby []string
}
【2】结构体的定义与使用
package main
import "fmt"
// 结构体 :
//结构体是用户定义的类型,表示若干个字段(Field)的集合。
//有时应该把数据整合在一起,而不是让这些数据没有联系。这种情况下可以使用结构体
// 结构体的标准语法
/*
type 名字 struct{
字段1 类型
字段2 类型
}
*/
// [1]定义了一个结构体,用户自定义的类型
type Person struct {
name string
age uint8
sex string
hobby []string
}
func main() {
// [2]使用结构体
var person Person
// 结构体是值类型 : 定义没有初始化,打印后有值
fmt.Println("这是定义的结构体 :>>>> ", person) // 这是定义的结构体 :>>>> { 0 []}
}
【3】结构体初始化
package main
import "fmt"
// [1]定义了一个结构体,用户自定义的类型
type Person struct {
name string
age uint8
sex string
hobby []string // 切片类型
}
func main() {
// [2]使用结构体并且始化
var person Person = Person{}
fmt.Println("这是定义的结构体无值传入 :>>>> ", person) // 这是定义的结构体无值传入 :>>>> { 0 []}
hobby := make([]string, 3, 4)
var person1 Person = Person{"Dream", 18, "男", hobby} // 按位置传参,符合顺序,并且不能少传参数
fmt.Println("这是定义的结构体有值传入 :>>>> ", person1) // 这是定义的结构体有值传入 :>>>> {Dream 18 男 [ ]}
var person2 Person = Person{name: "Dream", age: 18, sex: "男"} // 不按位置传参,可以不符合顺序,并且可以少传参数
fmt.Println("这是定义的结构体有值传入 :>>>> ", person2) // 这是定义的结构体有值传入 :>>>> {Dream 18 男 []}
}
【4】结构体使用
package main
import "fmt"
// [1]定义了一个结构体,用户自定义的类型
type Person struct {
name string
age uint8
sex string
hobby []string // 切片类型
}
func main() {
// [2]使用结构体并且始化
hobby := make([]string, 3, 4)
var person Person = Person{"Dream", 18, "男", hobby} // 按位置传参,符合顺序,并且不能少传参数
fmt.Println("这是定义的结构体有值传入 :>>>> ", person) // 这是定义的结构体有值传入 :>>>> {Dream 18 男 [ ]}
// 结构体的使用
// 取值
fmt.Println("这是取值并打印 :>>>> ", person.name) // 这是取值并打印 :>>>> Dream
// 修改值
person.name = "Hope"
fmt.Println("这是修改值并打印 :>>>> ", person.name) // 这是修改值并打印 :>>>> Hope
// 增加值 ---> 切片类型,必须先初始化才能使用
person.hobby[0] = "音乐"
// 会报错,越界。切片只能按长度的下标取值,不能按容量的下标取值
// person.hobby[3] = "乒乓球" // panic: runtime error: index out of range [3] with length 3
// append 追加值
person.hobby = append(person.hobby, "乒乓球")
fmt.Println("这是增加值并打印 :>>>> ", person.hobby) // 这是增加值并打印 :>>>> [音乐 乒乓球]
}
【5】创建匿名结构体
package main
func main() {
// 创建匿名结构体 ---> 结构体没有名字 ----> 一般定义在函数内部 ----> 只使用一次
// 没有 type 和 名字
p := struct {
name string
age int
}{
// 定义匿名结构体并初始化
name: "Dream",
age: 18,
}
}
【6】结构体的零值
package main
import "fmt"
type Person struct {
name string
age uint8
sex string
hobby []string // 切片类型
}
func main() {
// 结构体 的零值 --- 结构体是值类型 --- 空值不为 nil , 是每个结构体参数类型的零值
var person Person
fmt.Println("结构体 person 的零值 :>>>> ", person) // 结构体 person 的零值 :>>>> { 0 []}
}
【7】结构体当函数参数传递
package main
import "fmt"
type Person struct {
name string
age uint8
sex string
hobby []string // 切片类型
}
func main() {
// 结构体作为函数参数
var person Person
fmt.Println("结构体 person 的初识值 :>>>> ", person)
// 调用函数 --- 值类型当参数传递不会影响原来的值
index(person)
fmt.Println("调用函数后 ,结构体 person 的初识值 :>>>> ", person)
//结构体 person 的初识值 :>>>> { 0 []}
//这是在函数内部修改过后的结构体 :>>>> {梦梦 0 []}
//调用函数后 ,结构体 person 的初识值 :>>>> { 0 []}
}
func index(p Person) {
p.name = "梦梦"
fmt.Println("这是在函数内部修改过后的结构体 :>>>> ", p)
}
【8】访问结构体字段
package main
import "fmt"
type Person struct {
name string
age uint8
sex string
hobby []string // 切片类型
}
func main() {
// 访问结构体字段 : 通过 . 访问,如果结构体某个字段是引用类型,引用类型必须要初始化
var person Person = Person{name: "Dream", age: 18}
fmt.Println("访问 结构体 person 的值 :>>>> ", person.name)
fmt.Println("访问 结构体 person 的值 :>>>> ", person.age)
//访问 结构体 person 的值 :>>>> Dream
//访问 结构体 person 的值 :>>>> 18
}
【9】匿名字段
package main
import "fmt"
func main() {
// 匿名字段 ----> 字段没有名字 ----> 匿名字段类型不能重复 , 可以和有名字段混合使用
type Animal struct {
string // 定义了字段类型,但是字段没有名字
int // 类型名就是字段名
}
// 初始化结构体
// 按位置赋初值
var animal Animal = Animal{"梦梦", 18}
fmt.Println("这是初始化后的 结构体 animal :>>>> ", animal) // 这是初始化后的 结构体 animal :>>>> {梦梦 18}
// 按关键字赋初值 -- 如果没有字段名,则类型名就是字段名
var animal1 Animal = Animal{string: "蚩梦", int: 19}
fmt.Println("这是初始化后的 结构体 animal1 :>>>> ", animal1) // 这是初始化后的 结构体 animal1 :>>>> {蚩梦 19}
// 字段取值
fmt.Println("结构体 animal 的取值 :>>>> ", animal1.string) // 结构体 animal 的取值 :>>>> 蚩梦
}
【10】结构体嵌套
(1)结构体嵌套有名结构体
package main
import "fmt"
func main() {
// 结构体嵌套 --- 结构体中套结构体
type Duck struct {
name string
age int
}
type Chick struct {
name string
height float32
duck Duck // 可以是自己定义的结构体
}
// 结构体初始化
var chick Chick = Chick{name: "战斗鸡", height: 88, duck: Duck{name: "战斗鸭", age: 99}}
fmt.Println("这是结构体初始化后的 chick :>>>> ", chick) // 这是结构体初始化后的 chick :>>>> {战斗鸡 88 {战斗鸭 99}}
// 结构体的取值
fmt.Println("这是结构体的取值 :>>>> ", chick.duck.name) // 这是结构体的取值 :>>>> 战斗鸭
}
(2)结构体嵌套匿名结构体
package main
import "fmt"
func main() {
// 结构体嵌套 --- 结构体中套结构体
type Chick struct {
name string
height float32
// 可以是自己定义的结构体 --- 匿名结构体
duck struct {
name string
age int
}
}
// 结构体初始化
var chick Chick = Chick{name: "战斗鸡", height: 88}
chick.duck.name = "战斗鸭"
chick.duck.age = 66
fmt.Println("这是结构体初始化后的 chick :>>>> ", chick) // 这是结构体初始化后的 chick :>>>> {战斗鸡 88 {战斗鸭 66}}
}
【11】字段提升
package main
import "fmt"
func main() {
// 结构体嵌套 + 匿名字段 --- 字段提升
type Duck struct {
name string
age int
}
type Chick struct {
name string
height float32
Duck
}
// 结构体初始化
var chick Chick = Chick{name: "战斗鸡", height: 88, Duck: Duck{name: "战斗鸭", age: 66}}
fmt.Println("这是结构体初始化后的 chick :>>>> ", chick) // 这是结构体初始化后的 chick :>>>> {战斗鸡 88 {战斗鸭 66}}
// 取值
fmt.Println("取出内层结构体的值 :>>>> ", chick.Duck.name)
// 字段提升,本来是 Duck 的字段,但是提升到了 Chick 身上
// 但是字段名重复就不能再提升了
fmt.Println("取出内层结构体的值 :>>>> ", chick.name)
// 字段提升类似于面向对象的继承 ----> 相当于 Chick 继承了 Duck ----> 子类中调用父类的属性 ----> 子类对象.属性名
// Chick 重写了 Duck 的name字段,所以在取值时,自己有就先用自己的,自己没有再用继承的
// 在子类中使用父类的属性 super().属性名 ----> 对应到 Go 语言中,就是 父类名.属性名 (Duck.name)
}
【12】导出结构体和字段
(1)定义包
nature/Animal.go
package nature
type Animal struct {
name string
Age int // 变量名首字母大写开头,表示导出字段 ----> 可以在外部包使用
}
(2)外部导出包内部字段
package main
import (
"Project1/day03/nature"
"fmt"
)
func main() {
// 导出结构体字段 ----> 包名下 的变量首字母大写,表示导出,在其他包可以使用
// 字段名和结构体名字规则一样,大写可以导出使用,小写只能包内部使用
// Unexported field 'name' usage
// 字段名首字母小写表示非导出字段,外部包不能使用
// animal := nature.Animal{name: "小梦", Age: 18}
// 字段名首字母大写则表示导出字段,外部可以使用
animal := nature.Animal{Age: 18} // 不能按位置传参,只能按关键字传参
fmt.Println("这是创建的 animal :>>>> ", animal) // 这是创建的 animal :>>>> { 18}
}
【13】结构体相等性
(1)可以比较
- 如果字段都是可比较的,那结构体可以比较
package main
import "fmt"
func main() {
// 结构体相等性
// 结构体是否能比较,取决于结构体字段
// 如果字段都是可比较的,那结构体可以比较
// 如果有不可以比较的字段,结构体就不可以比较
type Animal struct {
name string
age int
}
// 值类型 , 可以 == 比较 ; 引用类型只能 ==nil
var a, b Animal = Animal{name: "梦梦", age: 18}, Animal{name: "梦梦", age: 18}
fmt.Println("定义两个相同的机构体,并判断是否相等 :>>>> ", a == b) // 定义两个相同的机构体,并判断是否相等 :>>>> true
}
(2)不可以比较
- 如果有不可以比较的字段,结构体就不可以比较
package main
import "fmt"
func main() {
// 结构体相等性
// 结构体是否能比较,取决于结构体字段
// 如果字段都是可比较的,那结构体可以比较
// 如果有不可以比较的字段,结构体就不可以比较
type Animal struct {
name *string
age int
hobby []string // 切片类型不能比较
}
var x []int = []int{1, 2, 3}
var y []int = []int{1, 2, 3}
// 切片类型不能 == 比较
fmt.Println("定义两个相同的机构体,并判断是否相等 :>>>> ", x == y) // Invalid operation: x == y (the operator == is not defined on []int)
}
package main
import "fmt"
func main() {
// 结构体相等性
// 结构体是否能比较,取决于结构体字段
// 如果字段都是可比较的,那结构体可以比较
// 如果有不可以比较的字段,结构体就不可以比较
type Animal struct {
name *string
age int
hobby []string // 切片类型不能比较
}
// 值类型 , 可以 == 比较 ; 引用类型只能 ==nil
var a, b Animal = Animal{}, Animal{} // 初始化
// 但是不能放在一起比较
fmt.Println("定义两个相同的机构体,并判断是否相等 :>>>> ", a == b) //Invalid operation: a == b (the operator == is not defined on Animal)
}
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17827374.html