GO 指针 结构体 方法 接口

内容详细

1 指针

        // python js java 都没有指针的概念
        // c c++  go 有指针的概念,go的指针会比c指针简单很多

        // 变量---》有类型---》存其他变量的内存地址
        指针是一种存储变量内存地址的变量
        package main

        import "fmt"

        // 指针

        // & 放在变量前,表示取该变量的地址
        // * 放在类型前,表示该类型的指针
        // * 放在变量(指针变量)前,表示解引用
        func main()  {
           // 1 指针定义
           //var i =10 //定义int类型变量
           //
           //var p =&i // 取i的地址,赋值给变量 p
           //fmt.Println(p)

           // 2 指针变量的类型
           //var i uint=10 //定义int类型变量
           //var p *uint=&i // *int表示 int类型的指针
           //fmt.Println(p)

           // 3 解引用
           //var i uint=10 //定义int类型变量
           //var p *uint=&i // *int表示 int类型的指针
           //fmt.Println(*p) //解引用

           // 4 指针变量的指针
           //var i int=10
           //var p *int=&i
           //var x **int=&p
           //var y ***int=&x
           //fmt.Println(p)
           //fmt.Println(x)
           //fmt.Println(y)
           //fmt.Println(*y)
           //fmt.Println(**y)
           //fmt.Println(***y)

           // 5 指针类型的零值---》引用类型零值是nil
           //var p *string
           //fmt.Println(p)  // <nil> 必须要 初始化才能用--》PyObject *

           //6 向函数传递指针参数-->修改会影响原来的
           //var i int=10
           ////var p *int=&i
           //testP(&i)  // 函数参数传递都是copy传递   0xc00001a080
           //fmt.Println("--",i) //11

           //7 不要向函数传递数组的指针,而应该使用切片
           //var a [3]int=[3]int{7,8,9}
           var a =[4]int{7,8,9}
           //var p =&a
           //fmt.Println(p) //应该是个地址,但是是:&[7 8 9],为了好看
           ////test2(p)
           //test3(p)
           //fmt.Println(a) // a变了!

           test4(a[:])
           fmt.Println(a)

           // 8 指针不支持运算---》c支持运算
           //





        }
        func testP(a *int)  {
           fmt.Println(a) // 地址
           fmt.Println(*a)//10
           *a++
           fmt.Println(*a) //11
        }

        func test2(a *[3]int)  {
           fmt.Println(a)
           (*a)[0]=999
           fmt.Println(a)
        }
        func test3(a *[4]int)  {
           fmt.Println(a)
           (*a)[0]=999
           fmt.Println(a)
        }
        func test4(a []int)  {
           fmt.Println(a)
           a[0]=999
           fmt.Println(a)
        }

