goLang学习笔记(二)

426 阅读11分钟

十一:数组和切片

不允许混合不同类型的元素

a:slice可以向后扩展,不可以向前扩展
b:s[i]不可以超越len(s),向后扩展不可以超越底层数组cap(s)
c:添加元素时如果超越cap,系统会重新分配更大的底层数组
d:由于值传递的关系,必须接收append的返回值
e:s = append(s,val)

1、数组的声明
    var a [3]int 声明一个长度为3的整型数组,数组中的所有元素都被自动赋值为数组类型的零值
    a := [3]int{12,13,19}
    a := [...]int{12,24,25}
    数组的大小是类型的一部分,数组不能调整大小

2、数组是值类型
    go中的数组是值类型而不是引用类型

3、数组的长度
len(a)

4、使用range迭代数组
    for i, v := range a {

    }

    for _, v := range a {

    }

5、多维数组
    a := [2][2]string{
        {'dd', "dsd"},
        {"sc", "xs"},
    }

6、切片
    a:创建一个切片
        a := [5]int{23,343,43,43,43}    var b []int = a[1:4]
        c := []int{5,3,5}

    b:切片的修改
        切片所做的更改会反映在数组中

    c:切片的长度和容量
        切片的容量是从创建切片索引开始的底层数组中元素数
        切片可以重置容量

    d:使用make创建一个切片
        make( []T, len, cap)[]T    i := make([]int, 5, 5)
    e:追加切片元素
        1、切片是动态的,使用append可以将元素追加到切片上  append(s[]T,x...T)[]T
        2、当新的元素被添加到切片时,会创建一个新的数组。现有的数组被复制到这个新的数组中,并返回这个新数组的新切片中。
        3、切片类型的零值是nil,一个nil切片的长度和容量都是0。可以使用append函数将值追加到nil切片
        4、...运算符可以将一个切片添加到另一个切片中  food := append(veggies, fruits...)

    f:切片的函数传递
        当切片作为参数传递给函数时,函数内所做的更改也会在函数外可见

    g:切片的内存优化
        切片持有对底层数组的引用。只要切片在内存中,数组就不能被垃圾回收。假设有一个非常大的数组,只要处理一小部分。
        解决方法L:使用copy函数生成一个切片的副本

十二:可变函数参数

func find(num int, nums ...int){

}
1、可变参数函数的工作原理是把可变参数转换为一个新的切片 ,可以不给可变参数nums传入任何参数, 
nums是一个长度和容量为0的nil切片

2、将切片直接传入可变参数函数 在切片后面加...  切片将直接传入函数,不再创建新的切片  
find(89,nums...)

十三:maps

map是在go中将值与键关联的内置类型,通过相应的键可以获取到值

1、如何创建map  map必须使用make函数初始化
    通过向map函数传入键和值的类型,可以创建map。make(map[type of key]type of value)是创建map的语法
       personSalary := make(map[string]int) 键是string类型,值是int类型

2、给map添加元素
personSalary["steve"] = 1233

personSalary := map[string]int{
    "steve":1200,
    "jam":1500,
}
personSalary["mike"] = 9000

3、获取map中的元素
   a: 获取一个不存在的元素,会返回int类型的零值0
   b: 要想知道map中到底是不是存在这个key,ok为true,表示key存在
           value, ok := map[key]

   c:遍历map中所有的元素需要用for range循环
        for key, value := range personSarlary{
            fmt.Printf(key,value)
        }
        使用for range遍历map时,不保证每次执行程序获取的元素顺序相同

4、删除map中的元素
    删除map中key的语法是delete(map,key)

5、获取map的长度
    len(personSalary)

6、map是引用类型
    和slices类似,map也是引用类型。

7、map的相等性
    map之间不能使用 == 操作符判断, == 只能用来检查map是否为nil

十四:字符串

字符串在go语言中有着自己特殊的实现,是一个字节切片。go中的字符串是兼容Unicode编码的,使用UTF-8进行编码

