面向对象编程
- Go支持面向对象编程特性,但和传统面向对象编程是有区别的,Go并不是纯粹的面向对象语言。
- Go没有类,其结构体和其它编程语言中的类具有同等的地位,可理解Go是基于结构体来实现面向对象特性的。
- Go面向对象编程非常简介,去掉了传统面向对象语言的继承、方法重载、构造函数、析构函数、隐藏的
this
指针等。 - Go仍然具有面向对象编程的继承、封装、多态的特性,只是实现方式和传统面向对象语言不同。
- Go面向对象很优雅,面向对象本身就是语言类型系统的一部分,通过接口关联,耦合性低,也非常灵活。
- Go中面向接口编程是非常重要的特性
结构体与结构体变量
- 结构体:自定义的数据类型,表示一类事物。
type struct_name struct {
field1 datatype
field2 datatype
}
//定义结构体
type User struct {
Id int
Name string
Birth string
Gender int
}
- 结构体变量:又称为实例或对象,是具体的、实际的,代表一个具体变量。
//结构体变量
var u User
u.Id = 1
u.Name = "alice"
u.Birth = "2020-12-21"
u.Gender = 1
结构体字段
- 结构体字段又称为属性,字段是结构体组成部分,一般是基础数据类型、数组,也可以是引用类型。
- 字段声明语法同变量一样采用
字段名 字段类型
- 字段类型可分为:基本类型、数组、引用类型
- 创建结构体变量后若没有给字段赋值,都会对应一个零值作为默认值。
- 不同结构体变量的字段是独立互不影响,一个结构体变量的更改不会影响到另外一个,因为结构体是值类型默认是值拷贝。
字段类型 | 类型 | 零值 |
---|---|---|
布尔类型 | 基础 | false |
整型 | 基础 | 0 |
字符串 | 基础 | "" |
数组类型 | 基础 | 默认值与元素类型相关 |
指针ptr /切片slice /映射map
|
引用 | nil,未分配空间。 |
结构体字段类型若存在指针、slice
、map
,它们的零值都是nil
,即还没有分配空间,如果需要使用必须先make()
。
类型 | 创建 |
---|---|
指针 | new() |
切片 | make() |
映射 | make() |
//定义结构体
type User struct {
Id int
array [5]float64
slice []int
ptr *int
dict map[string]string
}
//结构体变量
var u User
fmt.Printf("%+v\n", u) // {Id:0 array:[0 0 0 0 0] slice:[] ptr:<nil> dict:map[]}
fmt.Printf("%t\n", u.slice == nil) //true
fmt.Printf("%t\n", u.ptr == nil) //true
fmt.Printf("%t\n", u.dict == nil) //true
创建结构体变量
//创建结构体变量
var u1 User //直接声明
fmt.Printf("%+v\n", u1) //{Id:0}
var u2 User = User{}
fmt.Printf("%+v\n", u2) //{Id:0}
采用结构体指针方式创建后,结构体指针访问字段的标准方式应该是 (*结构体指针).字段名
,但Go做了简化也支持结构体指针.字段名
,因为这种方式更加符合程序员的使用习惯。Go编译器底层会对简化方式做自动转换。
var u3 *User = new(User)
fmt.Printf("%+v\n", u3) //&{Id:0}
var u4 *User = &User{}
fmt.Printf("%+v\n", u4) //&{Id:0}
简化写法
var u *User = new(User)
fmt.Printf("%+v\n", u) //&{Id:0}
(*u).Id = 100
fmt.Printf("%+v\n", *u)
u.Id = 200
fmt.Println(u)
由于u
是一个指针,因此标准的给字段赋值的方式应该采用的是(*u).Id = 100
,但也可以直接采用u.Id = 200
。由于Go设计者为了程序员使用方便,底层会对u.Id = 200
添加取值运算。
创建结构体实例时可直接指定字段的值
type User struct {
Id int
Name string
}
func main() {
u := User{1, "alice"}
fmt.Printf("%+v\n", u) // {Id:1 Name:alice}
}
内存分配机制
例如:结构体是值类型,默认赋值会拷贝副本,若需采用引用类型方式,则需使用结构体变量指针赋值。
u1 := User{1}
u2 := &u1 // var u2 *User = &u1
u2.Id = 100
fmt.Printf("%+v\n", u1) // {Id:100}
fmt.Printf("%+v\n", u2) // &{Id:100}
fmt.Printf("u1=%p, u2=%p\n", &u1, u2) // u1=0xc0000aa058, u2=0xc0000aa058
.
点运算符优先级要高于*
星号运算符,下列为错误写法:
u1 := User{1}
u2 := &u1 // var u2 *User = &u1
u2.Id = 100
fmt.Printf("%+v\n", *u2.Id) // invalid indirect of u2.Id (type int) (exit status 2)
注意事项
结构体字段在内存中是连续分布的
type Point struct {
x, y int
}
type Rect struct {
leftUp, rightDown Point
}
func main() {
r := Rect{
Point{0, 0},
Point{4, 4},
}
fmt.Printf("%p\n", &r.leftUp.x) // 0xc0000a8080
fmt.Printf("%p\n", &r.leftUp.y) // 0xc0000a8088
fmt.Printf("%p\n", &r.rightDown.x) // 0xc0000a8090
fmt.Printf("%p\n", &r.rightDown.y) // 0xc0000a8098
}
指针本身的地址是连续但指向的地址不一定是连续的
type Point struct {
x, y int
}
type Rect struct {
leftUp, rightDown *Point
}
func main() {
r := Rect{
&Point{0, 0},
&Point{4, 4},
}
fmt.Printf("%p\n", &r.leftUp) // 0xc000088220
fmt.Printf("%p\n", &r.rightDown) // 0xc000088228
fmt.Printf("%p\n", r.leftUp) // 0xc0000aa070
fmt.Printf("%p\n", r.rightDown) // 0xc0000aa080
}
结构体是用户单独定义的类型,和其它类型进行转换时需具有完全相同的字段(名字、个数、类型)。
type Point struct {
x, y int
}
type Axis struct {
x, y int
}
func main() {
p1 := Point{0, 0}
p2 := Axis(p1)
fmt.Printf("%+v\n", p1) // {x:0 y:0}
fmt.Printf("%+v\n", p2) // {x:0 y:0}
}
结构体进行type
重新定义时相当于取别名,Go会认为是新的数据类型,但相互之间可以强转。
type Point struct {
x, y int
}
type Axis Point
func main() {
var p1 Point
var p2 Axis
//p1 = p2 // cannot use p2 (type Axis) as type Point in assignment (exit status 2)
p1 = Point(p2)
fmt.Printf("%+v %+v\n", p1, p2) // {x:0 y:0} {x:0 y:0}
}
结构体字段上可添加一个标签tag
,标签可通过反射机制获取,比如序列化和反序列化中。
type Response struct {
Code int `json:"code"`
Msg string `json:"msg`
Data interface{} `json:"data"`
}
func main() {
response := Response{200, "success", nil}
buf, err := json.Marshal(response)
if err != nil {
log.Fatalln(err)
}
fmt.Printf("%s\n", buf) // {"code":200,"Msg":"success","data":null}
}
工厂模式
Go的结构体没有构造函数,可通过工厂模式来实例化结构体。
// 结构体
type user struct {
Id int
Name string
}
// 工厂模式
func User(id int, name string) *user {
return &user{
Id: id,
Name: name,
}
}
func main() {
u := User(1, "alice")
fmt.Printf("%+v\n", u) // &{Id:1 Name:alice}
}
网友评论