面向对象编程

我们要明白的是,GO不是完全的是面向对象,他是包含有面向对象的特性,至于思想,在Java已经学习过了;略

构造结构体

构造体是值类型默认是拷贝

1
2
3
4
type person struct {
name string
age int
}

使用构造体

1
2
3
4
5
6
7
8
type person struct {
name string
age int
}
var p person;
p.name="saxon"
p.age=18
fmt.Println(p)
1
2
3
4
5
6
7
var p person
p.name = "saxon"
p.age = 18
p1 := p
p1.name = "saxon2"
fmt.Println(p) //{saxon 18}
fmt.Println(p1) //{saxon2 18}

因为默认是值拷贝,所以我们的更改是不会互相影响的;包括属性是引用类型的数据也不会相互影响

申明结构体

  • 直接使用

    1
    2
    3
    4
    5
    6
    7
    type person struct {
    name string
    age int
    }
    var p person;
    p.name="saxon"
    p.age=18
  • 使用 var 变量名 结构体名=结构体名{}

    1
    2
    3
    4
    5
    6
    7
    8
    type person struct {
    name string
    age int
    class map[string]string
    }
    var p2 person=person{
    "saxon",18,map[string]string{"saxon":"saxon"},
    }
  • 使用 *var 变量名 结构体名=new(结构体名)

    *返回的是结构体指针,所以赋值可以使用p.name或者(p).name,都可以

    1
    2
    3
    4
    var p3 *person = new(person)
    *p3.name="saxon"
    *p3.age=18
    p3.class=map[string]string{"saxon":"saxon"}
  • 使用 *var 变量名 结构体名=&变量名{}

    1
    var p4 *person = &person{"saxon", 18, map[string]string{"saxon": "saxon"}}

    *最后两种返回的是指针类型,结构体指针访问属性的标准方式是:(结构体指针).field,但是go的底层也允许程序员使用结构体.field的方式访问变量

    由于==.==的优先级大于*,所以 *p1.age=18,是无法通过编译的

结构体内存分配机制

  • 结构体的内存地址在内存中是一段连续的空间

  • 指针的地址是连续的但是指针指向的地方不是连续的

  • 结构体是用户自己定义的,和其他类型相互转换的时候,需要类型字段完全一样,名字都一样

  • 可以给结构体取别名

    1
    2
    3
    type per person;
    var p2 per;
    p2.name="saxon

    但是需要注意的是,这并不代表他们就是一种类型

    image-20201208202056781

​ 正确的写法是进行一次强制类型转换go

1
2
var p3 per;
p3=per(p)
  • 可以在结构体的每一个属性上加上一个标签,这样就可以运用反射获得属性,运用在序列化和反序列化的时候

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    type person11 struct {
    Name string `json:"name"` //后面这个标签的意思就是我们解析过后的字段名字
    }

    func main() {
    var p person11;
    p.Name="saxon"
    jsonStr,_:=json.Marshal(p)
    fmt.Println(string(jsonStr))
    }

    结果:

    1
    {"name":"saxon"}

    注意如果是属性的首字母小写,就是私有,是无法被序列化的,就不会被解析

方法

定义

1
2
3
4
5
6
7
8
9
10
11
12
type person11 struct {
Name string `json:"ss"`
}
func(a person11) introduction(){
fmt.Println("我的名字是",a.Name)
}

func main() {
var p person11;
p.Name="saxon"
p.introduction()
}

格式:func(结构体名字 结构体类型) 函数名字(函数参数)函数返回值

方法使用注意

  • 方法只可以和绑定的结构体使用
  • 不可以直接调用,也不可以 由其他的变量结构体来调用

方法的调用和传参机制

对象调用方法的时候,自己也会备份到方法里面

如果想要通过函数改变结构体的参数数据,那么调用这个方法的主体就要是一个指针,那样的话,我们修改的值就是指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main

import (
"fmt"
)

type person11 struct {
Name string `json:"ss"`
}
func(a *person11) introduction(){
a.Name="sss"
fmt.Println("我的名字是",a.Name)
}

func main() {
var p person11;
p.Name="saxon"
fmt.Println(p)
p.introduction()
fmt.Println(p)
}

1
2
3
{saxon}
我的名字是 sss
{sss}

如果想要实现一个结构体的toString()方法,那么只要在这个下面有一个String()函数就可以了

1
2
3
func(a person11) String()string{
return "saxon"
}

调用的主体是指针的话,那么方法内部就可以修改属性的值,不然的话就是值类型,修改不起作用