1、单独获取字符串的每一个字节
    a:%x格式限定符用于指定16进制编码
        for i:= 0;i < len(s);i++{

        }
    b:%c格式用于打印字符串中的字符   在uft-8编码中,一个代码点可能会占用超过一个字节的空间,可以使用rune解决

2、rune
 rune是int32的别称,在go中,rune表示一个代码点,代码无论占用多少个字节,都可以用一个rune表示
    func printChars(s string){
        runes := []rune(s)
        for i:= 0;i < len(runes);i++{
            fmt.Printf("%c",runes[i])
        }
    }

3、字符串的for range循环
    for index, rune := range s{

    }

4、用字节切片构造字符串
    byteSlice := []byte{0x43, 0x61, 0x66, 0xC3, 0xA9}
    str := string(byteSlice)
    fmt.Println(str)
    用utf-8编码的16进制字节,暑促是Cafe   16进制换成10进制也一样

5、rune切片构造字符串 runeSlice := []rune{0x0053,0x0065}  //16进制的Unicode代码点

6、字符串的长度
    uft8 package包中的func RuneCountInString(s string) (n int)方法用来获取字符串的长度,这个方法传入一个字符串
    然后返回字符串中的rune的数量

7、字符串是不可变的
    a:go中的字符串是不可变的,一旦一个字符串被创建,那么它将无法被修改
    b:为了修改字符串,可以把字符串转化为一个rune切片。然后这个切片可以进行任何想要的改变,然后转化为一个字符串
        func mutate(s []rune) string {
            s[0] = 'a'
            return string(s)
        }
        func main() {
            h := "hello"
            fmt.Println(mutate([]rune(h)))
        }

十五:指针

  • 要改变内容必须使用指针接收者
  • 结构过大也考虑使用指针接收者
  • 一致性:如果有指针接收者,最好都是指针接收者
  • 值接收者是go语言特有
  • 值/指针接收者均可接收值/指针

1、指针的声明
    a:指针的变量类型为 *T,该指针指向一个T类型的变量
        b := 255    var a *int = &b
        &操作符用于获取变量的地址
    b:指针的零值是 nil

2、指针的解引用
    指针的解引用可以获取指针所指向的变量的值。将a解引用的语法是 *a

3、向函数传递指针参数
    change(val *int){
        *val = 55
    }

4、不要向函数传递数组的指针,而应该使用切片
    a:把一个指向数组的指针传递给这个函数     a[x]是(*a)[x]的简写形式
    b:最好使用切片修改数组

5、go不支持指针运算

十六:结构体

1、结构体的声明
    type Employee struct {
        firstName string
        lastName  string
        age       int
    }
    匿名结构体:
    var employee struct {
        fistName, lastName string
        age int
    }

2、创建匿名结构体
    emp3 := struct {
        firstName, lastName string
        age, salary         int
    }{
        firstName: "And",
        lastName:  "Nik",
        age:        31,
        salary:     50000,
    }

3、结构体的零值

4、结构体的指针
    emp8 := &Employee{"Sam", "And", 55, 6000}
    访问字段:(*emp8).firstName
            可以使用emp8.firstName来代替显式的解引用 (*emp8).firstName

5、匿名字段
    创建结构体时,字段可以只有类型,而没有字段名。这样的字段称为匿名字段
    //含有两个匿名字段
    type Person struct {
        string
        int
    }
    虽然匿名字段没有名称,但其实匿名字段的名称就默认为它的类型

6、嵌套结构体
    type Address struct {
        city,state string
    }
    type Person struct {
        name string
        age int
        address Address
    }

    var p = Person
    person.name = "nav"
    p.age = 50
    p.address = Address {
        city: 'Chi'
        state: "s"
    }

7、提升字段
    如果结构体中有匿名的结构体类型字段,则结构体内的字段就称为提升字段。提升字段就像是属于
    外部结构体一样,可以直接访问

8、导出结构体和字段
    如果结构体名称以大写字母开头,则是其他包可以访问的导出类型。如果结构体里的字段首字母大写,
    也能被其他包访问

