构造体(struct)是自定义形式造成新的数据类型,构造体是类型中带有成员的复合类型。Go 语言构造体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为构造体的成员。来形容真实世界的实体和实体对应的各种属性。
构造体属性也叫字段
或成员
,每个字段都有名称和类型,每个名称是惟一的。能够是任何类型,如一般类型、复合类型、函数、map、interface、struct等,所以咱们能够了解为go语言中的“类”。
定义
构造体定义形式如下:
type name struct{ fieldName1 type1 fieldName2 type2 ...}
如下,定义User 构造体:
type User struct { Name string age int}
实例化
下面定义只是类型,就想是一个int
一样,须要定义一个类型变量才能够应用,相似Java的类。
间接定义变量应用
package mainimport ( "fmt")type User struct { Name string age int}func main() { var user1 User //定义User 类型变量user var user2 *User //类型指针,未分配内存,不能间接应用 fmt.Println(user1, user2) //{ 0} <nil>}
定义默认成员变量
var user1 = User{Name: "abc"}fmt.Println(user1)func NewUser() *User { return &User{Name:"abc",age:20}}
应用内建函数new()
分配内存返回类型变量指针
var user = new(User)fmt.Println(user) //&{ 0}
拜访成员
应用.
来拜访
var user Useruser.Name = "abc"user.age = 20fmt.Println(user) //{abc 20}
首字母大小写问题,成员大写示意包外可见(即面向对象的私有属性),小写包外不可见
零值:构造体的零值是nil
初始值:构造体的初始值是非nil
时,各成员对应类型的初始值
空构造体:空构造体就是没有字段的构造体,空构造体不占内存
package mainimport ( "fmt" "unsafe")func main() { user1 := struct{}{} user2 := struct{}{} fmt.Printf("%p,%dn", &user1, unsafe.Sizeof(user1)) //0x585218,0 fmt.Printf("%p,%dn", &user2, unsafe.Sizeof(user2)) //0x585218,0}
从下面能够看出空构造体内存地址和大小都是一样的。依据这个个性,应用空构造体能够作为信号量,起到信号作用但不占内存。如空构造体类型的chan
匿名构造体
匿名构造体没有类型名称,毋庸通过 type 关键字定义就能够间接应用。
user := struct { Name string }{Name: "abc"}fmt.Println(user) //{abc}
比拟
如果构造体的全副成员都是能够比拟
的,且成员的程序
、类型
、数量
齐全一样才能够比拟,两个构造体将能够应用==或!=运算符进行比拟。
package mainimport ( "fmt")func main() { user1 := struct { Name string }{Name: "abc"} user2 := struct { Name string }{Name: "abc"} fmt.Println(user1 == user2) //true}
成员名称不一样
package mainimport ( "fmt")func main() { user1 := struct { Name string }{Name: "abc"} user2 := struct { name string }{name: "abc"} fmt.Println(user1 == user2) //invalid operation: user1 == user2 (mismatched types struct { Name string } and struct { name string })}
成员数量不一样
package mainimport ( "fmt")func main() { user1 := struct { Name string }{Name: "abc"} user2 := struct { Name string age int }{Name: "abc"} fmt.Println(user1 == user2) //invalid operation: user1 == user2 (mismatched types struct { Name string } and struct { Name string; age int })}
成员类型不能比拟
package mainimport ( "fmt")func main() { user1 := struct { Name string m map[int]int }{Name: "abc"} user2 := struct { Name string m map[int]int }{Name: "abc"} fmt.Println(user1 == user2) //invalid operation: user1 == user2 (struct containing map[int]int cannot be compared)}
程序不一样
package mainimport ( "fmt")func main() { user1 := struct { Name string age int }{Name: "abc"} user2 := struct { age int Name string }{Name: "abc"} fmt.Println(user1 == user2) //invalid operation: user1 == user2 (mismatched types struct { Name string; age int } and struct { age int; Name string })}
其实整个构造体就是一个类型(如int),成员程序、类型这些不一样,整体的构造体就不一样,故对于强类型语言来说就是不能比拟的,对应类型齐全一样还须要留神成员是否是能够比拟,如slice、map等
Go语言没有面向对象这个概念,但能够把构造体看做是一个类,能够实现面向对象的个性,如通过组合和嵌入实现继承
匿名字段
匿名字段是构造体没有显示的名字,是构造体嵌入一个或多个构造体,如上面
B间接嵌入A ,B是匿名字段
package mainimport ( "fmt")type A struct { Name string B}type B struct { Age int Name string}
拜访成员变量
func main() { var a = A{Name:"a",B:B{Name:"b",Age:20}} fmt.Printf("%#vn", a) //main.A{Name:"", B:main.B{Age:0}} fmt.Println(a.Name) //a fmt.Println(a.B.Name) //b fmt.Println(a.Age) //20 }
只有一个成员名称的状况下,Go语法糖能够省略嵌入构造体
fmt.Println(a.B.Age) //20fmt.Println(a.Age) //20
对应有多个雷同名称的成员,不能省略,因为编译器不晓得是哪个
type C struct { A B}func main() { var c = C{A:A{Name:"a"},B:B{Name:"b",Age:20}} fmt.Println(c.Name) //ambiguous selector c.Name}
正确做法是
func main() { var c = C{A:A{Name:"a"},B:B{Name:"b",Age:20}} fmt.Println(c.A.Name) //a fmt.Println(c.B.Name) //b}
组合
下面是没有名字的嵌入构造体,还能够给嵌入构造体命名,拜访必须要带上具体的字段,不能省略。
package mainimport ( "fmt")type A struct { Btype B}type B struct { Age int Name string}func main() { var a = A{Btype:B{Name:"b",Age:20}} fmt.Println(a.Name) //.Name undefined (type A has no field or method Name)}
标签
如上面在字段前面用`` 包起来的是标签,次要是通过反射来序列化和反序列化,具体由反射章节来讲。
type User struct { Id int `json:"id"` Account string `json:"account" form:"account"` Nickname string `gorm:"nickname" json:"nickname" form:"nickname"`}
办法
办法个别都是面向对象编程(OOP)的一个个性,Go语言的办法其实与一个值或变量关联的非凡的函数。这个值或变量叫做接收者
func ([typeName] 接收者) name (param) [return]{}
接收者是自定义的类型
package mainimport ( "fmt")type A struct {} //构造体type B int //intfunc (a A) show() { fmt.Println("a............")}func (b B) show() { fmt.Println("b............")}func main() { var a A var b B a.show() b.show()}
接收者不能间接用内置类型
func (c int) show() { //cannot define new methods on non-local type int fmt.Println("b............")}
接收者值
能够是值类型或指针类型
package mainimport ( "fmt")type A struct {}type B struct {}func (a A) show() { //值类型 fmt.Println("a............")}func (b *B) show() { //指针类型 fmt.Println("b............")}func main() { var a A var b B a.show() b.show()}
对与B
来说,上面两种调用形式是等价的,实质上他们都是一样的,b.show()
的写法是省略了(&b)
,只不过由语法糖来补全
func main() { var b B b.show() (&b).show()}
办法能够拜访接收者本身的信息,如下
package mainimport ( "fmt")type User struct { Id int Account string Nickname string }func (u User)show() { fmt.Println(u.Nickname)}func main() { var a = User{Nickname:"测试"} a.show() //测试}
值类型接收者拷贝类型的全副,批改不会
影响原数据;指针拷贝的是地址,批改会
影响原数据
package mainimport ( "fmt")type User struct { Id int Account string Nickname string}func (u User)show() { fmt.Println(u)}func (u User)setName1() { u.Nickname="值类型"}func (u *User)setName2() { u.Nickname="指针类型"}func main() { var a = User{Nickname:"测试"} a.setName1() a.show() a.setName2() a.show()}
接受者类型
自身不能为指针
package mainimport ( "fmt")type A int type B *int //变量类型为指针func (a A) show() { fmt.Println("a............")}func (b B) show() { //invalid receiver type B (B is a pointer type) fmt.Println("b............")}
办法的参数和返回值这些和函数一样,具体看函数章节