一文掌握GO语言实战技能(二)

  • Go 数组

  • Go 切片

  • Go 变量和内存地址

  • Go Map 类型

  • Go 面向对象编程

  • Go 方法的定义

GO 数组

数组是同一类型的元素集合。Go中的数组下标从0开始,因此长度为n的数组下标范围是[0, n-1]。

整数数组中元素默认初始化为0,字符串数组中的元素默认初始化为""。

 var a [3]int
 a[0] = 10
 a[1] = 20
 
var a [3]int = [3]int{10, 20, 30}
//定义时数组初始化

//数组初始化
 var a [3]int = [3]int{10, 20, 30}
//定义时数组初始化

a := [3]int{10, 20, 30}
//定义时数组初始化

a := [...]int{10, 20, 30}
//定义时数组初始化
 
a := [3]int{10}
//定义时数组初始化

a := [3]int{2:10}
//定义时数组初始化
  • 数组⻓度是类型的⼀部分

var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
var b [5]int
b=a
//a、b是不同类型的数组,不能赋值
  • len 内置函数

var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30

fmt.Printf(“len:%d\n”, len(a))
  • 数组的遍历

a. 下标进行遍历

b. range 函数进行遍历

range函数是个神奇而有趣的内置函数,你可以使用它来遍历数组,切片和字典。

  • 当用于遍历数组和切片的时候,range函数返回索引和元素。

  • 当用于遍历字典的时候,range函数返回字典的键和值。

package main

import "fmt"

func main() {
    var a [3]int
    a[0] = 10
    a[1] = 20
    a[2] = 30
    // 使用下标遍历
    for i := 0; i < len(a); i++ {
      // fmt.Printf(a[i] + " ")
    }
 
    for index, val := range a {
      // fmt.Printf(a[i] + " ")
    }
    // 这里我们使用range来计算一个切片的所有元素和
    // 这种方法对数组也适用
    nums := []int{2, 3, 4}
    sum := 0
    for _, num := range nums {
        sum += num
    }
    fmt.Println("sum:", sum)

    // range 用来遍历数组和切片的时候返回索引和元素值
    // 如果我们不要关心索引可以使用一个下划线(_)来忽略这个返回值
    // 当然我们有的时候也需要这个索引
    for i, num := range nums {
        if num == 3 {
            fmt.Println("index:", i)
        }
    }

    // 使用range来遍历字典的时候,返回键值对。
    kvs := map[string]string{"a": "apple", "b": "banana"}
    for k, v := range kvs {
        fmt.Printf("%s -> %s\n", k, v)
    }

    // range函数用来遍历字符串时,返回Unicode代码点。
    // 第一个返回值是每个字符的起始字节的索引,第二个是字符代码点,
    // 因为Go的字符串是由字节组成的,多个字节组成一个rune类型字符。
    for i, c := range "go" {
        fmt.Println(i, c)
    }
}
sum: 9
index: 1
a -> apple
b -> banana
0 103
1 111
  • Go数组排序

Go 分别提供了sort.Float64s()  sort.Strings()  sort.Ints() 对不同类型的数组进行排序,默认是升序。

降序需要使用sort.Reverse。

package main
 
import (
 "fmt"
 "sort"
)
 
func main() {
 
   arrayInt := []int{3, 1, 2, 5, 4}
 
   arrayFloat := []float64{3.2, 1.8, 1.9, 2.2, 4.3}
 
   arrayString := []string{"abc", "ab", "bc"}
 
   // 升序
    sort.Ints(arrayInt)
    sort.Float64s(arrayFloat)
    sort.Strings(arrayString)

    fmt.Println("sort int:", arrayInt)
    fmt.Println("sort float:", arrayFloat)
    fmt.Println("sort ", arrayString)

    // 降序
    sort.Sort(sort.Reverse(sort.IntSlice(arrayInt)))
    sort.Sort(sort.Reverse(sort.Float64Slice(arrayFloat)))
    sort.Sort(sort.Reverse(sort.StringSlice(arrayString)))

    fmt.Println("After reversed: ")

    fmt.Println("sort int:", arrayInt)
    fmt.Println("sort float:", arrayFloat)
    fmt.Println("sort ", arrayString)
 
}
  • 数组的拷贝和传参

a. 数组是值类型

var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
b := a
//b拷⻉了数组a中所有元素
b[0] = 1000
a: [10,20,30]
b: [1000,20,30]

b. 数组是值类型,函数传参也会拷⻉

package main
import ("fmt")
func main() {
  var a [3]int
  a[0] = 10
  a[1] = 20
  a[2] = 30
  
  modify(a) 
  fmt.Println(a)    // [10,20,30]
}
func modify(b [3]int) {
  b[0] = 1000
  fmt.Println(b)    // [1000,20,30]
  return
}

GO 切片

GO的切片是基于数组类型的一层封装。Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

 var a []int
//定义⼀个int类型的空切⽚
  • 切⽚初始化, a[start:end]创建⼀个包括从start到end-1的切⽚。

package main
import (
  "fmt"
)
func main() {
    // 初始化方法1
    a:= [5]int{76, 77, 78, 79, 80}
    var b []int = a[1: 4] //基于数组a
    // 创建⼀个切⽚,包括元素a[1] a[2] a[3]
    fmt.Println(b)
    
    // 初始化方法2
    c:=[]int{6, 7, 8} //创建一个数组并返回⼀个切⽚
    fmt.Println(c)
}

  • 数组切片的基本操作

a. arr[start:end]:包括start到end-1(包括end-1)之间的所有元素

b. arr[start:]:包括start到arr最后⼀个元素(包括最后一个元素)之间的所有元素

c. arr[:end]:包括0到end-1(包括end-1)之间的所有元素

d. arr[:]:包括整个数组的所有元素

  • 切⽚修改

package main
import (
  "fmt"
)
func main() {
    
    //创建⼀个数组,其中[...]是编译器确定数组的长度,darr的长度是9
   darr:=[...]int{57, 89, 90, 82, 100, 78, 67, 69, 59}
   //基于darr创建⼀个切⽚dslice,包括darr[2],darr[3],darr[4]三个元素
   dslice:=darr[2:5]
   fmt.Println("array before",darr)
   for i:=range dslice {
     //对于dslice中每个元素进行+1,其实修改是darr[2],darr[3],darr[4]
    dslice[i]++
  }
 
  fmt.Println("array after",darr)
}      
// array before [57 89 90 82 100 78 67 69 59]
// array after [57 89 91 83 101 78 67 69 59]

切片是数组的引用,当两个切片源于一个数组,改变其中一个数组,另一个数组也响应的进行的修改。

package main
import (
  "fmt"
)
func main() {
    
 numa:=[3]int{78, 79 ,80}
 //创建⼀个切⽚,包含整个数组的所有元素
  
 nums1 := numa[:]
 nums2 := numa[:]

 fmt.Println("array before change 1",numa)
   
 nums1[0]= 100
  
 fmt.Println("array after modification to slice nums1", numa)
   
 nums2[1]=101
  
 fmt.Println("array after modification to slice nums2", numa)
} 
// array before change 1 [78 79 80]
// array after modification to slice nums1 [100 79 80]
// array after modification to slice nums2 [100 101 80]
  • 使⽤make创建切片