9、结构体相等性
   a: 结构体是值类型,如果它的每一个字段都是可比较的,则该结构体也是可比较的。
         如果两个结构体变量的对应字段相等,则这两个变量也是相等的

   b:如果结构体包含不可比较的字段,则结构体变量也不可比较
        type image struct {
            data map[int]int
        }

十七:方法

1:方法就是一个函数,在func这个关键字和方法名中间加入里一个特殊的接受器类型。
 a: 接收器可以是结构体或非结构体类型,可以在方法内部访问'
    type Employee struct {
        name string
    }
    func (e Employee) display() {

    }
b:方法调用
    emp1 := Employee{
        name: 'Sam'
    }
    emp1.display()

2、为什么有了函数还需要方法
    a:go不是纯粹的面向对象编程语言,而且go不支持类,基于类型的方法是一种实现和类相似行为的途径
    b:相同名字的方法可以定义在不同的类型,而相同名字的函数是不被允许的

3、指针接收器和值接收器
    a:指针接收器的方法内部的改变对于调用者是可见的,值接收器不是这样
    b:指针接收器使用:
        1:对方法内部的接收器所做的改变应该对调用者可见时
        2:结构体有很多字段,在方法内部使用结构体作为值接收器需要拷贝整个结构体;使用
           指针接收器,只会传递一个指针到方法的内部使用

 4、匿名字段的方法
     属于结构体的匿名字段的方法可以被直接调用,就像方法属于定义匿名字段的结构体一样

5、方法中使用值接收器与在函数中使用值参数
    a:一个函数有一个值参数,只能接受一个值参数
    b:一个方法有一个值接收器,可以接受值接收器和指针接收器

6、方法职工使用指针接收器与在函数中使用指针参数
    函数中只接受指针,指针接收器的方法可以使用值接收器和指针接收器

7、在非结构体的方法上
    a:为了在一个类型上定义一个方法,方法的接收器类型定义和方法的定义应该在同一个包中
       func (a int) add(b int){
       // 尝试把一个add方法添加到内置的类型int,不被允许,因为add方法的定义和int类型的定义不
         //再一个包中
       }

       type myInt int
       func (a myInt) add(b myInt) myInt{
        //这样是可以的
       }

十八:接口一

1、接口
    a:在面向对象的领域里:接口定义一个对象的行为
    b:在go语言中,接口就是方法签名的集合。当一个类型定义里接口中的所有方法,称它实现里接口
    接口指定里一个类型应该具有的方法,并由该类型决定如何实现这些方法

2、接口的声明与实现
    a:其他语言如java,要求一个类使用implement关键字,显示的声明该类实现类接口
    b:在go中一个类型包含类接口中声明的所有方法,就隐式的实现类接口

3、接口的实际用途

4、接口的内部表示

5、空接口
    没有包含方法的接口称为空接口,空接口表示为interface{}.空接口没有方法,所有类型都实现了空接口

6、类型断言
    类型断言用于提取接口的底层值。在语法i.(T)中,接口i的具体类型是T。
    使用v,ok := i.(T)不会报错

7、类型选择
    a:类型选择用于将接口的具体类型与很多case语句所指定的类型进行比较
        switch i.(type){
            case string:
                fmt.Printtf('d')
            case int:
                fmt.Printf('x')
        }

    b:可以将一个类型和接口相比较。如果一个类型实现类接口,那么该类型与其实现的接口可以互相比较

十九:接口二

1、实现接口:指针接受者与值接受者
    a:使用值接受者的方法,可以用值来调用,也能用指针调用
    b:对于使用指针接受者的方法,用一个指针或者一个可取得地址的值调用都是合法的。
        但接口中存储的具体值不能取到地址

2、实现多个接口

3、接口的嵌套
    go语言没有提供继承机制,可以通过嵌套其他接口创建一个新接口

4、接口的零值
    接口的零值是nil,对于值为nil的接口。其底层值和具体类型都为nil,调用它的方法会产生异常。

二十:并发入门

1、并发是指立即处理多个任务的能力
    go语言原生支持并发,go使用go协程和信道来处理并发

2、并行是指同时处理多个任务
    并行并不一定会加快运行速度,因为并行运行的组件之间可能需要相互通信