golang面向对象和interface接口

时间:2023-01-13 19:37:17

一、 golang面向对象介绍

1、golang也支持面向对象编程,但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。
2、golang没有类(class),golang语言的结合体(struct)和其它编程语言的类有同等的地位。
3、golang面向对象编程,去掉了传统语言面向对象的继承、方法重载、结构函数和析构函数、隐藏的this指针等。
4、goalng仍然有面向对象编程的继承,封装和多态的特性,。
5、golang面向接口编程非常重要

二、结构体创建注意事项

1、字段申明语法同变量
2、字段的类型可以为:基本类型、数组或应用类型
3、在创建一个结构体变量后,如果没有给字段赋值,都对应改类型的默认值(如bool为false,int为0,string为"")
4、不同结构体变量的字段是独立互不影响的,一个结构体变量字段的更改不会影响另一个结构体的值类型。

三、创建struct语法

type 结构体名称 struct{
field type
}

创建例子:

type stru struct{
a1 string
a2 int
a3 [5]float64
a4 []int //切片
a5 *int //指针
a6 map[string]string //map
}

  

使用例子:

package main
import "fmt" func main(){
type ss struct{
Name string
Age int
}
//1、直接申明
var ss1 ss
ss1.Name = "张三"
ss1.Age = 20
fmt.Println(ss1)
//2、使用{}
var ss2 ss = ss{"李四",22}
fmt.Println(ss2)
//3、&
var ss3 *ss = new(ss)
(*ss3).Name = "王五"
(*ss3).Age = 30
fmt.Println(ss3,*ss3)
//4、{}
var ss4 *ss = &ss{}
ss4.Name = "韩梅梅"
ss4.Age = 66
fmt.Println(ss4,*ss4) } ###结果###
{张三 20}
{李四 22}
&{王五 30} {王五 30}
&{韩梅梅 66} {韩梅梅 66}

  

四、结构体使用注意事项

1、结构体的所有字段在内存中是连续的

例子:

package main
import "fmt" type Point struct {
x int
y int
} type Test1 struct {
s1,s2 Point
} func main(){
a1 := Test1{Point{1,2},Point{3,4}}
//打印地址
fmt.Printf("%p,%p,%p,%p",&a1.s1.x,&a1.s1.y,&a1.s2.x,&a1.s2.y) } ##结果##
0xc4200141e0,0xc4200141e8,0xc4200141f0,0xc4200141f8

  

2、结构体是用户单独定义的类型,和其它类型进行转换时需要有完全相同的字段(名字、个数和类型

例子:

package main
import "fmt" type A struct {
x int
} type B struct {
x int
} func main(){
var a A
var b B
b.x = 2
a = A(b) //可以转换,但是结构体的字段和类型必须完全一样
fmt.Println(a,b)
} ###结果###
{2} {2}

  

3、结构体进行 type 重新定义(相当于取别名),Golang 认为是新的数据类型,但是相互间可以强转

例子:

package main
import "fmt" func main(){
type A struct {
x int
}
type B A
var s1 A
var s2 B
s2 = B(s1) //强转
fmt.Println(s1,s2) } ##结果##
{0} {0}

  

4、struct 的每个字段上,可以写上一个 tag, 该 tag 可以通过反射机制获取,常见的使用场景就是序列化和反序列化

序列化的使用场景:

golang面向对象和interface接口

例子:

package main
import (
"fmt"
"encoding/json"
)
type A struct {
Name string `json:"name"` //`json:"name"`就是struct的tag
Age int `json:"age"`
} func main(){
s1 := A{"张三",20}
//将s1变量序列化为json格式字符串
jsonStr,err := json.Marshal(s1)
if err != nil {
fmt.Println("json错误处理",err)
}
fmt.Println(string(jsonStr))
} ###结果###
{"name":"张三","age":20}

  

五、结构体的方法

方法的声明和调用
type A struct {
Num int
}
func (a A) test() {
fmt.Println(a.Num)
}
语法的说明
1) func (a A) test() {} 表示 A 结构体有一方法,方法名为 test
2) (a A) 体现 test 方法是和 A 类型绑定的

例子:

package main
import "fmt" type A struct {
Name string
}
//test方法只能公告A类型的变量类调用,不能直接调用,也不能使用其它类型变量来调用
func (a A) test(){ //将test方法绑定到A类型中,a的名字是随意指定的
fmt.Println("test()",a.Name)
} func (b A) add(n1,n2 int) int {
return n1 + n2
} func main(){
var x A
x.Name = "zhang"
x.test()
fmt.Println(x.add(1,2))
}
####结果####
test() zhang
3

  