工厂模式

由于没有构造函数,所以我们使用工厂模式,实例化结构体

实际上和java的单例模式一样,解决我们的结构体的首字母是小写(私有)的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package Demo2

type Person12 struct {
Name string
}

func GetPerson(name string) (person Person12) {
person = Person12{
name,
}
return
}
func main() {

}
//测试
func main() {
per:=model.GetPerson("saxon")
fmt.Println(per)
}

这里的属性无论大小写,都可以通过工厂模式获得对象

*关于返回对象是指针的于一般对象的区别:返回值类型不同之处在于取值的方式,指针类型需要使用 * 号读取数据. 其次返回值指针判断空值更加容易简洁,t != nil.*

getter setter方法

封装了属性,所以我们要给外界一个接口,知道如何访问结构体里面的数据

例如:

1
2
3
func (person *person) SetName(name string) {
person.name = name
}
1
2
3
4
5
func main() {
per:=model.GetPerson("saxon")
per.SetName("saxon2")
fmt.Println(per)//saxon2
}

继承对象

由于没有extends这个关键字,所以我们要使用属于GO语言的继承,采用匿名结构体的方式实现继承机制

1
2
3
4
5
6
7
8
9
10
11
12
type Person struct {
Name string
}
type Student struct {
Person
Age int
}
var stu util.Student = util.Student{
util.Person{"saxon"}, 18,
}
fmt.Println(stu)

1
{{saxon} 18}

需要把所有的属性都公有化,就是首字母大写,不然无法直接使用,但是不要继承过多的结构体,相当于Java的单继承机制,我们尽量少继承一点

接口

在go里面没有implement这个关键字,所以一个结构体实现接口的方法就是实现接口的所有方法

image-20201209164038879

当一个结构体实现了那个接口的所有方法,我们就可以说,他继承了或者是实现了一个接口;接口和Java里面的概念一样,不允许有方法体(Java8 以前)

接口本身不可以创建实体类,但是却可以实现多态;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import "fmt"

type human interface {
run()
skip()
}
type Student struct {
name string
age int
}
type Worker struct {
name string
age int
}

func (student Student) run() () {
student.name = "saxon"
fmt.Println(student.name + "在跑步")
}
func (student Student) skip() () {
student.name = "saxon"
fmt.Println(student.name + "在跳")
}
func (worker Worker) run() () {
worker.name = "saxon"
fmt.Println(worker.name + "在跑步(worker)")
}
func (worker Worker) skip() () {
worker.name = "saxon"
fmt.Println(worker.name + "在跳(worker)")
}
func main() {
var stu human = Student{
"saxon", 18,
}
var worker human = Worker{
"mosong", 18,
}
stu.run()
worker.run()
}

可以和Java的使用方法一样创建多态,都是同一个类,但是实现不同的类,实现不同的功能

一个结构体可以实现多种的接口

自定义类型也可以使用和实现接口,我觉得这个可以实现Integer这些Java里面有的代表实体的类,写一些方法

1
2
3
4
5
6
7
8
type integer int

func (i integer) run(){
fmt.Println(i)
}

var i integer;
i.run()

如果一个类型的结构被继承,或者继承其他接口,那么这个接口的实现类就是实现所有的方法;如果一个类继承两个接口,那么这两个接口就必须不能有一样的方法

接口实现类数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import "fmt"

type Usb interface {
start()
}
type Phone struct {
name string
}
type Watch struct {
name string
}

func (phone Phone) start() {
fmt.Println(phone.name)
}
func (watch Watch) start() {
fmt.Println(watch.name)
}
func main() {
phone := Phone{
"iphone",
}
watch := Watch{
"iWatch",
}
var usbs []Usb = []Usb{
phone, watch,
}
usbs[0].start()
usbs[1].start()
}

实际上我们实现的是一个接口的切片,我们可以把都是实现了这个接口的实现类,在一个数组或者切片里面

断言

我们使用的变量太多,我们不知道他的实际类型是什么,所以我们要使用断言来对我们的数据类型进行一个判断

1
2
3
4
5
6
7
8
9
10
11
type assert interface{}
type Demo17 struct{

}
func main() {
b:=1
var a assert;
a=b
boolean:=a.(int32)
fmt.Println(boolean)
}