make为内建类型slice、map和channel分配内存。

 package main
  import (
    "fmt"
  )
 func main() {
    //[]中没有⻓度 第一个5 是切片的长度,第二个 5是切片的容量
    i:=make([]int, 5, 5)
    fmt.Println(i)
 }
 // [0 0 0 0 0]
  • 切片的⻓度和容量

 package main
  import (
    "fmt"
  )
 func main() {
 
    var a []int

    a =make([]int, 5, 10)
    a[0] = 10
    fmt.Printf("a=%v addr:%p len:%d cap:%d\n", a,a,len(a),cap(a))   // a=[10 0 0 0 0] addr:0xc0000a0000 len:5 cap:10

    a= append(a,11)
    fmt.Printf("a=%v addr:%p len:%d cap:%d\n", a,a,len(a),cap(a))  //a=[10 0 0 0 0 11] addr:0xc00009e000 len:6 cap:10

    for i:=0; i < 8;i++ {
      a= append(a, i)
      fmt.Printf("a=%v addr:%p len:%d cap:%d\n", a,a,len(a),cap(a))
      // a=[10 0 0 0 0 11 0] addr:0xc00009e000 len:7 cap:10
      // a=[10 0 0 0 0 11 0 1] addr:0xc00009e000 len:8 cap:10
      // a=[10 0 0 0 0 11 0 1 2] addr:0xc00009e000 len:9 cap:10
      // a=[10 0 0 0 0 11 0 1 2 3] addr:0xc00009e000 len:10 cap:10
      // a=[10 0 0 0 0 11 0 1 2 3 4] addr:0xc0000a4000 len:11 cap:20
      // a=[10 0 0 0 0 11 0 1 2 3 4 5] addr:0xc0000a4000 len:12 cap:20
      // a=[10 0 0 0 0 11 0 1 2 3 4 5 6] addr:0xc0000a4000 len:13 cap:20
      // a=[10 0 0 0 0 11 0 1 2 3 4 5 6 7] addr:0xc0000a4000 len:14 cap:20
    }
    // 观察切片的扩容操作 扩容的策略是翻倍扩容
    a = append(a,1000)
    fmt.Printf("a=%v addr:%p len:%d cap:%d\n", a,a,len(a),cap(a))
    // a=[10 0 0 0 0 11 0 1 2 3 4 5 6 7 1000] addr:0xc0000200a0 len:15 cap:20
 }

例子2:

package main

import (
  "fmt"
)
func main() {
     
  fruitarray :=[...]string{
   
    "apple", "orange", "grape",
      
    "mango", "water melon",
    
    "pine apple", "chikoo"}
    
  fruitslice:=fruitarray[1:3]//长度是2,容量is 6
     
  fmt.Printf("length of slice %d capacity %d",len(fruitslice), cap(fruitslice))
}  
// length of slice 2 capacity 6
  • 切片再切片

package main
   import (
    "fmt"
)
func main() {
     
  fruitarray:=[...]string{
    
    "apple", "orange", "grape", "mango",
        
    "water melon", "pine apple", "chikoo"}
      
    fruitslice:=fruitarray[1:3]
    //长度是2, 容量是6
     
    fmt.Printf("length of slice %d capacity %d\n",len(fruitslice), cap(fruitslice))
    //再重新进行切⽚,不能⼤于数组fruitarray的⻓度,否则越界
    
    fruitslice = fruitslice[:cap(fruitslice)]
        
    fmt.Println("After re-slicing length is",len(fruitslice), "and capacity is",cap(fruitslice))
           
}

// length of slice 2 capacity 6
// After re-slicing length is 6 and capacity is 6
  • append 操作

package main
import (
  "fmt"
)
func main() {
 cars:=[]string{"Ferrari", "Honda", "Ford"}
  //⻓度和容量都等于3
  fmt.Println("cars:", cars, "has old length",len(cars), "and capacity", cap(cars))
          
  cars=append(cars, "Toyota")
  //容量等于6
   
  fmt.Println("cars:", cars, "has new length",len(cars), "and capacity", cap(cars))
}
// cars: [Ferrari Honda Ford] has old length 3 and capacity 3
// cars: [Ferrari Honda Ford Toyota] has new length 4 and capacity 6

a. append⼀个切⽚

package main
import (
  "fmt"
)
func main() {
    
  veggies:=[]string{"potatoes","tomatoes","brinjal"}
          
  fruits :=[]string{"oranges","apples"}
  //fruits后⾯的3个点表示展开fruits切⽚成⼀个元素
      
  food:= append( veggies, fruits...)
  fmt.Println("food:",food)
}
//  food: [potatoes tomatoes brinjal oranges apples]
  • 空切片

package main
  import (
    "fmt"
  )
  func main() {
    //定义names是⼀个空切⽚,⻓度和容量都等于0
    //不能对空切⽚进行访问,否则panic
   
 var names []string
    
 if names == nil {
     
     fmt.Println("slice is nil going to append")
      
     names = append(names, "John", "Sebastian", "Vinay")
            
     fmt.Println("names contents:",names)   
 }
}
  • 切片传参