2 结构体

        // go语言中没有类的概念,class,但是能够使用类的所有特性,继承封装多态。。。。跟咱们之前学的面向对象的语言,语法上有差距

        //结构体:  c 和go中有结构体概念
        结构体是用户定义的【类型】(类),表示若干个字段的集合(属性,name,age...)
        package main

        import (
           "fmt"
           "go_day05_demo/entity"
        )

        // 结构体
        //type关键字  结构体名字  struct{
        // 字段1 字段类型
        // 字段2 字段类型
        //}



        //1 定义结构体  定义一个 人  这种类型,姓名,性别,年龄 字段
        type Person struct {  // 如果小写,在其他包使用不到Person结构体
           Name string  // 字段大写,表示导出,如果小写,表示隐藏,在其他包中取不到
           age uint8
           Sex string
        }


        func main() {
           // 2 使用结构体---》结构体是值类型-->有默认零值---》如果当函数参数传递,在函数中修改,不会影响原来的
           //var p Person  // 没有初始化,有零值,零值是每个字段的零值
           //fmt.Println(p.age) // 有默认零值,字段的零值
           //fmt.Println(p) //{ 0 }

           // 3 定义并初始化
           // 3.1 按位置初始化,按照顺序,有几个字段,必须传几个字段
           //var p Person=Person{"lqz",18,"男"}
           //fmt.Println(p)

           // 3.2 按关键字初始化,不用按顺序,字段不用都传
           //var p Person=Person{Sex:"男",age:19}
           //fmt.Println(p)


           // 4 字段的取值赋值  通过 .
           //var p Person=Person{Sex:"男",age:19}
           //fmt.Println(p)
           //fmt.Println(p.Name)
           //p.Name="xx"
           //fmt.Println(p.Name)

           // 5 结构体零值  不初始化有默认零值,零值是 各个字段的零值
           //var p Person
           //fmt.Println(p)

           // 6 匿名结构体---》结构体没有名字和type关键字--->一般定义在函数内部--->只使用一次
           // 好处是:多个变量可以包装到一个变量中
           //a:= struct {
           // age int
           // name string
           //}{19,"彭于晏"}
           //fmt.Println(a.name)
           //fmt.Println(a.age)
           //fmt.Println(a)

           // 7 结构体指针
           //var p =&Person{"lqz",18,"男"}
           //var p *Person=&Person{"lqz",18,"男"}
           //// 7.1 结构体指针,访问字段
           ////(*p).Name="pyy"
           //p.Name="pyy"  // 可以简写成   跟直接用结构体一样了,
           //fmt.Println(p)  // &{ pyy 19 男}

           // 8 结构体当函数参数传递
           //var p =Person{"lqz",18,"男"}
           //test5(p)
           //fmt.Println("----",p)  // p 不受影响

           // 8 结构体指针当函数参数传递
           //var p =&Person{"lqz",18,"男"}
           //test6(p)  //指针传入,地址copy传入,改了就影响原来
           //fmt.Println("----",p)  // p 受影响

           // 9 匿名字段 ---》字段没有名字--->跟结构体是否匿名没有必然联系
           // 有什么用---》很有用,做字段提升--->实现面向对象的继承
           //type Animal struct {
           //  string  // 字段没有名字,但是不能出现两个同种类型的字段 ,字段名就是类型名
           //  int
           //}
           ////var a =Animal{"小鸭子",1}
           //var a =Animal{string:"小鸭子",int:1}
           //fmt.Println(a)

           // 10 结构体嵌套--->结构体中套结构体
           //type Animal struct {
           // Name string
           // age int
           //}
           //type Dog struct {
           // //Name string
           // //age int
           // // 简写成--->结构体嵌套
           // //animal Animal
           // Animal    // 匿名字段,Animal字段匿名了,字段名就是类型名,而且不用写name和age了
           // // 狗比Animal多的字段
           // Speak bool
           //}
           ////var dog =Dog{Animal{"二哈",3},true}
           //var dog =Dog{Animal:Animal{"二哈",3},Speak:true}
           //// 面向对象继承,1 少写代码  2 子类对象.属性 如果子类没有,直接使用父类的
           //fmt.Println(dog.Name)  // dog的name本来是Animal的,使用了匿名字段,字段提升了
           //// 如果Animal不是匿名字段,不能提升
           //fmt.Println(dog.Animal.Name) // 指名道姓用   dog.Animal 相当于 super()
           //// Dog中有Name,Animal 用了匿名字段,Name冲突了,会优先使用Dog自己的


           // 11 导出结构体 和 字段
           //var p =entity.Person{"lyf",37}  // age 小写,外包包不能用
           var p =entity.Person{Name:"lyf"}  // age 小写,外包包不能用
           fmt.Println(p)

           // 12 结构体是值类型。
           //如果它的每一个字段都是可比较的,则该结构体也是可比较的。
           //如果两个结构体变量的对应字段相等,则这两个变量也是相等的
           //var p1=Person{"lqz",19,"男"}
           //var p2=Person{Name: "lqz",age: 19,Sex: "男"}
           //p2.age=20
           //if p1==p2{
           // fmt.Println("p1 和p2 相等")
           //}


           // 如果结构体包含不可比较的字段(有引用类型),则结构体变量也不可比较
           //type Cat struct {
           // Name string
           // age int
           // wife []string  // 可以有很多异性猫
           //}
           //var c1 =Cat{"小野豹",3,[]string{"女朋友1号","女朋友2号"}}
           //var c2 =Cat{"小野豹",3,[]string{"女朋友1号","女朋友2号"}}
           //if c1==c2 {
           //
           //}




        }

        func test5(p Person)  {
           fmt.Println(p)
           p.Name="刘亦菲"
           fmt.Println(p)

        }
        func test6(p *Person)  {
           fmt.Println(p)
           p.Name="刘亦菲"
           //(*p).Name="刘亦菲"
           fmt.Println(p)

        }

