文章目录
- Go语言面向对象
- 一、结构体定义
- 结构体定义
- 结构体语法格式
- 二、实例化结构体
- 结构体的使用
- 结构体实例化语法
- new关键字
- 三、初始化结构体变量
- 结构体初始化
- 顺序初始化
- 指定初始化
- 四、匿名结构体
- 匿名结构体定义
- 匿名结构体语法格式
- 五、结构体内嵌
- 结构体内嵌语法、特性
- 键值对形式
- 多值列表形式
- 通过嵌套匿名结构体实现继承
银行ATM的例子
相较于面向过程,面向对象有以下优势:
- 容易命名
- 在面向对象中,方法名相同但属于不同的类,这不会引起冲突,因为每个类定义了自己的方法和属性。相比之下,函数式编程中的函数名必须唯一,否则会导致冲突。
- 代码管理方便
- 易于模块化开发。类中包含方法和属性,属性用来记录数据,方法表示行为,类实例化后为对象,行为和数据由对象来统一管理。写出来的代码方法与属性各归各类,代码逻辑清晰,利于扩展;函数也表示行为,但是与函数配合的数据却散落摆放,缺乏“类”这样的结构来统一管理。
- 重用性高
- 代码冗余量小、重用性高。面向对象的代码在使用时,通过调用各个对象中的方法和属性,不同的排列组合就能适应各种不同的业务场景。
Go语言面向对象
- 在其他编程语言中大多使用class关键字来定义封装对象,表示该类的具体特征,然而Go并不是一个纯面向对象的编程语言。在Go语言中的面向对象,采用更灵活的结构体替代了类。
- Go语言并没有提供类class,但是它提供了结构体struct,方法method,可以在结构体上添加。提供了捆绑数据和方法的行为,这些数据和方法与类相似。
- Go语言设计的非常简洁优雅,Go没有沿袭传统面向对象编程中的诸多概念,例如继承、虚方法、构造方法和析构方法等。
- 虽然Go语言没有继承和多态。但是Go语言可以通过匿名字段实现继承,通过接口实现多态。在Go语言中学习面向对象,主要学习结构体struct、方法method、接口interface。
一、结构体定义
结构体定义
- Go语言中没有“类”的概念,通过struct来实现面向对象
- 通常用于表达一个事物的全部或部分属性
- 是一种自定义数据类型,可以封装多个基本数据类型
- 本质是将零个或者多个任意类型的命名变量组合在一起的聚合数据类型
- 每个变量叫做结构体的成员,变量名必须唯一,可用“_”补位
- 支持使用自身指针类型成员
结构体语法格式
type 类型名 struct {字段名 字段类型字段名 字段类型......
}
语法
- 类型名:标识自定义结构体的名称,在同一个包内不能重复。
- 字段名:表示结构体字段名,结构体中的字段名必须唯一。
- 字段类型:表示结构体字段的具体类型。
案例
type Student struct {stuID intstuName stringage int}//成员变量通常一行写一个,变量的名称在类型的前面
type Student struct {stuID,age intstuName string
}//相同类型的连续成员变量可以写在一行上。
示例1
type Student1 struct {stuId,age intstuName,address stringclass,teacher string}
示例2
type Student2 struct {stuId intaddress,stuName stringage intclass,teacher string}
- 成员变量的顺序对于结构体同一性很重要
- 相同类型的不连续成员变量不可以写在一行上
二、实例化结构体
结构体的使用
- 结构体属于数据类型,因此和声明Go语言内置数据类型一样使用var关键字声明结构体类型
- 只有当结构体实例化时,才会真正地分配内存
- 即必须实例化后才能使用结构体的字段
结构体实例化语法
var 结构体实例 结构体类型
示例:
package main
import "fmt"
func main() {type person struct {name stringcity stringage int8}var p1 person //实例化结构体
}
在定义一些临时数据结构等场景下还可以使用匿名结构体
package main
import "fmt"
func main() {var p1 struct{name string;city string; age int}
}
new关键字
结构体实例 := new(结构体类型)
- 使用new关键字实例化结构体后,得到的是结构体的地址;结构体类型为指针类型,但结构体本身仍然是值类型。
- 通过&符号对结构体取地址
- 取地址的同时会将结构体实例化
package main
import "fmt"
func main() {type person struct {name stringcity stringage int8}var p1 =new(person)//p1 := &person{}//相当于这样定义
}
三、初始化结构体变量
结构体初始化
- 结构体类型的值可以通过结构体字面量来设置,即通过结构体的成员变量来设置
- 有两种格式的结构体字面量:顺序初始化、指定成员初始化
顺序初始化
- 要求顺序为每个成员变量指定一个值,这种格式必须记住每个成员变量的顺序
指定初始化
- 通过指定部分或者全部成员变量的名称和值来初始化结构体变量,如果成员被忽略的话将默认用零值
package main
import ("fmt"
)
func main() {type person struct {name stringcity stringage int8}//顺序初始化,每个成员必须初始化var p1 personp1.name = "Go语言"p1.city = "深圳"p1.age = 18fmt.Println(p1)//指定成员初始化,没有初始化的成员自动赋值为零p2 := person{name: "Golang"}fmt.Println(p2)
}//运行结果为:
//{Go语言 深圳 18}
//{Golang 0}
四、匿名结构体
匿名结构体定义
- 匿名结构体,顾名思义,即没有名字的结构体,与匿名函数类似
- 匿名结构体无须type关键字就可以直接使用,匿名结构体在创建的同时也要创建对象
- 匿名结构体的初始化和使用更加简单,无须通过type关键字定义,且不用写出类型名称
匿名结构体语法格式
结构体实例 := struct{//匿名结构体定义成员变量1 类型1成员变量2 类型2...
}{//成员变量初始化(可选)成员变量1:值1, 成员变量2:值2, …
}
示例:
attribute2 := struct {Title, Publisher stringPrice float64BookId uintDiscount string
}{"唐宋传奇选","人民文学出版社",15.1,566357,"6折",
}
五、结构体内嵌
结构体内嵌语法、特性
语法:
type 结构体1 struct{字段名1 字段类型1字段名2 字段类型2
}
type 结构体2 struct{结构体1字段名3 字段类型3
}
特性:
- 是一种组合特性
- 使用结构体内嵌可构建一种面向对象编程思想中的继承关系
- 结构体实例化后,可直接访问内嵌结构体的所有成员变量和方法
示例:
//定义关于圆心坐标、圆、圆柱的结构体:
type Point struct {x,y int
}
type Circle struct {x,y,r int
}
type Cylinder struct {x,y,r,h int
}//嵌入相同部分:
type Point struct {x,y int
}
type Circle struct {P Pointr int
}
type Cylinder struct {C Circleh int
}
键值对形式
type 结构体1 struct{字段名1 字段类型1字段名2 字段类型2
}
type 结构体2 struct{结构体1字段名3 字段类型3
}func main(){结构体实例 := 结构体2 {结构体1{字段名1:字段名1值字段名2:字段名2值},字段名3:字段名3值}
}
- 键值对之间以逗号分割
- 键值之间以冒号分割
- 结构体成员的字段名应该具有唯一性
示例:
package main
import "fmt"
type rectangle struct {length,wide int
}
type cuboid struct {r rectanglehigh int
}func main(){c :=cuboid{r:rectangle{length: 5,wide: 6,},high:7,}fmt.Println(c.r.length)fmt.Println(c.r.wide)fmt.Println(c.high)
}//运行结果为:
//5
//6
//7
当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找,但嵌套结构体内部可能存在相同的字段名。
这个时候为了避免歧义需要指定具体的内嵌结构体的字段。
package main
import "fmt"
type Address struct {City stringCountry string }
type ContactInfo struct {Phone stringCity string }
type Person struct {Name stringAddress // 匿名嵌套ContactInfo // 匿名嵌套
}
func main(){p := Person{
Name: "Alice",
Address: Address{
City: "New York",
Country: "USA", },
ContactInfo: ContactInfo{
Phone: "123-456-7890",
City: "Los Angeles",
}, }
fmt.Println("Person Name:", p.Name)
fmt.Println("Address City:", p.Address.City)}
fmt.Println("ContactInfo City:", p.ContactInfo.City)
当访问结构体成员时会先在结构体中查找该字段,找不到再去匿名结构体中查找,但嵌套结构体内部可能存在相同的字段名。
这个时候为了避免歧义需要指定具体的内嵌结构体的字段。
多值列表形式
type 结构体1 struct{字段名1 字段类型1字段名2 字段类型2
}
type 结构体2 struct{结构体1字段名3 字段类型3
}
func main(){结构体实例 := 结构体2 {结构体1{字段名1值字段名2值},字段名3值}
}
!必须初始化结构体所有字段 每一个初始值的填充顺序必须与字段在结构体中的声明顺序一致 键值对与多值列表的初始化形式不能混用
示例:
package main
import "fmt"
type rectangle struct {length,wide int
}
type cuboid struct {r rectanglehigh int
}
func main(){c :=cuboid{rectangle{5,6,},7,}fmt.Println(c.r.length)fmt.Println(c.r.wide)fmt.Println(c.high)
}//运行结果为:
//5
//6
//7
通过嵌套匿名结构体实现继承
package main
import "fmt"
//Animal 动物
type Animal struct {name string
}
func (a *Animal) move() {fmt.Printf("%s会动!\n", a.name)
}
//Dog 狗
type Dog struct {Feet int8*Animal //通过嵌套匿名结构体实现继承
}
func (d *Dog) wang() {fmt.Printf("%s会汪汪汪~\n", d.name)
}
func main() {d1 := &Dog{Feet: 4,Animal: &Animal{ //注意嵌套的是结构体指针name: "乐乐",},}d1.wang()d1.move()
}//运行结果为:
//乐乐会汪汪汪~
//乐乐会动!