使用步骤:

  • 创建一个空的接口,这个接口将要接收我们要判断的类型,如果是这个类型,那么接受的变量就会被赋值,如果不是,那么就会退出,报错;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    package main

    import "fmt"

    type assert interface{}
    type Demo17 struct{
    name string
    }
    func main() {
    var x assert;
    y:=Demo17{
    "saxon",
    }
    x=y
    a:=x.(int) //不是这个值的类型,所以报错
    //a:=x.(Demo17) 是这个类型的,所以a变成这个值
    fmt.Println(a)
    }
  • 如果我们要使用断言并且让他在失败时不中断,我们就使用if,结构来获得属性返回我们想要的数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    package main

    import "fmt"

    type test17 struct {
    name string
    }
    type assert17 interface{}

    func main() {
    a := test17{
    "saxon",
    }
    var x assert17
    x=a
    if y,ok:=x.(int);ok{
    fmt.Println(y)
    }else{
    fmt.Println("error")
    }
    go
    }

    核心

    1
    2
    y, ok := x.(test17)
    data,bool:=对象.(数据类型)

    其中前面的data代表着数据,如果对象是这个数据类型的时候,这个就是对象的数据,如果不是的话,他会是数据类型里面的默认值

接下来进行一个实践

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main

import "fmt"

func main() {
a:=true
b:="ss"
check(a,b)
}
func check(items ...interface{}) {
for index, value := range items {
switch a := value.(type) {
case bool:
fmt.Printf("第%d个是%T类型", index, a)
case string:
fmt.Printf("第%d个是%T类型", index, a)
}
}
}

判断一个对象的实体是否是某一个类型,除了可以在数据类型那里写实际的类型,还可以使用type代替,可以在switch内部使用参数.(type),里面的type就是type;但是仅仅局限于我们的switch循环里面

在switch里面可以使用这种方法直接获得对象的数据类型

第一个简单的实践项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package main

import (
"fmt"
"strconv"
)

var manger *Manger

func init() {
manger = &Manger{}
manger.list = make([]Customer, 10)
}
func main() {
manger.start()
}

type Manger struct {
list []Customer
}
type Customer struct {
name string
sex string
age int64
telephone string
email string
}

func (manger *Manger) start() {
fmt.Println("---------------客户管理页面-------------")
fmt.Println("---------------(1)添加用户-------------")
fmt.Println("---------------(2)查询用户-------------")
fmt.Println("---------------(3)删除用户-------------")
fmt.Println("---------------(4)修改用户-------------")
fmt.Println("---------------(5)退出系统-------------")
fmt.Println("-----------请选择你要使用的功能-------------")
key := 0
fmt.Scanln(&key)
switch key {
case 1:
manger.AddCustomer()
manger.start()
case 2:
manger.SearchAllCustomer()
manger.start()
case 3:
manger.DeleteCustomer()
manger.start()
case 4:
manger.AlertCustomer()
manger.start()
case 5:
fmt.Println("-----------谢谢使用--------------")
}

}

func (manger *Manger) AddCustomer() (customer Customer) {
fmt.Println("请输入信息添加用户,使用空格分开")
var name, sex, telephone, email string
var age int64
fmt.Scanln(&name, &sex, &age, &telephone, &email)
customer = Customer{
name, sex, age, telephone, email,
}
fmt.Println("-----------添加成功-------------")
manger.list = append(manger.list, customer)
return
}
func (manger *Manger) SearchAllCustomer() {
fmt.Println("-----------客户信息-----------")
for _, value := range manger.list {
if value.name != "" {
fmt.Println("姓名:" + value.name)
fmt.Println("性别:" + value.sex)
fmt.Println("年龄:", value.age)
fmt.Println("电话号码:", value.telephone)
fmt.Println("邮箱:", value.email)
fmt.Println()
}
}
}
func (manger *Manger) DeleteCustomer() {
var name string
fmt.Println("输入你要删除的用户名")
fmt.Scanln(&name)
for index, value := range manger.list {
if value.name == name {
manger.list = append(manger.list[:index], manger.list[index+1:]...)
}
}
}
func (manger *Manger) AlertCustomer() {
fmt.Println("输入你要修改的用户名")
name := ""
fmt.Scanln(&name)
for index, value := range manger.list {
if value.name == name {
fmt.Println("请输入你要修改的信息项目和修改后的信息")
ctype := ""
info := ""
fmt.Scanln(&ctype)
switch ctype {
case "name":
value.name = info
case "age":
age, _ := strconv.ParseInt(info, 10, 32)
value.age = age
case "sex":
value.sex = info
case "telephone":
value.telephone = info
case "email":
value.email = info
case "Q","q":

}
} else {
if index==len(manger.list)-1{
fmt.Println("没有这个用户请再次确认信息")
}
}
}
}