package main
import (
 "fmt"
)
//在函数内部修改numbers切⽚的值
 
func subtactOne(numbers []int) {
    for i:= range numbers {
      numbers[i]-=2
    }
}
func main() {
    nos:=[]int{8, 7, 6}
    fmt.Println("slice before function call", nos)
    subtactOne(nos)
    //nos修改⽣效了,说明切片是引⽤类型
    fmt.Println("slice after function call", nos)
}
// slice before function call [8 7 6]
// slice after function call [6 5 4]
  • 切片深拷贝

package main

import (
  "fmt"
)
func main() {
   // 当元素数量超过容量
   // 切片会在底层申请新的数组
   slice := make([]int, 5, 5)
   slice1 := slice
   slice = append(slice, 1)
   slice[0] = 1
   fmt.Println(slice)//[1 0 0 0 0 1]
   fmt.Println(slice1)//[0 0 0 0 0]
   // copy 函数提供深拷贝功能
   // 但需要在拷贝前申请空间
   slice2 := make([]int, 4, 4)
   slice3 := make([]int, 5, 5)
   fmt.Println(copy(slice2, slice))//4
   fmt.Println(copy(slice3, slice))//5
   fmt.Println(slice2)// [1 0 0 0]
   fmt.Println(slice3)// [1 0 0 0 0]
   slice2[1] = 2
   slice3[1] = 3
   fmt.Println(slice)//[1 0 0 0 0 1]
   fmt.Println(slice2)//[1 2 0 0]
   fmt.Println(slice3)//[1 3 0 0 0]
}
  • 切片遍历

var a [3]int
a[0] = 10
a[1] = 20
a[2] = 30
B := a[:]
for index, val := range b {

}
//和数组遍历是⼀样的
  • new⽤于各种类型的内存分配,new返回是⼀个指针

Go 变量和内存地址

每个变量都有内存地址,可以说通过变量来操作对应⼤大⼩小的内存 。注意:通过&符号可以获取变量的地址。

普通变量存储的是对应类型的值,这些类型就叫值类型 例: var a int32。

指针类型的变量存储的是一个地址,所以⼜叫指针类型或引用类型, 在定义时,前方加*就变为引用类型,var a *int32, a中存储的是一个地址,我们称为引用类型或者指针类型。

package main
import (
  "fmt"
)
func main() {
  var a int32
  a = 100
  fmt.Printf("%d\n", a)
  fmt.Printf("%p\n", &a)
  b:=255
     
  var c *int = &b
       
  fmt.Printf("Type of c is %T\n", c)
          
  fmt.Println("address of b is", c)
}
// 100
// 0xc00001608c
// Type of a is *int
// address of b is 0xc00009a008
一文掌握GO语言实战技能(二)_第1张图片
  • 指针类型变量的默认值为nil,也就是空地址

package main
import (
  "fmt"
)
func main() {
  a := 25 
  var b *int 
  if b == nil {
    fmt.Println("b is", b) 
    b = &a
    fmt.Println("b after initialization is", b)
  }
}
// b is 
// b after initialization is 0xc0000a2008
  • 如果操作指针变量指向的地址里面的值呢?

注意:通过* 符号可以获取指针变量指向的变量。

package main
import (
  "fmt"
)
func main() {
  b := 255
  a := &b
    
  fmt.Println("address of b is", a)
        
  fmt.Println("value of b is",*a)
}
// address of b is 0xc00009a008
// value of b is 255
  • 通过指针修改变量的值

package main

import (
  "fmt"
)
func main() {
  b := 255
  a := &b
    
  fmt.Println("address of b is", a)
  fmt.Println("value of b is", *a)
  *a++
  fmt.Println("new value of b is",b)
}
// address of b is 0xc00009a008
// value of b is 255
// new value of b is 256
  • 指针变量传参

package main

import (
  "fmt"
) 
func change (val *int){
  *val = 55
}
func main() {
  a := 255
  fmt.Println("value of a before function call is",a)
  b := &a
  change(b)
  fmt.Println("value of a after function call is", a)
}
// value of a before function call is 255
// value of a after function call is 55