3 方法

         // 什么是函数,什么是方法?  绑定给对象或者类的函数,由对象或者类来调用,可以自动传值

        // 方法
        方法其实就是一个函数,在 func 这个关键字和方法名中间加入了一个特殊的接收器类型。
        接收器可以是结构体类型或者是非结构体类型。接收器是可以在方法的内部访问的
        package main

        import "fmt"

        // 方法---》结构
        /*
        func (接收器)   方法名(参数1 参数类型,参数2 参数类型)(返回值){
           内容
        }
        */


        // 1 定义方法
        type Dog struct {
           Name string
           Age int
        }
        // 给结构体绑定一个方法---》结构体有了属性,有了方法---》就是类的概念
        // 这个接收器就是python中的 self,只不过写的位置不太一样
        func (self Dog)Speak()  {
           fmt.Println("我是狗狗:",self.Name,"我说话了")
        }

        // 值类型接收器
        func (dog Dog)changeName(name string)  {
           dog.Name=name
           fmt.Println("方法中改了名字,",dog)
        }
        // 指针类型接收器
        func (dog *Dog)changeAge(age int)  {
           dog.Age=age
           fmt.Println("方法中改了年龄,",dog)
        }
        func main() {
           // 2 使用方法  通过 . 调用
           //var dog =Dog{"二哈",2}
           //dog.Speak()  // 自动把dog传到方法中

           // 3 有了函数,为什么还要方法?
           // 函数有几个值就要传几个,不能自动传值
           // 方法绑定给结构体,拿到结构体对象直接调用即可,不需要在传参数
           //var dog =Dog{"二哈",2}
           //dog.Speak()  // 自动把dog传到方法中
           //Speak(dog)

           // 4 值接收器和指针接收器

           // 值类型接收器-->修改不会影响原来的
           //var dog =Dog{"二哈",2}
           //dog.changeName("牧羊犬")
           //fmt.Println(dog) // 这个名字改了还是没改?

           // 指针类型接收器
           //var dog =Dog{"二哈",2}
           //dog.changeAge(99)
           //fmt.Println(dog) // 这个年龄改了

           //5 无论是值类型接收器还是指针类型接收器 都可以值来调或者指针来调
           // 值来调用
           //var dog =Dog{"二哈",2}
           //dog.changeName("牧羊犬")
           //dog.changeAge(99)
           //fmt.Println(dog)

           // 指针来调用
           var dog =&Dog{"二哈",2}
           dog.changeName("牧羊犬")
           dog.changeAge(99)
           fmt.Println(dog)


           // 1 无论是值还是指针,都可以调用值接收器的方法或者指针接收器的方法
           // 2 无论值还是指针,只有指针接收器的方法,修改才会影响原来的



        }

        func Speak(self Dog)  {
           fmt.Println("我是狗狗:",self.Name,"我说话了")
        }

4 接口

        // python中鸭子类型就是接口的概念

        // 一般语言来讲,接口规定了子类中有哪些方法(接口没有实现,只定义),子类自己去实现,接口是同一种类型
        假设定义了一个鸭子接口,有run和speak方法

        普通肉鸭,实现接口---》python 继承鸭子这个类,重写run和speak方法,它可以还有自己的方法
        唐老鸭,实现接口---》python 继承鸭子这个类,重写run和speak方法,它可以还有自己的方法

        普通肉鸭和唐老鸭都属于鸭子这种类型 ---》非鸭子类型的语言


        // 鸭子类型的解释
        对于python来讲,不需要定义出鸭子接口,不需要继承鸭子类,只要普通肉鸭中有run和speak方法
        只要唐老鸭中有run和speak方法,我就认为我们是同一种类,鸭子这个类

        // 不要显示的声明接口,继承接口,只要 多个不同类中有同样的方法,我们就可以把这些类归纳为一种类型---->python 特性,语言层面不强求,所有的权限都交给程序员

        // 鸭子类型:带来了问题---》正因为不强求,子类如果不小心把方法名写错了,它就不是同一种类型,程序不会报错,运行到那才会报错

        // 领导告诉你,你去写一个类,里面有speak和run方法,写好了,告诉我,我来用
            -你不小心把speak  ---》spaek
            -等你领导真正调用的时候,报错了

            -实际编码中,鸭子类型好用(少写代码),但是好多情况下不用,而要强制你必须实现run和speak,为了代码的健壮性,违背了鸭子类型,drf源码中,认证类,继承认证的基类,重写xx方法,正常情况不需要继承,直接写就行了

            -1 通过抛异常的方式,如果没有重写方法,抛异常了
            -2 abcstract装饰器修饰,子类必须实现,不实现,直接报错
            以上都违背了鸭子类型的概念,但是确保了代码的健壮性


        // python程序员,初级和高级的程序员写的代码差距特别特别大,初级的写的,根本没法维护
        // go,java程序员,好多东西都是限制死的,必须这样写,确保了,即便新手,写的代码也没有大的问题


        // 接口
        在面向对象的领域里,接口一般这样定义:【接口定义一个对象的行为】。接口只指定了对象应该做什么,至于如何实现这个行为(即实现细节),则由对象本身去确定
posted @ 2022-06-07 16:46  风花雪月*  阅读(40)  评论(0编辑  收藏  举报