• golang面向对象和interface接口


    一、 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 可以通过反射机制获取,常见的使用场景就是序列化和反序列化

    序列化的使用场景:

    例子:

    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 这样的关键字

    基本语法

    例子:

    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
    

      

  • 相关阅读:
    加载声音的过程
    onkeyup,onkeydown和onkeypress
    加载着色器的异常

    3
    1
    1
    java总结
    环路
    own address as source address
  • 原文地址:https://www.cnblogs.com/zhangb8042/p/10557098.html
Copyright © 2020-2023  润新知