go time时间偏移_go-1

本文详细介绍了Go语言的基础概念,包括目录结构、包的使用、Go命令、程序结构和语言特性。重点讲解了Go中的时间处理,如时间日期类型和字符串原理,还探讨了数据类型、变量、常量、标识符和关键字。此外,还讨论了Go中的数据类型操作符、字符串、流程控制、函数、变量作用域和可见性。文章以深入浅出的方式阐述了Go语言的基础知识,适合初学者入门学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一篇

一:GO基本概念

Golang⽬录结构划分

  • 个人项目

9b2a2b542cbb43a06b349999ce645ea7.png
  • 公司项目

be9452d18a10a74d961fb13b04fdb60f.png

包的概念

1. 和python⼀样,把相同功能的代码放到⼀个⽬录,称之为包

2. 包可以被其他包引⽤

3. main包是⽤来⽣成可执⾏⽂件,每个程序只有⼀个main包

4. 包的主要⽤途是提⾼代码的可复⽤性

Go 基本命令介绍

a. go run 快速执⾏go⽂件,就像执⾏脚本⼀样

b. go build 编译程序,⽣成⼆进制可执⾏⽂件

c. go install 安装可执⾏⽂件到bin⽬录

d. go test 执⾏单元测试或压⼒测试

e. go env 显示go相关的环境变量

f. go fmt 格式化源代码

Go程序结构

a. go源码按package进⾏组织,并且package要放到⾮注释的第⼀⾏

b. ⼀个可执⾏程序只有⼀个main包和⼀个main函数

c. main函数是程序的执⾏⼊⼝

golang语⾔特性

1. 垃圾回收

a. 内存⾃动回收,再也不需要开发⼈员管理内存

b. 开发⼈员专注业务实现,降低了⼼智负担

c. 只需要new分配内存,不需要释放 golang语⾔特性

2. 天然并发

a. 从语⾔层⾯⽀持并发,⾮常简单。只需要go⼀下

b. goroutine,轻量级线程,创建成千上万个goroute成为可能

