20200326 指针结构体方法与接口

昨日回顾

// swtich 数组切片 map

// 1 switch (作为if else的另一种尝试)
          /*
          swtich 变量 {
            case 1,5:
              代码
            case 2,6:
              代码
            defult:
              代码
          }

          swtich  {
            case 条件:
              代码
            case 条件:
              代码
              Fallthrough
            defult:
              代码
          }
          */
//2 数组
 -内存中连续的存储空间,数组大小是固定的
	-var a [8]int=[8]int{1,2}
	-var a [8]int=[8]int{1:99}
	-a[2]
	-数组是值类型
	-数组长度 len
	-数组的遍历(两种)
      -for i,v:=range a{
              ---i:索引
              ---v:值
      }
	-多维数组
			-var a [2][3]int=[2][3]int{{1,2,3},{4,5,6}}
			-a[0][1]
//3 切片:本身不存储数据,对底层数组的引用
	-基于底层数组创建
			var a [8]int=[8]int{1,2}
			b:=a[:]
			b:=make([]int,3,5)
			b:=[]int{1,2}
	-长度和容量
	-使用 b[100]  //程序执行,如果越界,就报错
	-引用类型
	-追加 append
	-循环  range
	-多维切片
	-切片的数据结构  a[4]
			{
        指向数组的指针
        长度
        容量、
      }

//4 map:key value存储
	-定义 var a map[key的类型]value的类型
	-var a map[key的类型][]map[int]string
	-var a map[key的类型]对象
	-a[key]
	-可能为空值,不会报错
	-判断key是否存在
    if _,ok:=a[999];!ok{
      fmt.Println("不存在")
    }
	-map的长度 len
	-循环map
      for k,v:=range a{
          k:key
          v:value
      }
	-map是引用类型 (空值为nil,当参数传递会修改原来的值)
	-相等性(只能跟nil比较)




//补充:自动化运维
	-资产收集20台服务器
	-项目日志:echars 
	-批量执行命令,批量安装软件,代码自动发布,自动下线

go语言进阶

一. 指针 &*

指针是一种存储 变量内存地址 的变量

  • 取一个变量的地址: & 取地址符号
  • 如果在类型前面加*,表示指向这个类型的指针
  • 在指针变量前加*,表示解引用(反解),通过地址拿到值

1. 指针的定义

func main(){
    var b=156
    // 把b内存地址赋值给a这个指针
    // 取一个变量的地址: & 取地址符号
    
    // 如果在类型前面加*,表示指向这个类型的指针
    var a *int = &b
    // var c *string =&b  int类型不能赋值给字符串类型的指针
    // a :=&b
    fmt.Println(a)
}

2.指针的零值

var b int 
fmt.Println(b)   // nil

3. 指针的解引用

拿到地址指向的值

var b = 156
a:=&b
fmt.Println(a)
// 指针a指向的值: 解引用(反解)
fmt.Println(*a)

4. 向该函数传递指针参数

func(){
    var b = 126
    fmt.Println(b)	//156
    test(&b)
    fmt.Println(*b)	//166
}

func test(b *int){
    //解引用,然后操作 b是一个指针
    *b++	//指针传过来地址,修改了原来的数据
    fmt.Println(*b)	//166
}

5. 不要向函数传递数组的指针,而应该使用切片

  • 切片较数组内存占用小
  • 传递类型不会因数组大小而更改
func main() {
    var a[3]int = [3]int{3,4,5}
    //写一个函数,传入数组,改掉原来的数组
    test(&a)    // 传入变量的地址
    
    
    // 向函数传递切片 (推荐使用切片)
    test1(a[:])
}


//*[3]int 类型的指针
func test(x *[3]int){
    //x传入的是指针
    //x[1]=100      // 支持指针自动操作,不会报错,内部自动处理
    (*x)[1]=100     // 先解引用,再改值
    fmt.Println(x)  //x是一个指向数组的指针,地址 : 打印出 &[3 100 5]
}


func test1(x []int){
    x[0] = 100
    fmt.Println(x)
}

6. go 不支持指针运算

    //var b =156
    //a := &b
    ////不允许指针运算
    //a++

7. 指向指针的指针

    var b int=156
    var a *int=&b

    var c **int
    c = &a
    fmt.Println(c)

    var d ***int
    d = &c
    fmt.Println(d)

8. 数组指针与指针数组

  • 数组指针: 指向数组的指针
  • 指针数组: 数组里面放指针
    //数组指针
    var a[3]int =[3]int{1,2,3}
    var b *[3]int = &a
    fmt.Println(b)

    //指针数组
    var x,y,z=12,13,14
    var b [3]*int = [3]*int{&x,&y,&z}
    fmt.Println(b)

