今天总结一下golang中的复合数据类型的使用:

复合数据类型主要有:指针、数组、结构体、切片(slice)、map、函数、接口(interface)、通道

一:指针

1. 基本数据类型,变量存的就是值,也叫值类型

2. 获取变量的地址,用 & ,比如: var num int ,获取 num 的地址: &num

3. 指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值,比如: var ptr *int = &num

4. 获取指针类型所指向的值,使用: * ,比如: var ptr *int ,使用 *ptr 获取 ptr 指向的值

注意:

值类型,都有对应的指针类型,形式为 *数据类型,比如:int对应的指针就是 *int ,float64 对应的指针类型就是 *float64 ,依此类推;

值类型包括:基本数据类型、数组和结构体(struct)

1 > 值类型与引用类型的特点:

值类型:变量直接存储值,内存通常在栈中分配;

引用类型:变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配,当没有任何变量应用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收;

2 > Golang中值类型和引用类型的区分

值类型:基本数据类型(int系列、float系列、bool、string)、数组和结构体

引用类型:指针、slice切片、map、管道、interface等都是引用类型

二:数组

1. 基本结构:var arrName [len]T

例如:arrName := [...]int{1, 2, 3} arrName是数组的名字,len是数组的长度,需要时常量,在编译时确定;T是数组元素的类型;长度也可以通过初始化元素个数决定,这时,len部分使用 "..." 代替,如果数组元素是可比较的,那么数组也是可比较的;但只能是同类型的数组才能比较,否则编译错误哦 !%t - 输出一个bool值,%T - 输出一个变量的类型。

注意:数组是按值传递的,但可以通过传递一个数组指针。

三:结构体


type Name struct {

    ID          int

    Name        string

    ManagerID   int

}

2. 可以通过点号访问起成员,点号也适用于结构体指针,和C语言不一样,不需要 ->

3. 结构体定义中不可以包含自己,但可以包含本身的指针,这样可以构造链表,树等结构。

4. 没有任何成员的结构体称为空结构体,写作 struct {}。他没有长度,也不携带任何信息。

5. 其成员是否可以导出,看其名字的首字母是否大写。

6. 在Go这种按值调用的语言中,调用函数接收到的是实参的一个副本,并不是实参的引用。

7. Go的实例化结构体的方式有:1. 以 var 的方式声明结构体即可完成实例化。 2. 通过 new 关键字来进行实例化。 3. 取结构体的地址来实现实例化。

type Test struct {

    Address         string

    Port            int

}

//第一种:以 var 的方式声明结构体即可完成实例化
var test Test
test.Address = "127.0.0.1"
test.Port    = 8801

//第二种:通过 new 关键字来进行实例化
test := new(Test)
test.Address = "127.0.0.1"
test.Port    = 8801

//第三种:取结构体的地址来实现实例化
test := &Test{}
test.Address = "127.0.0.1"
test.Port    = 8801

注意:以快捷方式访问匿名成员的内部变量,同样适用于访问匿名成员的内部方法。因此,外围的结构体类型获取的不仅是匿名成员的内部变量,还有相关的方法。这个机制就是从简单类型对象组合成复合类型对象的主要方式。 在Go中,组合是面向对象编程方式的核心

四:切片(slice)

1. 定义:表示一个拥有相同类型元素的可变长度序列:[]T,看上去像没有长度的数组类型。

2. slice有三个属性:指针、长度(len)、容量(cap)。

3.一个底层数组可以对应多个slice,而且这些slice之间也可以重叠。

4. 可以通过子串生成操作,从数组或者slice得到slice,访问容量外的元素,会导致宕机错误。

5. 创建一个数组的slice,等于为数组创建了一个别名。

6. 和数组不同,slice无法做比较,因此不能用 == 在测试两个slice是否拥有相同的元素。标准库里面提供了高度优化的bytes.Equal来比较两个字节slice ([]byte),但对于其他类型,则需要我们自己写函数来比较。

7. slice只能与nil做比较,slice不能作为map的key。

8. 对于可能修改slice属性的函数,一般都返回 修改后的slice,规则是:将函数返回值赋值给此前的slice。 xs = append(xs, y) 。

9. copy函数也很常用。

注意:slice并不是纯引用类型,而是像下面这种聚合类型:

type IntSlice struct {

    ptr             *int
    len, cap         int
}

对于下面的列子:

func modifySlice(s []int) {
    s[0] = 0
}
func appendSlice(s []int) {
    s = append(s, 4)
}
func main() {
    s := []int { 1, 2, 3 }
    fmt.Println(s) // [1,2,3]
    modifySlice(s) // [0,2,3], 虽然ptr是传值的,但其是指针,可以修改其所指地址的内容,当然这里要在len的范围内。
    appendSlice(s) // [0,2,3], ptr,len, cap都是传值的,虽然在函数里面len发生了变化,ptr, cap是否发生变化不定,但当其返回这里时,ptr, len, cap都不可能变化,因此s没有变化
}

注意:Go语言中的参数,都是按值传递。

五:map

1. GO语言中的map是散列表的引用,map[K]V,其中K类型,必须可以通过 == 进行比较,虽然浮点数也可以,但不是一个好主意。

2. 可以用make来创建一个map:ages := make( map[string]int ) 或者

ages := map[string]int {
    "alice":31,
    "charlie":34,
}

3. 可以使用delete函数从map中删除一个元素。 delete(ages, "alice")。

4. map元素不是一个变量,不可以获取它的地址。因为map是动态变化的。

5. 对于nil的map,可以查找,删除,获取元素个数,但是设置操作会导致错误。设置元素前,必须初始化map。和slice一样,map也是不可比较的,唯一合法的就是和nil做比较。

6. map可以实现集合类型,map[T]bool

7. unicode.ReplacementChar 就是解析Unicode编码遇到不合法的UTF-8字符时,返回的值。

8. map值类型本身可以是复合类型:map[string]map[string]bool

六:函数

七:接口

简单来说,Go语言中的接口就是一组方法的签名。接口是Go语言整个类型系统的基石,其他语言的接口是不同组件之间的契约的存在,对契约的实现是强制性的,必须显式声明实现了该接口,这类接口称之为“侵入式接口”。而Go语言的接口是隐式存在,只要实现了该接口的所有函数则代表已经实现了该接口,并不需要显式的接口声明哦

举例说明一下:

package main

    import "fmt"

    //支付接口
    type Pay interface {
        //函数名 + 参数 + 返回类型
        Order(id int ) int          //订单支付
        Sync()         string       //订单回调
    }


    //微信支付
    type WeChatPay struct {
        pay string
    }

    //阿里支付
    type AliPay struct {
        pay string
    }

    //微信支付的订单状态
    func (w *WeChatPay) Order(id int) int {
        return 200
    }

    //微信支付订单回调操作
    func (w *WeChatPay) Sync() string{
        return "微信支付订单回调成功!"
    }

    //阿里支付的订单状态
    func (a *AliPay) Order(id int) int {
        return 300
    }

    //阿里支付订单回调操作
    func (a *AliPay) Sync() string{
        return "阿里支付订单回调成功!"
    }


    //测试接口
    func test(pay Pay,id int){
        fmt.Println(pay.Order(id))//支付状态
        fmt.Println(pay.Sync())//回调状态
    }



    //接口的调用结果
    func main() {
        wx := &WeChatPay{}
        test(wx,9)
    }

测试结果: