构造体 (struct) 是自定义形式造成新的数据类型,构造体是类型中带有成员的复合类型。Go 语言构造体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为构造体的成员。来形容真实世界的实体和实体对应的各种属性。
构造体属性也叫 字段
或成员
, 每个字段都有名称和类型,每个名称是惟一的。能够是任何类型,如一般类型、复合类型、函数、map、interface、struct 等,所以咱们能够了解为 go 语言中的“类”。
定义
构造体定义形式如下:
type name struct{
fieldName1 type1
fieldName2 type2
...
}
如下,定义 User 构造体:
type User struct {
Name string
age int
}
实例化
下面定义只是类型,就想是一个 int
一样,须要定义一个类型变量才能够应用,相似 Java 的类。
间接定义变量应用
package main
import ("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 User
user.Name = "abc"
user.age = 20
fmt.Println(user) //{abc 20}
首字母大小写 问题,成员大写示意包外可见(即面向对象的私有属性), 小写包外不可见
零值:构造体的零值是nil
初始值 : 构造体的初始值是非nil
时,各成员对应类型的初始值
空构造体: 空构造体就是没有字段的构造体,空构造体不占内存
package main
import (
"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 main
import ("fmt")
func main() {
user1 := struct {Name string}{Name: "abc"}
user2 := struct {Name string}{Name: "abc"}
fmt.Println(user1 == user2) //true
}
成员名称不一样
package main
import ("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 main
import ("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 main
import ("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 main
import ("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 main
import ("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) //20
fmt.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 main
import ("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 main
import ("fmt")
type A struct {} // 构造体
type B int //int
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()}
接收者不能间接用内置类型
func (c int) show() { //cannot define new methods on non-local type int
fmt.Println("b............")
}
接收者 值
能够是值类型或指针类型
package main
import ("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 main
import ("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 main
import ("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 main
import ("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............")
}
办法的参数和返回值这些和函数一样,具体看函数章节