func calc() {  //⼤量计算 } 
func main() {  go calc() }

3. channel

a. 管道,类似unix/linux中的pipe

b. 多个goroute之间通过channel进⾏通信

c. ⽀持任何类型

4. 多返回值

a. ⼀个函数返回多个值

5. 编译型语⾔

a. 性能只⽐C语⾔差10%

b. 开发效率和python、php差不多

二:GO 数据类型 变量 常量

go中标识符和关键字

1. 标识符是⽤来表示Go中的变量名或者函数名,以字⺟或_开头。后⾯跟着字⺟ 、_或数字

A. 88ab(错) B. _ab28 C. ab_28

2. 关键字是Go语⾔预先定义好的,有特殊含义的标识符。共25 个

377532ff8d5d8802a1d724ebd85365a1.png

3 变量

语法:var identifier type

var a int
var b string
var c bool
var d int = 8
var e string = “hello”

或者是

var (
 a int //0
 b string //“”
 c bool //false
 d int = 8 // 8
 e string = “hello” //hello
)

4 常量

常量使⽤const 修饰,代表永远是只读的,不能修改。

语法:const identifier [type] = value,其中type可以省略。

举例: const b string = “hello world”
const b = “hello world”
const Pi = 3.1414926
const a = 9/3

优雅的写法:

const(
 a = 1
 b = 2
 c = 3
)

更加专业的写法

const (
 a = iota
 b
 c
)
const(
 a = 1 << iota
 b
 c
)

三 go中的数据类型与操作符

1 布尔类型

2. 整数和浮点数类型

3. 字符串类型

1. 布尔类型

a. var b bool 和 var b bool = true 和 var b = false 
b. 操作符 == 和 !=
c. 取反操作符: !b 数据类型和操作符 
d. && 和 || 操作符 
e. 格式化输出占位符: %t

2. 整数和浮点数类型

e3859c70c3023c0cf6525a9a8ae28025.png

http://www.360doc.com/content/20/0218/08/33093582_892845322.shtml
a. int8、int16、int32、int64
b. uint8、uint16、uint32、uint64
c. int 和 uint,和操作系统平台相关
d. float32 和 float64浮点类型
e. 所有整数 初始化为0,所有浮点数初始化为0.0,布尔类型初始化为false

a. Go是强类型语⾔言,不不同类型相加以及赋值是不不允许的
b. 那怎么样才能实现,不不同类型相加呢?
c. 输出占位符:整数%d,%x⼗十六进制,%f浮点数 3. 字符串类型

a. var str string
b. var str string = “hello world”
c. 字符串串输出占位符 %s
d. 万能输出占位符: %v

%c

字符串的两种表示方式

a. 双引号, “”,可以包含控制字符
b. 反引号, ``,所有字符都是原样输出

字符串串常⽤用操作

a. ⻓长度:len(str)
b. 拼接:+,fmt.Sprintf
c. 分割:strings.Split
d. 包含: strings.Contains
e. 前缀或后缀判断:strings.HasPrefix, strings.HasSuffix 
f. ⼦子串串出现的位置: strings.Index(), strings.LastIndex() 
g. join操作: strings.Join(a[]string, sep string)

操作符

a. 逻辑操作符, == 、 != 、<、<=、>=
b. 算数操作符, +、-、*、/、%

go程序的基本结构

1 任何⼀一个代码⽂文件⾪隶属于⼀一个包

package main 
import “fmt” 
func main() {
  fmt.Println(“hello, world”) 
}

2. import 关键字,引⽤用其他包:

3. 开发可执⾏行行程序,package main, 并且有且只有⼀一个main⼊入⼝口函数

4. 包中函数调⽤用:
a. 同⼀一个包中函数,直接⽤用函数名调 b. 不不同包中函数,通过包名+点+
函数名进⾏行行调⽤用

5. 包访问控制规则:
a. ⼤大写意味着这个函数/变量量是可导出 b. ⼩小写意味着这个函数/变量量是私有的
包外部不不能访问


四: 字符串原理 时间 日期

1.字符串串原理理解析

1. 字符串串底层就是⼀一个byte数组,所以可以和[]byte类型互相转换
2. 字符串串之中的字符是不不能修改的,那怎么修改呢 转成字符切片
3. 字符串串是由byte字节组成,所以字符串串的⻓长度是byte字节的⻓长度
4. rune类型⽤用来表示utf8字符,⼀一个rune字符由1个或多个byte组成

2 时间和日期类型

1. time包
2. time.Time类型,⽤用来表示时间
3. 获取当前时间, now := time.Now()
4. time.Now().Day(),time.Now().Minute(),time.Now().Month(),time.Now().Year()
5. 格式化,fmt.Printf(“%02d/%02d%02d %02d:%02d:%02d”, now.Year()...)
6. 获取当前时间戳,time.Now().Unix()。
7. 时间戳转Time类型。
8. time.Duration⽤用来表示纳秒

bc1bcaf577081470eea4a8ecfb9675e8.png

五: 流程控制

 if condition {
    //do something
 } else if condition {
      //do something
 } else {
    //do something
 }

或者:
if statement; condition {
}
package main
   import (
  "fmt"
  )
func main() {
    
if num:=10; num % 2 == 0 { //checks if number is even
       
fmt.Println(num,"is even")
    } else{
   
    fmt.Println(num,"is odd")
    
}
}

循环

 for initialisation; condition; post {
 }


例子

package main
   import (
  "fmt"
  )
 func main() {
      
for i:=1; i<= 10;i ++ { 
          fmt.Printf(" %d",i)
      }
}
  

break,终⽌止循环

package main

import ( 
   "fmt"
)
func main() {
    
   for i:=1; i<=10;i++ {
     if i > 5 {
        break
     }
     fmt.Printf("%d",i)
   }
  
   fmt.Printf("nline after for loop")
 }   
12345  "nline after for loop"

continue,终⽌止本次循环

package main

import ( "fmt" )
func main() {
  for i :=1;i<=10;i++ {
    if i%2 == 0 {
      continue
    }
    fmt.Printf("%d", i)
  }
}  
13579


练习一: 可以在上面定义i初始值

package main
import (
   "fmt"
)

function main() {
   i :=0 
   for ;i<=10; {
      fmt.Printf("%d", i)
      i += 2
   }
} 
0 2 4 6 8 10

练习二

package main

import ('fmt')
func main() {
  i:=0
  for i <= 10 {
   fmt.Printf('%d ', i)
   i += 2
  }
}
0 2 4 6 8 10

练习三 多个变量

package main

import (fmt')

func main() {
  for no, i:=10,1; i<=10 && no <=19; i, no = i+1,no+1 {
     fmt.Printf("%d * %d = %dn", no, i, no*i)
  }
}

Switch 语句

package main
   import (
    "fmt"
   )

func main() {
  finger:=4
   
  switch finger {
   
    case 1:
   
      fmt.Println("Thumb")
      
    case 2:
   
      fmt.Println("Index")
      
    case 3:
   
      fmt.Println("Middle")
      
    case 4:
   
      fmt.Println("Ring")
      
    case 5:
   
      fmt.Println("Pinky")
  }
}
package main
   import (
  "fmt"
  )
func main() {
    
switch finger
  
case 1:
   
fmt.Println("Thumb")
      
case 2:
   
fmt.Println("Index")
      
case 3:
   
fmt.Println("Middle")
      
case 4:
   
fmt.Println("Ring")
      
case 5:
   
fmt.Println("Pinky")
      
default: //default case
   
fmt.Println("incorrect finger number")
      
}
  

六: 函数介绍

1. 定义:有输⼊入、有输出,⽤用来执⾏行行⼀一个指定任务的代码块。

 func functionname([parametername type]) [returntype] {
         //function body
 }
//其中参数列列表和返回值列列表是可选

例如:
 func add(a int, b int) int {
      Return a + b
 }

⽆无参数和返回值的函数

 func functionname() {
     //function body
 }

如果连续的一系列参数的类型是一样,前面的类型可以不写,例如:

 func add(a, b int) int {
      Return a + b
 }

函数调用

 func add(a, b int) int {
   Return a + b
 }
func main() {
  sum := add(2, 3)

}

多返回值

 func calc(a, b int) (int, int) {
      sum := a + b
      sub := a - b
   return sum, sub
 }
func main() {
   sum, sub := add(2, 3)

}

对返回值进行命名

 func calc(a, b int) (sum int, sub int) {
      sum = a + b
      sub = a - b
      return
 }
func main() {
    sum, sub := add(2, 3)

}

_标识符 这样就会忽略sub的值

 func calc(a, b int) (sum int, sub int) {
      sum = a + b
      sub = a - b
      return
 }
func main() {
    sum, _ := add(2, 3)

}
 

可变参数

func calc_v1(b ...int) (sum int, sub int) {
   sum:=0
   for i :=0; i<len(b);i++ {
     sum = sum =b[i]
   }
   return sum
}
func calc_v2(a int, b ...int) (sum int, sub int) {
return
}
func calc_v3(a int, b int, c ...int) (sum int, sub int) {
return
}

b其实是数组

defer语句

延迟执行语句,在函数返回之后会调用,用来释放一些资源

 func calc_v1(b ...int) (sum int, sub int) {
      defer fmt.Println(“defer”)
      return
 }

func testDefer () {
   defer fmt.PrintIn("hello")
   fmt.PrintIn("aaaa")
   fmt.PrintIn("bbbb")
}
打印顺序是 aaaa bbbb hello

多个defer语句句,遵循栈的特性:先进后出

 func calc_v1(b ...int) (sum int, sub int) {
      defer fmt.Println(“defer1”)
      defer fmt.Println(“defer2”)
      return
 }
打印顺序是 defer2 defer1

内置函数

1. close:主要⽤用来关闭channel
2. len:⽤用来求⻓长度,⽐比如string、array、slice、map、channel
3. new:⽤用来分配内存,主要⽤用来分配值类型,⽐比如int、struct。返回的是指针 4. make:⽤用来分配内存,主要⽤用来分配引⽤用类型,⽐比如chan、map、slice
5. append:⽤用来追加元素到数组、slice中
6. panic和recover:⽤用来做错误处理理

函数变量作用域和可见性

1. 全局变量量,在程序整个⽣生命周期有效。
var a int = 100
2. 局部变量量,分为两种:1)函数内定义,2)语句句块内定义。函数执行后就销毁

 func add(a int, b int) int {

   var sum int = 0
   //sum是局部变量量
   if a > 0 {
      var c int = 100
      //c是局部变量量,仅在if语句句块有效
    }
 }

for d:=100;d>0 {
}
for 循环结束 d也被销毁

3. 可⻅见性,包内任何变量量或函数都是能访问的。包外的话,⾸首字⺟母⼤大写是可导出的 能够被其他包访问或调⽤用。⼩小写表示是私有的,不不能被外部的包访问。

 func add(a int, b int) int {



 }
 //add这个函数只能在包内部调⽤用,是私有的,不不能被外部的包调⽤用

var a int = 100 仅在当前包调用
var A int  = 200 可以在其他包使用

60f1de95e3b057f0fdeb74613b082363.png

75659874874702c382957c250daf4bbb.png

4 匿名函数,即没有名字的函数

f1:= func (a,b int) int {
    return a + b
}
 

func test () {
  var i int =0
  defer fmt.PrintIn("i=%dn",i)
  i =100
  fmt.PrintIn("i=%dn",i)
  return 
}
打印结果 i=100 i=0,当defer执行的时候 i就被赋值了,所以ki为0

改成
func test () {
  var i int =0
  defer func () {
    fmt.PrintIn("i=%dn",i)
  }()
  i =100
  fmt.PrintIn("i=%dn",i)
  return 
}

当前匿名函数执行的时候i就变为100了,所以两次输出的都是100

1. 函数也是⼀一种类型,因此可以定义⼀一个函数类型的变量量

4. 函数作为⼀一个参数

func calc (a,b int, op func(int,int)int) int {
   return op(a,b)
}
sum:=calc(100,200,add)
func add (a,b int) int{
return a+b
}

闭包

闭包:⼀一个函数和与其相关的引⽤用环境组合⽽而成的实体

package main 
import “fmt” 
func main() {
   var f = Adder() 
   fmt.Print(f(1),” - “) 
   fmt.Print(f(20),” - “) 
   fmt.Print(f(300))
}
func Adder() func(int) int { 
  var x int
  return func(d int) int { 
    x += d
    return x 
  }
}
 

闭包实例:

package main
 func add(base int) func(int) int {
   return func(i int) int {
     base += i
     return base
   }
 }
 func main() {
  tmp1 := add(10)
  fmt.Println(tmp(1), tmp(2))
  tmp2 := add(100)
  fmt.Println(tmp2(1), tmp2(2))
}
 

例子2:

func calc(base int) (func(int) int, func(int) int) {
 add := func(i int) int {
   base += i
   return base
 }
 sub := func(i int) int {
   base -= i
   return base
 }
 return add, sub
}

func main() {
  f1, f2 := calc(10)
  fmt.Println(f1(1), f2(2))
  fmt.Println(f1(3), f2(4))
  fmt.Println(f1(5), f2(6))
  fmt.Println(f1(7), f2(8))
}

例子3:

闭包的副作用

package main
 import (
    "fmt"
    "time"
 )
func main() {
  for i:=0; i<5; i++ {
    go func(){
    fmt.Println(i)
  }()
}

time.Sleep(time.Second)
}
只会打印 5, 如果想要 0-4 应该将i传入匿名函数

------_---------------------------------------------------------------------------------

第二篇

数组

数组是同⼀一类型的元素集合。 var a [3]int //定义⼀一个数组

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:%dn”, len(a))
//a、b是不不同类型的数组,不不能赋值

数组的遍历:

 var a [3]int
 a[0] = 10
 a[1] = 20
 a[2] = 30
 for i := 0; i < len(a); i++ {

 }

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

 }

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

}
//a、b是不不同类型的数组,不不能赋值

数组的拷贝和传参

1. 数组是值类型

 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]

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

 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
  }

切片

1. 切⽚片是基于数组类型做的⼀一层封装。它⾮非常灵活,可以⾃自动扩容。

 var a []int
//定义⼀一个int类型的空切⽚片

2. 切⽚片初始化, a[start:end]创建⼀一个包括从start到end-1的切⽚片。

package main
import (
  "fmt"
)
func main() {
    
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:

package main
   import (
  "fmt"
  )
 func main() {
    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]

切片例子2:

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)
} 

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

使⽤用make创建切⽚片

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

切⽚片的⻓度和容量

68bd6e5a33c942123289752020c5a529.png
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))
}  

9. 切⽚片的再切⽚片

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 %dn",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

10.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

11. 空切⽚片

  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)   
}
}
 
 

12. 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]

13 切片传参

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)
} 

切片拷贝

package main

import (
  "fmt"
)
func main() {
    
  veggies :=[]string{"potatoes","tomatoes","brinjal"}
          
  fruits := []string{"oranges","apples"}
        
  copy(veggies, fruits)
      
  fmt.Println(veggies, fruits)
}

切片遍历

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

 }
//和数组遍历是⼀一样的

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

 //初始化⼀一个切⽚片
  s := make([]int, 10, 30)

17. new⽤用于各种类型的内存分配,new返回是⼀一个指针。

----------------------------------------------------------------------------------------

第三篇

变量和内存地址

1. 每个变量量都有内存地址,可以说通过变量量来操作对应⼤大⼩小的内存 。

注意:通过&符号可以获取变量量的地址

package main

import (
  "fmt"
)
func main() {
  var a int32
  a = 100
  fmt.Printf("%dn", a)
  fmt.Printf("%pn", &a)
}

100
0xc00001608c

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

100

0xc00001608c

3. 指针类型的变量量存储的是⼀一个地址,所以⼜又叫指针类型或引⽤用类型

在定义时,前方加*就变为引用类型

var a *int32, a中存储的是一个地址,我们称为引用类型或者指针类型

9605568ea9d5ed8d3020985804c5d9bd.png

4. 指针类型定义,var 变量量名 *类型

package main

import (
  "fmt"
)
func main() {
  b:=255
     
  var a *int = &b
       
  fmt.Printf("Type of a is %Tn", a)
          
  fmt.Println("address of b is", a)
}
Type of a is *int
address of b is 0xc00009a008

5. 指针类型变量量的默认值为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 <nil>
b after initialization is 0xc0000a2008

6. 如果操作指针变量量指向的地址⾥里里⾯面的值呢? 注意:通过* 符号可以获取指针变量量指向的变量量。

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

7. 通过指针修改变量量的值

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

8. 指针变量量传参

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:

package main

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

[90 90 91]

10.切⽚片传参 注意:切⽚片是引⽤用类型!!

package main

import (
  "fmt"
) 
func change (arr []int){
  arr[0] = 90
}
func main() {
  a:=[3]int{89, 90, 91}
  change(a[:])
  fmt.Println(a)
}
[90 90 91]
 
 
 
 

11. make⽤用来分配引⽤用类型的内存,⽐比如 map、slice以及channel ,

new⽤用来分配除引⽤用类型的所有其他类型的内存,⽐比如 int、数组等

值拷⻉和引⽤拷⻉贝

值拷⻉

 package main
   import (
  "fmt"
  )
 func main() {
     // 值拷贝
     var a int = 100
     b := a
    // 引用拷贝8
 }

引⽤拷⻉

package main
import (
  "fmt"
)
 func main() {
     var a int = 100
     var b *int = &a
     var c *int = b
     *c = 200
}

6fe2112b6b7a7fc441b35711cf5daa8e.png

Map 类型

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

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

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

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)  
  }
}

3. 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)
}

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)
}

6. 如何判断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)
}

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] = %dn", key, value)
  }
}

8. 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)
}
 
 
 

9. 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))
}

10.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]

12. 11.默认情况下,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=%dn", 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
 

13. 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:%vn", 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:%vn", 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[]
 
 
 
 


------------------------------------------------------

第四篇

面向对象编程

1 .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”,
}

2 struct初始化的默认值

 var user User
 fmt.Printf(“%#vn”, user)

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

var user *User = &User{}     
fmt.Printf(“%p %#vn”, 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"

6. 结构体的内存布局: 占⽤用⼀一段连续的内存空间

9e2f4facc9f85afd22d8f56f2c212f68.png

7. 结构体没有构造函数, 必要时需要⾃自⼰己实现 匿名字段和嵌套

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

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

9. 结构体嵌套

 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',
    },
  }
}

10.匿名结构体

 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'
}
  

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

12. 匿名结构体的冲突解决

   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)
}
  

使用结构体的字段,如果结构体字段有值,则使用节后体,如果没有则使用匿名结构体的字段

如果两个匿名结构体重都有相同的字段,当访问这个相同字段时候,要指定访问那个匿名结构体的字段

13.字段可⻅见性,⼤大写表示可公开访问,⼩小写表示私有

4fcabb948fbfd9ae7270cd2f2034aafb.png

14. 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
}
 
 
 

b27ce2f694a67ebc2d5e6eafe6d41a5e.png

方法的定义:

1. 和其他语⾔言不不⼀一样,Go的⽅方法采⽤用另外⼀一种⽅方式实现。

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

0dc206639315386f5a48684ca9c19130.png

3. 可以为当前包内定义的任何类型增加⽅方法

0cfd90f7fc0ae8e0d282f8939a118f85.png

4. 函数不不属于任何类型,⽅方法属于特定的类型 5. 指针类型作为接受者

331ca46eb9668de520adacb5b12ff0e7.png

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

值类型:

type People struct {
Name  string
Country string
}
func (p People) Print() {
  fmt.Println("name=%s country=%sn",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=%sn",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
}
 
 
 
 
 
 
 

7. 什什么时候⽤用值类型/指针类型作为接受者? A. 需要修改接受者中的值的时候 B. 接受者是⼤大对象的时候,副本拷⻉贝代价⽐比较⼤大 C. ⼀一般来说,通常使⽤用指针类型作为接受者 8. 匿匿名结构体与继承

通过组合 和 匿名字段实现的继承,

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

 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()

}
 
 
 
 
 

9. 多重继承与冲突解决

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

 type People struct {
    Sex       string
    Age int
    *Mother
    *Father
 }

10. 结构体与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:%sn", string(data))

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


第五篇


接口

接口介绍和定义

1. 接口定义了一个对象的行为规范 A. 只定义规范,不实现 B. 具体的对象需要实现规范的细节

2. Go中接口定义 A. type 接口名字 interface B. 接口里面是一组方法签名的集合

3. Go中接口的实现

A. 一个对象只要包含接口中的方法,那么就实现了这个接口

B. 接口类型的变量可以保存具体类型的实例

type Animal interface { 
  Talk()
  Eat() int
  Run() 
 }

5. 接口类型变量 A. var a Animal B. 那么a能够存储所有实现Animal接口的对象实例

package main

import (
	"fmt"
)

type Animal interface {
	Talk()
	Eat()
	Name() string
}

type Dog struct {
}

func (d Dog) Talk() {
	fmt.Println("汪汪汪")
}

func (d Dog) Eat() {
	fmt.Println("我在吃骨头")
}

func (d Dog) Name() string {
	fmt.Println("我的名字叫旺财")
	return "旺财"
}

type Pig struct {
}

func (d Pig) Talk() {
	fmt.Println("坑坑坑")
}

func (d Pig) Eat() {
	fmt.Println("我在吃草")
}

func (d Pig) Name() string {
	fmt.Println("我的名字叫猪八戒")
	return "猪八戒"
}

func testInterface1() {
	var d Dog
	var a Animal
	a = &d

	a.Eat()
	a.Talk()
	a.Name()

	var pig Pig
	a = &pig
	a.Eat()
	a.Talk()
	a.Name()

	fmt.Printf("%T %vn", a, a)
}
func main() {
	testInterface1()

	//testInterface2()
}

6. 空接口 A. 空接口没有定义任何方法 B. 所以任何类型都实现了空接口 ,空接口可以存储任何类型,就是一个容器

interface { }

package main

import (
	"fmt"
)

func describe(a interface{}) {
	fmt.Printf("%T %vn", a, a)
}

func testInterface1() {
	var a interface{}
	var b int = 100
	a = b

	fmt.Printf("%T %vn", a, a)

	var c string = "hello"
	a = c
	fmt.Printf("%T %vn", a, a)

	var d map[string]int = make(map[string]int, 100)
	d["abc"] = 1000
	d["eke"] = 30

	a = d
	fmt.Printf("%T %vn", a, a)
}

type Student struct {
	Name string
	Sex  int
}

func main() {

	a := 65
	describe(a)

	str := "hello"
	describe(str)

	var stu Student = Student{
		Name: "user01",
		Sex:  1,
	}

	describe(stu)

}

8. 类型断言 A. 如何获取接口类型里面存储的具体的值呢?

使用a.(类型的值),ok用来判断是否正确吗,如果没有ok,程序会挂掉

A. 如何解决,引入 ok判断机制! v, ok := i.(T)

package main

import (
	"fmt"
)

func test(a interface{}) {

	// s := a.(int)
	s, ok := a.(int)
	if ok {
		fmt.Println(s)
		return
	}

	str, ok := a.(string)
	if ok {
		fmt.Println(str)
		return
	}

	f, ok := a.(float32)
	if ok {
		fmt.Println(f)
		return
	}

	fmt.Println("can not define the type of a")
}

func testInterface1() {
	var a int = 100
	test(a)

	var b string = "hello"
	test(b)
}

B. type switch。 转换为2次,解决办法是 v:=a.(type)

func testSwitch(a interface{}) {
	switch a.(type) {
	case string:
		fmt.Printf("a is string, value:%vn", a.(string))
	case int:
		fmt.Printf("a is int, value:%vn", a.(int))
	case int32:
		fmt.Printf("a is int, value:%vn", a.(int))
	default:
		fmt.Println("not support typen")
	}
}

func testSwitch2(a interface{}) {
	switch v := a.(type) {
	case string:
		fmt.Printf("a is string, value:%vn", v)
	case int:
		fmt.Printf("a is int, value:%vn", v)
	case int32:
		fmt.Printf("a is int, value:%vn", v)
	default:
		fmt.Println("not support typen")
	}
}

func testInterface2() {
	var a int = 100
	testSwitch(a)
	var b string = "hello"
	testSwitch(b)
}

func testInterface3() {
	var a int = 100
	testSwitch2(a)
	var b string = "hello"
	testSwitch2(b)
}