// 例子2
func change (arr *[3]int){
  (*arr)[0] = 90
}
func main() {
  a:=[3]int{89, 90, 91}
  change(&a)
  fmt.Println(a)
}
// [90 90 91]

对比 切片传参注意:切片是引用类型!!
func change (arr []int){
  arr[0] = 90
}
func main() {
  a:=[3]int{89, 90, 91}
  change(a[:])
  fmt.Println(a)
}
// [90 90 91]
// make⽤来分配引⽤类型的内存,⽐如 map、slice以及channel ,new⽤来分配除引用类型的所有其他类型的内存,⽐如 int、数组等
 
  • 值拷⻉和引⽤拷⻉

 package main
 import (
    "fmt"
 )
 func main() {
     // 值拷贝
     var a int = 100
     b := a
     
    // 引用拷贝
     var a int = 100
     var b *int = &a
     var c *int = b
     *c = 200
 }
一文掌握GO语言实战技能(二)_第2张图片

Go Map 类型

map类型是⼀个key-value的数据结构。注意:map必须初始化才能使⽤,否则panic。

map类型的变量默认初始化为nil,需要使用make分配map内存。

//var a map[key的类型]value类型
     
var a map[string]int
var b map[int]string
var c map[float32]string

package main

import (
  "fmt"
) 

func main() {  
  var a map[string]int
  if a == nil {
    fmt.Println("map is nil. Going to make one.")
    a = make(map[string]int)  
  }
}

  • map插入操作

package main

import (
  "fmt"
) 

func main() {  
  a := make(map[string]int)
  a["steve"] = 12000
  a["jamie"] = 15000
  a["mike"] = 9000
  fmt.Println("a map contents:", a)
}
// a map contents: map[jamie:15000 mike:9000 steve:12000]
  • map 声明方式二, 通过key访问map中的元素

package main

import (
  "fmt"
) 

func main() {  
  a := map[string]int {
    "steve": 12000,
    "jamie": 15000,
  }
  a["mike"] = 9000
  fmt.Println("a map contents:", a)
}
  • 如何判断map指定的key是否存在? value, ok := map[key]

package main

import (
  "fmt"
) 

func main() {  
  a := map[string]int {
    "steve": 12000,
    "jamie": 15000,
  }
  a["mike"] = 9000
  b := "joe"
  value, ok := a[b]
  if ok == true {
    fmt.Println("Salary of", b, "is", value)
  } else {
    fmt.Println(b,"not found")
  }
  fmt.Println("a map contents:", a)
}
// joe not found
// a map contents: map[jamie:15000 mike:9000 steve:12000]
  • map遍历操作

package main
import (
  "fmt"
) 

func main() {  
  a := map[string]int {
    "steve": 12000,
    "jamie": 15000,
  }
  a["mike"] = 9000
  for key, value:= range a {
      fmt.Printf("personSalary[%s] = %d\n", key, value)
  }
}
// personSalary[steve] = 12000
// personSalary[jamie] = 15000
// personSalary[mike] = 9000
  • map删除元素

package main
import (
  "fmt"
) 
func main() {  
  a := map[string]int {
    "steve": 12000,
    "jamie": 15000,
  }
  a["mike"] = 9000
  delete(a, "steve")
  fmt.Println("map after deletion", a)
}
// map after deletion map[jamie:15000 mike:9000]
  • map的⻓度  len(a)

package main
import (
  "fmt"
) 

func main() {  
  a := map[string]int {
    "steve": 12000,
    "jamie": 15000,
  }
  a["mike"] = 9000
  fmt.Println("length is", len(a))
}
// length is 3
  • map是引⽤类型

package main
import (
  "fmt"
) 
func main() {  
  a := map[string]int {
    "steve": 12000,
    "jamie": 15000,
  }
  a["mike"] = 9000
  b := a
  b["mike"] = 18000
  fmt.Println("a map changed", a)
}
// a map changed map[jamie:15000 mike:18000 steve:12000]
  • 默认情况下,map并不是按照key有序进⾏遍历的 ,map按照key进行排序,遍历 。

