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