func main() {
	//testInterface1()
	//testInterface2()
	

13. 指针接收

package main

import (
	"fmt"
)

type Animal interface {
	Talk()
	Eat()
	Name() string
}

type Dog struct {
}

func (d *Dog) Talk() {
	fmt.Println("汪汪汪")
}

func (d *Dog) Eat() {
	fmt.Println("我在吃骨头")
}

func (d *Dog) Name() string {
	fmt.Println("我的名字叫旺财")
	return "旺财"
}

func main() {
	var a Animal
	var d Dog
	//a存的是一个值类型的Dog,那么调用a.Eat(),&Dog->Eat()
	//如果一个变量存储在接口类型的变量中之后,那么不能获取这个变量的地址
	a = d
	a.Eat()
	// 值类型实现了一个接口,指针类型可以存储进去
	// 指针类型实现了一个接口,值类型存储存储不进去

	fmt.Printf("%T %vn", a, a)
	var d1 *Dog = &Dog{}
	a = d1
	//*(&Dog).Eat() 使用的是指针类型那么定义时候使用
	a.Eat()
	fmt.Printf("*Dog %T %vn", a, a)
}

同一类型实现多接口,Animal 和PuruDongWu 都可以实现d

package main

import (
	"fmt"
)

type Animal interface {
	Talk()
	Eat()
	Name() string
}

type PuruDongWu interface {
	TaiSheng()
}

type Dog struct {
}

func (d Dog) Talk() {
	fmt.Println("汪汪汪")
}

func (d Dog) Eat() {
	fmt.Println("我在吃骨头")
}

func (d Dog) Name() string {
	fmt.Println("我的名字叫旺财")
	return "旺财"
}

func (d Dog) TaiSheng() {
	fmt.Println("狗是胎生的")
}

func main() {
	var d Dog
	var a Animal

	fmt.Println("%v %T %p", a, a, a)

	if a == nil {
		//a.Eat()
		fmt.Println("a is nil")
	}

	a = d
	a.Eat()

	var b PuruDongWu
	b = d
	b.TaiSheng()
}

15. 接口嵌套,和结构体嵌套类似

package main

import "fmt"

type Animal interface {
	Eat()
	Talk()
	Name() string
}

type Describle interface {
	Describle() string
}

type AdvanceAnimal interface {
	Animal
	Describle
}

type Dog struct {
}

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

func (d Dog) Talk() {
	fmt.Println("dog is talking")
}

func (d Dog) Name() string {
	fmt.Println("my name is dog")
	return "dog"
}

func (d Dog) Describle() string {
	fmt.Println("dog is a dog")
	return "dog is a dog"
}

func main() {
	var d Dog
	var a AdvanceAnimal

	a = d
	a.Describle()
	a.Eat()
	a.Talk()
	a.Name()
}

 
 
 
 

反射详解 变量介绍

1. 变量的内在机制 A. 类型信息,这部分是元信息,是预先定义好的

B. 值类型,这部分是程序运行过程中,动态改变的

var arr [10]int 
arr[0] = 10 
arr[1] = 20 
arr[2] = 30 
arr[3] = 40 
arr[4] = 50

type Animal struct { 
  Name string
  age int 
}
var a Animal
 
 
 
 
 
 
 
 
 

2. 反射与空接口 A. 空接口可以存储任何类型的变量 B. 那么给你一个空接口,怎么里面存储的是什么东西? C. 在运行时动态的获取一个变量的类型信息和值信息,就叫反射

3. 怎么分析? A. 内置包 reflect B. 获取类型信息: reflect.TypeOf

C. 获取值信息: reflect.ValueOf

Type.Kind(),获取变量的类型

97a0e1db812a5d5e56899cf435ddf5b4.png

Ptr 指针的值

package main

import (
	"fmt"
	"reflect"
)

func reflect_example(a interface{}) {
	t := reflect.TypeOf(a)
	fmt.Printf("type of a is:%vn", t)

	k := t.Kind()
	switch k {
	case reflect.Int64:
		fmt.Printf("a is int64n")
	case reflect.String:
		fmt.Printf("a is stringn")
	}
}

func reflect_value(a interface{}) {
	v := reflect.ValueOf(a)
	// t := reflect.TypeOf(a)
	k := v.Kind()
	//fmt.Printf("a store value is :%dn", v.Int())
	switch k {
	case reflect.Int64:
		fmt.Printf("a is int64, store value is:%dn", v.Int())
	case reflect.Float64:
		fmt.Printf("a is float64, store value is:%fn", v.Float())
	}
}

func reflect_set_value(a interface{}) {
	v := reflect.ValueOf(a)
	// t := reflect.TypeOf(a)
	k := v.Kind()
	//fmt.Printf("a store value is :%dn", v.Int())
	switch k {
	case reflect.Int64:
		v.SetInt(100)
		fmt.Printf("a is int64, store value is:%dn", v.Int())
	case reflect.Float64:
		v.SetFloat(6.8)
		fmt.Printf("a is float64, store value is:%fn", v.Float())
	case reflect.Ptr:
		fmt.Printf("set a to 6.8n")
		v.Elem().SetFloat(6.8)
	default:
		fmt.Printf("default switchn")
	}
}

func main() {
	var x float64 = 3.4
	reflect_example(x)
	reflect_value(x)
	reflect_set_value(&x)
	fmt.Printf("x value is %vn", x)
	/*
		var b *int = new(int)
		*b = 100
	*/
}

通过反射设置变量的值

2afd33c5b4aae72f1c29fa3dbd78ddf0.png

01dd8f4c21ec5ac553967d50ccafce1c.png

98a633d23d771c5dbaa55afafc922eb7.png

6a9639b72a993bbcf5dc7f22b9b691ce.png

11. 获取结构体类型相关信息

package main
import (
        "fmt"
        "reflect"
)
type S struct {
    A int
    B string
}
func main() {
        s := S{23, "skidoo"}
        v := reflect.ValueOf(s)
        t := v.Type()
        for i := 0; i < v.NumField(); i++ {
            f := v.Field(i)
       fmt.Printf("%d: %s %s = %vn", i,
           t.Field(i).Name, f.Type(), f.Interface())
} }

0: A int = 23
1: B string = skidoo

13. 设置结构体相关字段的值

v.Elem() 获取的是结构体 v.Elem().Field(0) 获取的就是第一个值 A,

v.FieldByName("SetA") 也可以获取对应的值

package main import (
        "fmt"
        "reflect"
)
type S struct {
        A int
        B string
}
func main() {
        s := S{23, "skidoo"}
        v := reflect.ValueOf(&s)
        t := v.Type()
        v.Elem().Field(0).SetInt(100)
        // 等价 v.Elem().FieldByName("A").SetInt(100)
        for i := 0; i < v.Elem().NumField(); i++ {
           f := v.Elem().Field(i)
           fmt.Printf("%d: %s %s = %vn", i,t.Elem().Field(i).Name, f.Type(), f.Interface())
} }

14. 获取结构体的方法信息

package main
import (
        "fmt"
        "reflect"
)
type S struct {
   A int
   B string
}
func (s *S) Test() {
   fmt.Println("this is a test") 
}
   func main() {
        s := S{23, "skidoo"}
        v := reflect.ValueOf(&s)
        t := v.Type() 
        v.Elem().Field(0).SetInt(100) 
        fmt.Println("method num:", v.NumMethod()) 
        for i := 0; i < v.NumMethod(); i++ {
            // t获取结构体方法信息,如果调用就是v.method
            f := t.Method(i)
            fmt.Printf("%d method, name:%v, type:%vn", i, f.Name, f.Type) 
        }
   }
package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Name  string
	Sex   int
	Age   int
	Score float32
	//xxx   int
}

