共计 2808 个字符,预计需要花费 8 分钟才能阅读完成。
Go 不是纯粹的面向对象语言,和传统的面向对象编程有区别,所以说 Go 语言支持面向对象编程特性是比较准确的。
Go 语言基于结构体 struct 来实现 OOP 特性,通过 type 和 struct 关键字声明结构体,格式如下:
type 类型名 struct { // 类型名在同一个包内不能重复
字段 1 字段 1 类型 // 字段名必须唯一
字段 2 字段 2 类型
}
结构体从本质上讲是一种自定义的数据类型,只不过这种数据类型比较复杂,可以认为结构体是一种聚合类型。
结构体实例化
声明结构体变量再赋值:
type Student struct {
sid int // 字段首字母小写,类似 private,其它包不能使用
course []string // 选修课程}
func main() {
// 声明一个结构体对象,值类型,默认开辟空间,字段赋予零值
var s Student
fmt.Println("s:", s) // s: {0 []}
// 访问结构体成员使用点号 . 操作符
fmt.Println(s.sid) // 0
// 更改成员变量值
s.sid = 1001
fmt.Println(s.sid) // 1001
// 方式 2:键值对赋值
s2 := Student{sid: 1002, course: []string{"chinese", "math"}}
fmt.Println(s2)
// 方式 3:多值赋值
s3 := Student{1003, []string{"chinese", "math"}}
fmt.Println(s3)
}
实例化之 & 结构体:
func initSid(s *Student) {s.sid = 1001 // 其实是 (*s).sid = 1001
}
func main() {s := Student{course: []string{"chinese", "math"}}
initSid(&s)
fmt.Println(s) // {1001 [chinese math]}
s2 := &Student{course: []string{"chinese", "math"}}
initSid(s2)
fmt.Println(s2) // &{1001 [chinese math]}
}
实例化之 new(结构体):
func main() {s := new(Student) // 等同于 &Student{}
fmt.Println(s) // &{0 []}
}
模拟构造函数
Go 语言没有构造函数,但可以使用结构体初始化过程来模拟实现构造函数(首字母大写)。
// 自定义工厂函数,返回局部变量地址
func NewStudent(sid int, course []string) *Student {
return &Student{
sid: sid,
course: course}
}
func main() {s := NewStudent(1001, []string{"math", "english"})
fmt.Println(s) // &{1001 [math english]}
}
方法接收器
Go 语言中的方法(Method)是一种作用于特定类型变量的函数,这种特定类型变量叫做接收者(Receiver)。方法与函数的区别是,函数不属于任何类型,方法属于特定类型。
func (s Student) learn() {fmt.Printf("%d 正在学习...", s.sid)
}
func main() {s := NewStudent(1001, []string{"math", "english"})
s.learn()}
接收者是指针类型:
type Player struct {
Name string
HealthPoint int
}
func NewPlayer(name string, hp int) *Player {
return &Player{
name,
hp,
}
}
func (p *Player) attack() {fmt.Printf("%s 发起攻击!\n", p.Name)
}
func (p *Player) attacked() {fmt.Printf("%s 被攻击!\n", p.Name)
p.HealthPoint -= 10
fmt.Println("HealthPoint:", p.HealthPoint)
}
func main() {player := NewPlayer(" 张三 ", 100)
player.attack()
player.attacked()}
匿名字段
结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段称为匿名字段。
type Person struct {
string // 其实是 string string
int // 其实是 int int
}
func main() {
p := Person{
" 张三 ",
18,
}
fmt.Println(p.string, p.int) // 张三 18
}
结构体也可以作为匿名字段使用:
type Addr struct {
province string
city string
}
type Person struct {
string
int
Addr // 其实是 Addr Addr
}
func main() {
p := Person{
" 张三 ",
18,
Addr{" 广东 ", " 深圳 "},
}
fmt.Println(p.Addr.province, p.city) // 广东 深圳
}
当结构体中有和匿名字段相同字段时,采用外层优先访问原则。
包和封装
每个目录一个包,main 包包含可执行入口。
为结构定义的方法必须放在同一个包内,可以是不同文件。
结构体继承
// Animal 动物
type Animal struct {name string}
func (a *Animal) eat() {fmt.Printf("%s is eating!\n", a.name)
}
// Dog 类型
type Dog struct {
Kind string
*Animal // 通过嵌套匿名结构体实现继承,直接写类型,没有字段名
}
func (d *Dog) bark() {fmt.Printf("%s is barking ~\n", d.name)
}
func main() {
d := &Dog{
Kind: " 金毛 ",
Animal: &Animal{ // 注意嵌套的是结构体指针
name: " 旺财 ",
},
}
d.eat()
d.bark()}
结构体标签
type Stu struct {
Name string `json:"name"` // 结构体标签
Age int `json:"-"` // 表示不参与序列化
}
func main() {stuStruct := Stu{Name: " 张三 ", Age: 18}
// 序列化
jsonStuStruct, _ := json.Marshal(stuStruct)
fmt.Println(string(jsonStuStruct)) // {"name":" 张三 "}
// 反序列化
var StuStruct Stu
err := json.Unmarshal(jsonStuStruct, &StuStruct)
if err != nil {return}
fmt.Println(StuStruct) // {张三 0}
}
正文完