package main
import (
   "fmt"
   "sort"
)
func main() {
   var a map[string]int = make(map[string]int, 10)
   for i := 0; i < 10; i++ {
     key := fmt.Sprintf("key%d", i)
     a[key] = i
   }
   var keys []string
   for key, _ := range a {
     keys = append(keys, key)
   }
   sort.Strings(keys)
   for _, key := range keys {
     fmt.Printf("key:%s=%d\n", key, a[key])
   }
}
 
// key:key0=0
// key:key1=1
// key:key2=2
// key:key3=3
// key:key4=4
// key:key5=5
// key:key6=6
// key:key7=7
// key:key8=8
// key:key9=9
  • map类型的切⽚

  package main
  import (
    "fmt"
  )
  func main() {
    var mapSlice []map[string]int
    mapSlice = make([]map[string]int, 5)
    fmt.Println("before map init")
    for index, value := range mapSlice {
        fmt.Printf("index:%d value:%v\n", index, value)
    }
  

    mapSlice[0] = make(map[string]int, 10)
    mapSlice[0]["a"] = 1000
    mapSlice[0]["b"] = 2000
    mapSlice[0]["c"] = 3000
    mapSlice[0]["d"] = 4000
    mapSlice[0]["e"] = 5000
    fmt.Println("after map init")
    for index, value := range mapSlice {
      fmt.Printf("index:%d value:%v\n", index, value)
    }
  }
  
// before map init
// index:0 value:map[]
// index:1 value:map[]
// index:2 value:map[]
// index:3 value:map[]
// index:4 value:map[]
// after map init
// index:0 value:map[a:1000 b:2000 c:3000 d:4000 e:5000]
// index:1 value:map[]
// index:2 value:map[]
// index:3 value:map[]
// index:4 value:map[]

Go 面向对象编程

  • struct声明和定义

Go中⾯向对象是通过struct来实现的, struct是用户⾃定义的类型 , 注意:type是⽤来定义⼀种类型

 type User struct {
     Username  string
     Sex       string
     Age       int
     AvatarUrl string
 }

初始化 方法分为两种:

方法一:

注意:使⽤变量名+ ‘.’ + 字段名访问结构体中的字段
var user User
user.Age = 18
user.Username = “user01”
user.Sex = “男”
user.AvatarUrl = “http://my.com/xxx.jpg"

方法二:

var user User = User {
  “Username” : “user01”,
  “Age”: 18,
  “Sex”: “男”,
  “AvatarUrl”: “http://my.com/xxx.jpg”,
}