func (s *Student) SetName(name string) {
	s.Name = name
}

func (s *Student) Print() {
	fmt.Printf("通过反射进行调用:%#vn", s)
}

func main() {
	var s Student
	s.SetName("xxx")
	//SetName(&s, "xxx")
	v := reflect.ValueOf(&s)
	t := v.Type()
	//t := reflect.TypeOf(s)

	fmt.Printf("struct student have %d methodsn", t.NumMethod())
	for i := 0; i < t.NumMethod(); i++ {
		method := t.Method(i)
		fmt.Printf("struct %d method, name:%s type:%vn", i, method.Name, method.Type)
	}

	//通过reflect.Value获取对应的方法并调用
	m1 := v.MethodByName("Print")
	var args []reflect.Value
	m1.Call(args)

	m2 := v.MethodByName("SetName")
	var args2 []reflect.Value
	name := "stu01"
	nameVal := reflect.ValueOf(name)
	args2 = append(args2, nameVal)
	m2.Call(args2)

	m1.Call(args)
}

16. 获取结构体中tag信息

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Name  string `json:"name" db:"name"`
	Sex   int
	Age   int
	Score float32
	//xxx   int
}

func (s *Student) SetName(name string) {
	s.Name = name
}

func (s *Student) Print() {
	fmt.Printf("通过反射进行调用:%#vn", s)
}

func main() {
	var s Student
	s.SetName("xxx")
	//SetName(&s, "xxx")
	v := reflect.ValueOf(&s)
	t := v.Type()
	//t := reflect.TypeOf(s)

	field0 := t.Elem().Field(0)
	fmt.Printf("tag json=%sn", field0.Tag.Get("json"))
	fmt.Printf("tag db=%sn", field0.Tag.Get("db"))

	//json.UnMa
	var s string

}

反射总结以及应用场景 17. 反射总结 A.在运行时动态的获取一个变量的类型信息和值信息 18. 应用场景 A. 序列化和反序列化,比如json, protobuf等各种数据协议 B. 各种数据库的ORM, 比如gorm,sqlx等数据库中间件 C. 配置文件解析相关的库, 比如yaml、ini等

723e72f801e12b9594c2de9c5cf80d0d.png

第六篇

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值