六、golang面向对象的三大特性

1、封装

封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其它包只有通过被授权的操作(方法),才能对字段进行操作封装的好处

1、隐藏实现细节
2、提可以对数据进行验证,保证安全合理

2、封装的实现步骤

1) 将结构体、字段(属性)的首字母小写(不能导出了,其它包不能使用,类似 private)
2) 给结构体所在包提供一个工厂模式的函数,首字母大写。类似一个构造函数
3) 提供一个首字母大写的 Set 方法(类似其它语言的 public),用于对属性判断并赋值
func (var 结构体类型名) SetXxx(参数列表) (返回值列表) {
//加入数据验证的业务逻辑
var.字段 = 参数
}
4) 提供一个首字母大写的 Get 方法(类似其它语言的 public),用于获取属性的值func (var 结构体类型名) GetXxx() {return var.age

例子:

[root@localhostgo_test]#cat src/fz/fz.go
package fz
import "fmt" type T1 struct {
Name string
age int //开头是小写,其它包不能直接访问
} func Ss(name string) *T1 {
return &T1{
Name:name,
}
} func (a *T1) Getage() int {
fmt.Println(a.age)
return a.age
} [root@localhostgo_test]#cat class6.go
package main
import (
"fmt"
"fz"
) func main(){
x := fz.Ss("root")
fmt.Println(x)
x.Getage()
}
[root@localhostgo_test]#go run class6.go
&{root 0}
0

  

2、继承

继承可以解决代码复用,让我们的编程更加靠近人类思维。
当多个结构体存在相同的属性(字段)和方法时,可以从这些结构体中抽象出结构体,在该结构体中定义这些相同的属性和方法
在 Golang 中,如果一个 struct 嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体的字段和方法,从而实现了继承特性

继承给编程带来的便利

1) 代码的复用性提高了
2) 代码的扩展性和维护性提

嵌套匿名结构体的基本语法
type Goods struct {
Name string
Price int
}
type Book struct {
Goods //这里就是嵌套匿名结构体 Goods
Writer string
}

例子:

package main
import "fmt" type A struct {
name string
age int
} func (p *A) Run() {
fmt.Println("Run...")
} type B struct {
A
b1 int
} type C struct {
A
} func (p *C) Crun() {
fmt.Println(p.name,p.age)
} func main(){
var q B
q.name = "qq"
q.age = 3
q.b1 = 20
q.Run()
fmt.Println(q)
fmt.Println("==========")
var w C
w.name = "ww"
w.age = 222
w.Run()
fmt.Println(&w) } ####结果######
Run...
{{qq 3} 20}
==========
Run...
&{{ww 222}}

  

3、多态(多态基于接口interface)

介绍:

变量(实例)具有多种形态。面向对象的第三大特征,在 Go 语言,多态特征是通过接口实现的。可以按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态

接口体现多态的两种形式

1、在下面interface的 Usb 接口案例,Usb usb ,即可以接收手机变量,又可以接收相机变量,就体现了Usb接口多态

2、多态数组

例子:给 Usb 数组中,存放 Phone 结构体 和 Camera 结构体变量

package main
import ( "fmt"
)
//声明/定义一个接口
type Usb interface {
//声明了两个没有实现的方法
Start()
Stop()
}
type Phone struct {
name string
}
//让 Phone 实现 Usb 接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作。。。")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作。。。")
}
type Camera struct {
name string
}
//让 Camera 实现 Usb 接口的方法
func (c Camera) Start() {
fmt.Println("相机开始工作。。。")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作。。。")
}
func main() {
//定义一个 Usb 接口数组,可以存放 Phone 和 Camera 的结构体变量
//这里就体现出多态数组
var usbArr [3]Usb
usbArr[0] = Phone{"vivo"}
usbArr[1] = Phone{"小米"}
usbArr[2] = Camera{"尼康"}
fmt.Println(usbArr)
} #####结果#######
[{vivo} {小米} {尼康}]

  

七、接口interface

简介

1、interface 类型可以定义一组方法,但是这些不需要实现。并且 interface 不能包含任何变量。到某个自定义类型要使用的时候,在根据具体情况把这些方法写出来实现

2、 接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态和高内聚低偶合的思想。
3、 Golang 中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此,Golang 中没有 implement 这样的关键字

基本语法

golang面向对象和interface接口

例子:

package main
import ( "fmt"
)
//声明/定义一个接口
type Usb interface {
//声明了两个没有实现的方法
Start()
Stop()
}
type Phone struct {
}
//让 Phone 实现 Usb 接口的方法
func (p Phone) Start() {
fmt.Println("手机开始工作。。。")
}
func (p Phone) Stop() {
fmt.Println("手机停止工作。。。")
}
type Camera struct {
}
//让 Camera 实现 Usb 接口的方法
func (c Camera) Start() {
fmt.Println("相机开始工作。。。")
}
func (c Camera) Stop() {
fmt.Println("相机停止工作。。。")
}
//计算机
type Computer struct {
}
//编写一个方法 Working 方法,接收一个 Usb 接口类型变量
//只要是实现了 Usb 接口 (所谓实现 Usb 接口,就是指实现了 Usb 接口声明所有方法)
func (c Computer) Working(usb Usb) { //usb 变量会根据传入的实参,来判断到底是 Phone,还是 Camera
//通过 usb 接口变量来调用 Start 和 Stop 方法
usb.Start()
usb.Stop()
}
func main() {
//测试
//先创建结构体变量
computer := Computer{}
phone := Phone{}
camera := Camera{}
//关键点
computer.Working(phone)
computer.Working(camera)
} #####结果####
手机开始工作。。。
手机停止工作。。。
相机开始工作。。。
相机停止工作。。。

  

使用接口注意事项

1、接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)
2、接口中所有的方法都没有方法体,即都是没有实现的方法。
3、在 Golang 中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。
4、一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型
5、只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。
6、一个自定义类型可以实现多个接口
7、Golang 接口中不能有任何变量
8、一个接口(比如 A 接口)可以继承多个别的接口(比如 B,C 接口),这时如果要实现 A 接口,也必须将 B,C 接口的方法也全部实现。
9、interface 类型默认是一个指针(引用类型),如果没有对 interface 初始化就使用,那么会输出 nil
10、空接口 interface{} 没有任何方法,所以所有类型都实现了空接口, 即我们可以把任何一个变量赋给空接口

接口最佳实践例子:

package main
import ( "fmt"
"sort"
"math/rand"
)
//1.声明 Hero 结构体
type Hero struct{
Name string
Age int
}
//2.声明一个 Hero 结构体切片类型
type HeroSlice []Hero //3.实现 Interface 接口
func (hs HeroSlice) Len() int {
return len(hs)
}
//Less 方法就是决定你使用什么标准进行排序
//1. 按 Hero 的年龄从小到大排序!!
func (hs HeroSlice) Less(i, j int) bool {
return hs[i].Age < hs[j].Age
//修改成对 Name 排序
//return hs[i].Name < hs[j].Name
}
func (hs HeroSlice) Swap(i, j int) {
//交换
hs[i], hs[j] = hs[j], hs[i]
}
//1.声明 Student 结构体
type Student struct{
Name string
Age int
Score float64
}
//将 Student 的切片,安 Score 从大到小排序!!
func main() {
//先定义一个数组/切片
var intSlice = []int{0, -1, 10, 7, 90}
//要求对 intSlice 切片进行排序
//1. 冒泡排序...
sort.Ints(intSlice)
fmt.Println(intSlice)
//测试看看我们是否可以对结构体切片进行排序
var heroes HeroSlice
for i := 0; i < 10 ; i++ {
hero := Hero{
Name : fmt.Sprintf("英雄|%d", rand.Intn(100)), Age : rand.Intn(100), }
//将 hero append 到 heroes 切片
heroes = append(heroes, hero)
}
//看看排序前的顺序
for _ , v := range heroes {
fmt.Println(v)
}
sort.Sort(heroes)
fmt.Println("-----------排序后------------")
//看看排序后的顺序
for _ , v := range heroes {
fmt.Println(v)
}
i := 10
j := 20
i, j = j, i
fmt.Println("i=", i, "j=", j) // i=20 j = 10
} ######运行结果######
[-1 0 7 10 90]
{英雄|81 87}
{英雄|47 59}
{英雄|81 18}
{英雄|25 40}
{英雄|56 0}
{英雄|94 11}
{英雄|62 89}
{英雄|28 74}
{英雄|11 45}
{英雄|37 6}
-----------排序后------------
{英雄|56 0}
{英雄|37 6}
{英雄|94 11}
{英雄|81 18}
{英雄|25 40}
{英雄|11 45}
{英雄|47 59}
{英雄|28 74}
{英雄|81 87}
{英雄|62 89}
i= 20 j= 10