更简单的写法:

 user := User {
   “Username” : “user01”,
   “Age”: 18,
   “Sex”: “男”,
   “AvatarUrl”: “http://my.com/xxx.jpg”,
}
  • struct初始化的默认值是什么?

  var user User
  fmt.Printf(“%#v\n”, user)
  • 结构体类型的指针

注意:&User{}和new(User) 本质上是⼀样的,都是返回一个 结构体的地址

package main
import (
  "fmt"
) 

func main() {  
  var user *User = &User{}     
  fmt.Printf(“%p %#v\n”, user)

  // 直接初始化:

  var user *User = &User {
    Username : “user01”,
    Age: 18,
    Sex: “男”,
    AvatarUrl: “http://my.com/xxx.jpg”,
  }

  // 或者使用new
  var user User = new(User)
    user.Age = 18
    user.Username = “user01”
    user.Sex = “男”
    user.AvatarUrl = “http://my.com/xxx.jpg"
  }
  • 结构体的内存布局: 占⽤⼀段连续的内存空间

一文掌握GO语言实战技能(二)_第3张图片
  • 结构体没有构造函数, 必要时需要⾃⼰实现

  • 匿名字段: 即没有名字的字段      注意:匿名字段默认采用类型名作为 字段名

 type User struct {
     Username  string
     Sex       string
     Age int
     AvatarUrl string
 }
 
type User struct {
    Username  string
    Sex       string   
    Age int
    AvatarUrl string
    int
    string
}
  • 结构体嵌套

type Address struct {
     City           string
     Province       string
 }

type User struct {
     Username  string
     Sex       string
     Age int
     AvatarUrl string
     address *Address
}     

func main () {
  user := &User {
    Username: 'user01',
    Sex: 'man',
    address: &Address {
      Province: 'beijing',
      City: 'beijing',
    },
  }
}
  • 匿名结构体

第二种方式 先在结构体里面找Provice以及City,如果结构体里面没有,则在匿名结构体里面找,

type Address struct {
     City           string
     Province       string
}
type User struct {
    Username  string
    Sex       string
    Age int
    AvatarUrl string
    *Address
}
func main () {
    var user User
    user.Username = 'user01'
    user.Sex = 'man'
    // 第一种方式
    user.Address = &Address{
       Provice: 'bj',
       City: 'bj'
    }
   // 第二种方式
   user.Province = 'bj01'
   user.City = 'bj01'
}
  • 匿名结构体的冲突解决 使用结构体的字段,如果结构体字段有值,则使用结构体,如果没有则使用匿名结构体的字段 如果两个匿名结构体都有相同的字段,当访问这个相同字段时候,要指定访问那个匿名结构体的字段

package main
import (
  "fmt"
)

type Address struct {
  City   string
  Province string
  CreateTime string
}

type Email struct {
  Account  string
  CreateTime  string
}

type User struct { 
  Username  string
  Sex       string
  Age int
  AvatarUrl string
  *Address
  *Email
  CreateTime string
}


func main (){
  var user User
  user.Username = "user01"
  user.Sex = "man"
  user.City = "bj"
  user.Address = new(Address)
  user.Email = new(Email)
  user.Address.City ="bj01"
  user.Address.CreateTime ="001"
  user.Email.CreateTime = "002"
  fmt.Println( user.Email.CreateTime, user.Address.CreateTime)
}
  
  • 字段可⻅性,⼤写表示可公开访问,⼩写表示私有

type User struct {
    Username string
    Sex string
    Age int
    avatarUrl string // 私有
    CreateTime string
}
  • tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来 字段类型后⾯,以反引号括起来的 key-value结构的字符串,多个tag 以逗号隔开。

type User struct { 
    Username  string   `json:”username”,db:”user_name”`
    Sex       string   `json:”sex”`   
    Age       int      `json:”age”`
    avatarUrl string
    CreateTime string
}

Go 方法的定义

  • 和其他语⾔不⼀样,Go的⽅法采⽤另外⼀种⽅式实现

  • Go的⽅法是在函数前⾯加上一个接受者,这样编译器就知道这个⽅法属于哪个类型了

一文掌握GO语言实战技能(二)_第4张图片
  • 可以为当前包内定义的任何类型增加方法

一文掌握GO语言实战技能(二)_第5张图片
  • 函数和方法的区别: 函数不属于任何类型,⽅法属于特定的类型

  • 指针类型作为接受者一文掌握GO语言实战技能(二)_第6张图片

  • 指针类型和值类型作为接受者的区别

值类型:

type People struct {
  Name  string
  Country string
}
func (p People) Print() {
  fmt.Println("name=%s country=%s\n",p.Name, p.Country)
}

func (p People) Set(name string, country string) {
  p.Name = name
  p.Country = country
}
func main() {
  var p1 People = People {
    Name: "people01",
    Country: "china",
  }
  p1.Print()  //  people01 china
  p1.Set("people02","english")
  p1.Print() // 不变 p是实例的拷贝   people01 china
}
 
 
指针类型:
type People struct {
  Name  string
  Country string
}
func (p People) Print() {
  fmt.Println("name=%s country=%s\n",p.Name, p.Country)
}
// 值类型
func (p People) Set(name string, country string) {
  p.Name = name
  p.Country = country
}
// 引用类型
func (p *People) SetV2(name string, country string) {
  p.Name = name
  p.Country = country
 }
func main() {
  var p1 People = People {
    Name: "people01",
    Country: "china",
  }
  p1.Print()  //  people01 china
  p1.Set("people02","english")
  p1.Print() // 不变 p是实例的拷贝   people01 china
  (&p1).SetV2("people02","english")或 语法糖: p1.SetV2("people02","english")
  p1.Print() // 变了 people02 english
}
 
  • 什么时候用值类型/指针类型作为接受者?

a. 需要修改接受者中的值的时候 b. 接受者是⼤对象的时候,副本拷贝代价⽐较大 c. ⼀般来说,通常使⽤指针类型作为接受者

  • 匿名结构体与继承

通过组合 和 匿名字段实现的继承,方法的冲突解决和匿名字段的冲突解决一致,都是通过指定对应的实例的方法解决

type Anaimal struct {
  Name  string
  Sex string
}
func (a *Anaimal) Talk() {
  fmt.Println(a.Name)
}

type Dog struct {
  Feet string
  *Anaimal
}

func (d *Dog) Eat () {
  fmt.Println("dog is eat")
}

func main () {
  var d *Dog = &Dog{
    Feet: "Four Feet",
    Anaimal: &Anaimal {
      Name: "dog",
      Sex: "xiong",
    },
  }
  d.Name = "dog"
  d.Sex = "xiong"
  d.Eat()
  d.Talk()

}


  • 多重继承与冲突解决

type Mother struct {
     Name string
 }
 type Father struct {
     Name string
 }

 type People struct {
    Sex       string
    Age int
    *Mother
    *Father
 }
  • 结构体与json序列列化

package main
import (
   "encoding/json"
   "fmt"
)

type Student struct {
   Id   string
   Name string
   Sex  string
}

type Class struct {
   Name     string
   Count    int
   Students []*Student
}

var rawJson = `
{"Name":"101","Count":0,"Students":[{"Id":"0","Name":"stu0","Sex":"man"},{"Id":"1","Name":"stu1","Sex":"man"},{"Id":"2","Name":"stu2","Sex":"man"},{"Id":"3","Name":"stu3","Sex":"man"},{"Id":"4","Name":"stu4","Sex":"man"},{"Id":"5","Name":"stu5","Sex":"man"},{"Id":"6","Name":"stu6","Sex":"man"},{"Id":"7","Name":"stu7","Sex":"man"},{"Id":"8","Name":"stu8","Sex":"man"},{"Id":"9","Name":"stu9","Sex":"man"}]}
`

func main() {
   c := &Class{
      Name:  "101",
      Count: 0,
   }

   for i := 0; i < 10; i++ {
      stu := &Student{
         Name: fmt.Sprintf("stu%d", i),
         Sex:  "man",
         Id:   fmt.Sprintf("%d", i),
      }
      c.Students = append(c.Students, stu)
   }

   data, err := json.Marshal(c)
   if err != nil {
      fmt.Println("json marshal failed")
      return
   }

   fmt.Printf("json:%s\n", string(data))

   //json反序列化
   fmt.Println("unmarshal result is \n\n")
   var c1 *Class = &Class{}
   err = json.Unmarshal([]byte(rawJson), c1)
   if err != nil {
        fmt.Println("unmarhsal failed")
        return
   }
   fmt.Printf("c1:%#v\n", c1)
   for _, v := range c1.Students {
        fmt.Printf("stu:%#v\n", v)
   }
}

推荐阅读

(点击标题可跳转阅读)

RxJS入门

一文掌握Webpack编译流程

一文深度剖析Axios源码

Javascript条件逻辑设计重构
Promise知识点自测
你不知道的React Diff
你不知道的GIT神操作
程序中代码坏味道(上)

程序中代码坏味道(下)

学习Less,看这篇就够了

一文掌握GO语言实战技能(一)

一文掌握Linux实战技能-系统管理篇

一文掌握Linux实战技能-系统操作篇

一文达到Mysql实战水平

一文达到Mysql实战水平-习题答案

从表单抽象到表单中台

vue源码分析(1)- new Vue

实战LeetCode 系列(一) (题目+解析)

一文掌握Javascript函数式编程重点

实战LeetCode - 前端面试必备二叉树算法

一文读懂 React16.0-16.6 新特性(实践 思考)

阿里、网易、滴滴、今日头条、有赞.....等20家面试真题

30分钟学会 snabbdom 源码,实现精简的 Virtual DOM 库


觉得本文对你有帮助?请分享给更多人

关注「React中文社区」加星标,每天进步

你可能感兴趣的:(一文掌握GO语言实战技能(二))