结构(struct)是一种自定义的方式来组成一个新的数据类型,结构是一个复合类型,类型中有成员。Go语言结构体是一种聚合数据类型,它是由零个或多个任意类型的值聚合而成的实体。每个值都称为结构的成员。描述现实世界中的实体以及实体对应的各种属性。结构属性也称为字段或成员,每个字段都有一个名称和类型,并且每个名称都是唯一的。可以是任何类型,比如普通类型、复合类型、函数、map、接口、struct等,所以我们可以把它理解为go语言中的一个“类”。定义结构体如下:typenamestruct{fieldName1type1fieldName2type2...}如下,定义User结构体:typeUserstruct{Namestringageint}Instantiation上面的定义只是一个类型,就像一个int一样,需要定义一个类型变量才可以被使用,类似于Java的类型。直接使用packagemainimport("fmt")typeUserstruct{Namestringageint}funcmain(){varuser1User//定义用户类型变量uservaruser2*User//类型指针,不分配内存,不能直接使用fmt.Println(user1,user2)//{0}}定义默认成员变量varuser1=User{Name:"abc"}fmt.Println(user1)funcNewUser()*User{return&User{Name:"abc",age:20}}使用内置函数new()分配内存并返回类型变量指针varuser=new(User)fmt.Println(user)//&{0}使用.访问varuserUseruser.Name="abc"user.age=20fmt。println(user)//{abc20}首字母大小写问题,成员大写表示在包外可见(即面向对象的公共属性),小写在包外不可见零值:结构体的零值为nilInitialvalue:structure当初始值为非nil时,每个成员对应类型的初始值空结构:空结构是没有字段的结构,空结构不占用内存packagemainimport("fmt""unsafe")funcmain(){user1:=struct{}{}user2:=struct{}{}fmt.Printf("%p,%dn",&user1,unsafe.Sizeof(user1))//0x585218,0fmt.Printf("%p,%dn",&user2,unsafe.Sizeof(user2))//0x585218,0}从上面可以看出,空结构的内存地址和大小是一样的。根据这个特点,使用空结构可以作为信号量,起到信号的作用,但不占用内存。例如,空结构类型的chan匿名结构没有类型名,可以不通过type关键字定义,直接使用。user:=struct{Namestruct}{Name:"abc"}fmt.Println(user)//{abc}只有结构体的所有成员都具有可比性,且成员的顺序、类型、数量正好是相同为了比较,可以使用==或!=运算符比较两个结构。packagemainimport("fmt")funcmain(){user1:=struct{Namestring}{Name:"abc"}user2:=struct{Namestring}{Name:"abc"}fmt.Println(user1==user2)//true}成员名称不一样packagemainimport("fmt")funcmain(){user1:=struct{Namestring}{Name:"abc"}user2:=struct{namestring}{name:"abc"}fmt.Println(user1==user2)//invalidoperation:user1==user2(mismatchedtypesstruct{Namestring}andstruct{namestring})}成员数量不一样packagemainimport("fmt")funcmain(){user1:=struct{Namestring}{Name:"abc"}user2:=struct{Namestringageint}{Name:"abc"}fmt.Println(user1==user2)//无效操作:user1==user2(mismatchedtypesstruct{Namestring}andstruct{Namestring;ageint})}成员类型不能比较packagemainimport("fmt")funcmain(){user1:=struct{Namestringmmap[int]}{Name:"abc"}user2:=struct{Namestringmmap[int]int}{Name:"abc"}fmt.Println(user1==user2)//输入lidoperation:user1==user2(structcontainingmap[int]intcannotbecompared)}orderisdifferent}{Name:"abc"}fmt.Println(user1==user2)//invalidoperation:user1==user2(mismatchedtypesstruct{Namestring;ageint}andstruct{ageint;Namestring})}其实整个结构都是一个类型(比如int),成员顺序和类型不同,整体结构不同,所以对于强类型语言来说是无法比较的。如果对应的类型完全一样,需要注意成员是否可以进行比较,比如slice、map等Go语言没有面向对象的概念,但是结构可以看成一个类,可以实现面向对象的特性,例如通过组合和嵌入来继承匿名字段。匿名字段是不显示在结构中的名称。它们是嵌入一个或多个A结构的结构体,比如下面的B直接嵌入到A中,B是一个匿名字段packagemainimport("fmt")typeAstruct{NamestringB}typeBstruct{AgeintNamestring}访问成员变量funcmain(){vara=A{姓名:"a",B:B{姓名:"b",年龄:20}}fmt.Printf("%#vn",a)//main.A{姓名:"",B:main.B{Age:0}}fmt.Println(a.Name)//afmt.Println(a.B.Name)//bfmt.Println(a.Age)//20}当成员名只有一个时,Go语法糖可以省略嵌入结构fmt.Println(a.B.Age)//20fmt.Println(a.Age)//20对同名成员应该有多个,不能省略,因为编译器不知道是哪个typeCstruct{AB}funcmain(){varc=C{A:A{Name:"a"},B:B{Name:"b",Age:20}}fmt.Println(c.Name)//ambiguousselectorc.Name}正确的做法是funcmain(){varc=C{A:A{Name:"a"},B:B{Name:"b",Age:20}}fmt.Println(c.A.Name)//afmt.Println(c.B.Name)//b}上面的组合是一个没有名字的嵌入结构,你也可以给嵌入结构命名,访问必须带上特定字段,packagemainimport("fmt")typeAstruct{BtypeB}typeBstruct{AgeintNamestring}funcmain(){vara=A{Btype:B{Name:"b",Age:20}}fmt不能.Println(a.Name)//.Nameundefined(typeAhasnofieldormethodName)}标签在字段后面用``包裹如下,主要是通过反射实现序列化和反序列化,具体在反射章节。typeUserstruct{Idint`json:"id"`Accountstring`json:"account"form:"account"`Nicknamestring`gorm:"nickname"json:"nickname"form:"nickname"`}方法和方法一般都是面向对象的编程面向对象编程(OOP)的一个特性,Go语言中的方法实际上是与一个值或变量相关联的特殊函数。这个值或变量叫做接收者func([typeName]receiver)name(param)[return]{}接收者是自定义类型packagemainimport("fmt")typeAstruct{}//structtypeBint//intfunc(aA)show(){fmt.Println("a.......")}func(bB)show(){fmt.Println("b...........")}funcmain(){varaAvarbBa.show()b.show()}接收方不能直接使用内置类型func(cint)show(){//cannotdefinenewmethodsonnon-localtypeintfmt.Println("b.........")}接收者值可以是值类型或指针类型packagemainimport("fmt")typeAstruct{}typeBstruct{}func(aA)show(){//值类型fmt.Println("a.........")}func(b*B)show(){//指针类型fmt.Println("b........")}funcmain(){varaAvarbBa.show()b.show()}对于B来说,下面两种调用方式是等价的,本质上都是一样的,b.show()写的时候没有(&b),但它是通过语法糖funcmain(){varbBb.show()(&b).show()}方法完成的,可以访问接收方自身的信息,如下packagemainimport("fmt")typeUserstruct{IdintAccountstringNicknamestring}func(uUser)show(){fmt.Println(u.Nickname)}funcmain(){vara=User{Nickname:"Test"}a.show()//Test}valuetypereceiver复制所有类型,修改不会影响原始数据;pointercopy是地址,修改会影响原数据包mainimport("fmt")typeUserstruct{IdintAccountstringNicknamestring}func(uUser)show(){fmt.Println(u)}func(uUser)setName1(){u.Nickname="="valuetype"}func(u*User)setName2(){u.Nickname="pointertype"}funcmain(){vara=User{Nickname:"Test"}a.setName1()a.show()a.setName2()a.show()}接收者类型本身不能是指针packagemainimport("fmt")typeAinttypeB*int//变量类型是指针func(aA)show(){fmt.Println("a......")}func(bB)show(){//invalidreceivertypeB(Bisapointertype)fmt.Println("b..........“)}