Go语言第六章(结构体)

📅 2026/8/7 8:03:06
Go语言第六章(结构体)
结构体结构体是 Go 中的一种复合数据类型用于将多个不同类型的数据组合在一起形成一个新的数据类型。日常开发里用结构体描述「一个人」「一辆车」「一条订单」这类对象非常常见。定义结构体用typestruct定义一种新类型type结构体名struct{字段名 类型 字段名 类型 字段名 类型}实操定义一个Person包含姓名、年龄和爱好packagemainimportfmttypePersonstruct{NamestringAgeintHobby[]string}funcmain(){person:Person{Name:John,Age:20,Hobby:[]string{reading,swimming},}fmt.Println(person)}Person{...}是字面量初始化按字段名赋值顺序可以随便写没写的字段会是该类型的零值。字段读写通过.访问和修改字段packagemainimportfmttypePersonstruct{NamestringAgeint}funcmain(){p:Person{Name:张三,Age:18}fmt.Println(p.Name)// 张三p.Age20fmt.Println(p.Age)// 20}几种初始化方式packagemainimportfmttypePersonstruct{NamestringAgeint}funcmain(){// 1. 按字段名初始化推荐可读性好p1:Person{Name:张三,Age:18}// 2. 按字段顺序初始化字段多时容易写错不推荐p2:Person{李四,20}// 3. 只给部分字段赋值其余为零值p3:Person{Name:王五}// Age 为 0// 4. 先声明再赋值全部为零值varp4 Person p4.Name赵六p4.Age22// 5. 取地址得到 *Personp5:Person{Name:钱七,Age:25}fmt.Println(p1,p2,p3,p4,p5)}指针形式p5访问字段时Go 会自动解引用写p5.Name即可不必写(*p5).Name。结构体嵌套一个结构体里可以包含另一个结构体类型的字段用来组合更复杂的数据packagemainimportfmttypeCarstruct{BrandstringModelstringYearint}typePersonstruct{NamestringAgeintHobby[]stringCar Car}funcmain(){person:Person{Name:John,Age:20,Hobby:[]string{reading,swimming},Car:Car{Brand:Toyota,Model:Camry,Year:2020,},}fmt.Println(person)fmt.Println(person.Car.Brand)// Toyota}这里Car是命名字段访问时要写person.Car.Brand。结构体嵌入如果字段类型前不写字段名就是嵌入匿名字段。嵌入后被嵌入类型的字段和方法会「提升」到外层可以直接用packagemainimportfmttypeCarstruct{BrandstringModelstringYearint}typePersonstruct{NamestringAgeintCar// 嵌入没有字段名只有类型}funcmain(){person:Person{Name:John,Age:20,Car:Car{Brand:Toyota,Model:Camry,Year:2020,},}// 提升后可以直接访问fmt.Println(person.Brand)// Toyota// 也可以走完整路径fmt.Println(person.Car.Model)// Camry}嵌套是「有名字的组合」嵌入是「把另一个类型的能力提升过来」。两者都能组合数据嵌入更适合表达「is-a / 带有某种能力」的关系。方法方法是「绑在某个类型上的函数」。接收者写在func和函数名之间func(接收者 类型)方法名(参数列表)返回值{// ...}值接收者packagemainimportfmttypePersonstruct{NamestringAgeint}func(p Person)Introduce(){fmt.Printf(我叫 %s今年 %d 岁\n,p.Name,p.Age)}funcmain(){p:Person{Name:张三,Age:18}p.Introduce()// 我叫 张三今年 18 岁}值接收者拿到的是副本方法里改字段不会影响原变量。指针接收者需要修改结构体本身时用指针接收者packagemainimportfmttypePersonstruct{NamestringAgeint}func(p*Person)GrowUp(){p.Age}func(p Person)Introduce(){fmt.Printf(我叫 %s今年 %d 岁\n,p.Name,p.Age)}funcmain(){p:Person{Name:张三,Age:18}p.GrowUp()p.Introduce()// 我叫 张三今年 19 岁}调用时写p.GrowUp()即可Go 会自动取地址。习惯上要改状态用指针接收者只读、小结构体可以用值接收者。同一类型的方法最好统一风格不要有的用值、有的用指针混着来。匿名结构体不提前type定义直接在用的地方写struct { ... }适合只临时用一次的场景packagemainimportfmtfuncmain(){person:struct{NamestringAgeint}{Name:John,Age:20,}fmt.Println(person.Name,person.Age)}配置、单次返回、测试数据等「用完就丢」的结构用匿名结构体很方便。若多处复用还是起个名字更清晰。泛型结构体Go 1.18 起结构体也可以带类型参数packagemainimportfmttypePerson[Tstring|int]struct{NamestringAgeintPhone T}funcmain(){person:Person[string]{Name:John,Age:20,Phone:010-10086,}person2:Person[int]{Name:John,Age:20,Phone:10086,}fmt.Println(person)fmt.Println(person2)}Person[T string | int]表示Phone只能是string或int。创建时要写明具体类型如Person[string]、Person[int]。作业定义与初始化定义Book结构体书名、作者、页数用至少两种方式创建实例并打印。嵌套定义Address城市、街道和User姓名、地址创建用户并打印「姓名住在城市的街道」。嵌入把上一题的Address改成嵌入到User用提升后的字段直接打印城市。方法给Book写值接收者方法Info()打印信息再写指针接收者方法AddPages(n int)增加页数调用后验证页数有变。综合定义Rectangle宽、高写方法Area()和Perimeter()返回面积和周长并在main里验证。