二. 结构体 type struct

go语言中的结构体,类似于面向对象语言中的类

  • go中的面向对象: 继承,封装,多态
  • 一系列属性的集合

1.结构体定义

定义一个结构体

type 结构体名字 struct {
    属性1 类型
    属性2 类型
    
    
// 大小写表示公有与私有
type Person struct {
    name string
    age int
    sex int
}

2.结构体的使用

func main(){
    p :=Person{}    //相当于实例化得到有一个对象
    var p Person =Person{}    //结构体的类型就是 Person
    fmt.Println(p)
    fmt.Println(p.name)
} 

type Person struct {
    name string
    age int
    sex int
}

3. 结构体的零值

 	var p Person// 值类型,零值是属性的零值
    p.name="jack"
    fmt.Println(p)

4. 定义并初始化(传入属性)

var p Person=Person{}

   按位置传
var p Person=Person{"jack",15,1}
    按关键字传(位置可以乱,有几个值就要传几个值)
var p Person=Person{name:"jack",age:15,sex:2}

fmt.Println(p)
fmt.Println(p.name)

5. 创建匿名结构体

没有名字,也没有type关键字,定义在函数体内部
只使用一次,把一堆属性放到一个变量中
    a := struct {
       name string
       age int
       sex int
    }{age:18}
    fmt.Println(a.age)

6. 结构体的指针

    var p Person=Person{sex:1,age:12}
    var p1 *Person=&Person{sex:1,age:12}    //指针
    var p1 *Person  // 结构体空值是nil
    fmt.Println(p1)

7. 匿名字段

  • 字段没有名字
  • 做变量提升,提升字段:面向对象中的继承
	// 按位置
    var p Person2=Person2{"lqz",18}
    //按关键字
    var p Person2=Person2{int:18,string:"jack"}
    fmt.Println(p)



//匿名字段
type Person2 struct {
    string
    int
    sex int     // 匿名与有名可以套用
}

8.嵌套结构体(结构体中套结构体)

    // 按位置
    var p Person3=Person3{"jack",12,Hobby{1,"蓝蓝"}}
    //按关键字
    var p Person3=Person3{name:"abc",hobby:Hobby{}}
    fmt.Println(p)
    //把hobby的id设置2,name设置为篮球
    p.hobby.name="篮球"
    p.hobby.id = 2
    fmt.Println(p)



//结构体嵌套
type Hobby struct {
    id int
    name string
}
type Person3 struct {
    name string
    age int
    hobby Hobby
}

9.字段提升(只把不重复的字段提升)

    // 按位置
    var p Person4=Person4{"jack"}
    //按关键字(面向对象的继承)
    var p Person4=Person4{name:"jack",Hobby2:Hobby2{}}

//Hobby中的字段被提升了(可以直接.)
    p.hobby_id=2
    p.hobby_name="chenglong"
    p.Hobby2.hobby_id=3

    // go中通过匿名字段和结构体嵌套实现面向对象的继承


--------------------------------
// 字段提升
type Hobby2 struct {
    hobby_id int
    hobby_name string
}
type Person4 struct {
    name string
    sex int
    age int
    Hobby2
}

10. 导出结构体(大小写)和字段(大小写)


11.结构体相等性

  • 结构体是值类型。
  • 如果它的每一个字段都是可比较的,则该结构体也是可比较的。
  • 如果两个结构体变量的对应字段相等,则这两个变量也是相等的
  • 如果结构体包含不可比较的字段,则结构体变量也不可比较。

三. 方法 fnuc (t type)

python: 什么是方法(绑定给对象,类,自动传值),什么是函数

go: 方法是绑定给结构体的: 结构体只有属性 + 方法 = 类

1. 方法的定义

func 关键字和方法名之间 加入了一个特殊的接收器类型
func (t type) 方法名(){}

2. 使用方法

    p :=Person4{"jack",28,1}
    //自动传值
    p.printName()
    // 有了函数,为什么还要方法
    效果一样,但是调用方式,以及函数必须传值等


//定义一个结构体
type Person4 struct {
    name string
    age int
    sex int
}
//给结构体绑定方法
func (p Person4)printName(){
    fmt.Println(p.name)
}

//普通函数
func printName(p Person4)  {
	fmt.Println(p.name)
}

3. 指针接收器与值接收器

值接收器,不会修改原来的

指针接收器才会修改原来的

    p:=Person4{"jack",18,1}
    p.changeName("engo")    //值接收器
    //(&p).changeName("engo")    //值接收器,也可以
    p.changeAge(1111)       // 指针接收器
    //(&p).changeAge(1111)       // 指针接收器,没有区别
    fmt.Println(p)
    // 不管是值类型接收器,还是指针类型接收器,都可以用值和指针来调用



//3. 指针接收器与值接收器
func (p Person4)changeName(name string) {
    p.name=name
    fmt.Println(p)
}
//指针类型接收器的修改年龄
func (p *Person4)changeAge(age int) {
    //(*p).age=age    //解引用(正统的修改)
    p.age=age   // 可以直接修改
    fmt.Println(p)
}

4. 那么什么时候使用指针接收器,什么时候使用值接收器

指针接收器也可以被使用在如下场景:当拷贝一个结构体的代价过于昂贵时

5. 匿名字段的方法

    p :=Person5{}
    //匿名字段的方法也会提升
    p.hobbyName="篮球"
    p.printName()   // 继承.子类没有printName方法,就会调用父类的
    //指定调用父类的printName方法(面向对象的super())
    p.Hobby1.printName()




//匿名字段的方法
type Hobby1 struct {
    id int
    hobbyName string
}

type Person5 struct {
    name string
    age int
    sex int
    Hobby1
}
//Hobby1 的绑定方法
func (h Hobby1)printName(){
    fmt.Println(h.hobbyName)
}
//Person5 的绑定方法
func (p Person5)printName(){
    fmt.Println(p.name)
}

6. 在方法中使用值接收器 与 在函数中使用值参数

  • 方法: 指针和值都可以来调用
  • 函数 只能传值

7. 在方法中使用指针接收器 与 在函数中指针参数

  • 方法: 指针和值都可以来调用
  • 函数 只能传递指针

8.在非结构体上的方法

    //像给int类型绑定个add方法可以吗?
    //var i int =10
    //i.add()  // 不行

// 但是可以给传统数据类型重命名,然后绑定方法
    var i Myint=10
    i.add()
    i.add()
    i.add()
    fmt.Println(i)


type Myint int //把int类型重命名为Myint

func (i *Myint)add(){
    (*i)=(*i)+1     // 指针类型 解引用
}

四. 接口 type 名字 interface

interface(接口)是golang最重要的特性之一,Interface类型可以定义一组方法,但是这些不需要实现。并且interface不能包含任何变量。

简单的说:

  • interface是方法的集合
  • interface是一种类型,并且是指针类型
  • interface的更重要的作用在于多态实现

1. 定义

定义一个对象的行为,一系列方法的集合

规范了子类应该有哪些行为,类似abc模块
django中: 父类写了一个发布方法,没有d代码.只有一个raise, 子类继承,但是没有重写该方法,一调用就报错



type  接口名称 interface {
method1 (参数列表) 返回值列表
method2 (参数列表) 返回值列表
...
}

2. 简单使用

  • 接口的使用不仅仅针对结构体,自定义类型、变量等等都可以实现接口。
  • 如果一个接口没有任何方法,我们称为空接口,由于空接口没有方法,所以任何类型都实现了空接口。
  • 要实现一个接口,必须实现该接口里面的所有方法。
package main

import "fmt"

//1.定义了一个鸭子接口(run speak方法)
type DuckInterface interface {
    run()
    //run(a int)int     // 有参数,有返回值
    speak()
}




//写一个唐老鸭结构体,实现该接口
type TDuck struct {
    name string
    age int
    wife string
}
//实现接口(只要结构体绑定了接口中的所有方法,就叫做结构体实现了该接口)
func (t TDuck)run() {   //传参的话,接口也必须定义
    fmt.Println("我是唐老鸭",t.name,"我会走路")
}
func (t TDuck)speak() {
    fmt.Println("我是唐老鸭",t.name,"我会说话")
}



//写一个肉鸭结构体
type RDuck struct {
    name string
    age int
}
func (t RDuck)run() {   //传参的话,接口也必须定义
    fmt.Println("我是肉鸭",t.name,"我会走路")
}
func (t RDuck)speak() {
    fmt.Println("我是肉鸭",t.name,"我会说话")
}

func main() {
    //实例化
    var t TDuck=TDuck{"嘤嘤怪",17,"刘亦菲"}
    var r RDuck=RDuck{"怪五",18}
    //唐老鸭的run
    t.run()
    //肉的run
    r.run()

    //定义一个鸭子接口类型(肉鸭和唐老鸭都实现了鸭子接口,所以都可以把对象赋值给鸭子接口)
    var i1 DuckInterface
    i1 = TDuck{"嘤嘤怪",17,"刘亦菲"}
    var i2 DuckInterface
    i2 = RDuck{"怪五",18}
    //分别实现run方法
    i1.run()
    i2.run()
}
原文地址:https://www.cnblogs.com/fwzzz/p/12734552.html