if 条件语句

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
if condition1 {
    
} else if condition2 {
    
}else {
    
}

// if 的简短语句
// 同  for 一样, if 语句可以在白条鸡表达式前面执行一个简单的语句
if  v:= x-100; v<0 {
    return v
}

switch语句

1
2
3
4
5
6
7
8
switch var1 {
    case val1: //空分支
    case val2:
    	fallthrough // go的一个关键字,执行 case3 的 f()
	case val3:
    	f()
	default: //默认分支
}

for-range

遍历 数组, 切片,字符串 , map

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
for idx, char := range mystring {
    
}

for key,value := range myMap {
    
}

for idx, value := range myArray {
    
}
//需要注意:如果 for 遍历指针数组,则 value 取出的指针地址为原指针地址的拷贝

打印 os Args

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
	"flag"
	"fmt"
	"os"
)
 
func main() {
	fmt.Println("hello world")
	var name = flag.String("name", "world", "get name your want ")
	flag.Parse()
	fmt.Println("os .args is ", os.Args)
	fmt.Println("name = ", *name)
	// printSlice[int]([]int{1, 2, 3})

}
1
2
# flag 是 go参数的一个包,可以捕捉 你输入的参数, 
go run main.go  --name jesse

变量定义

1
2
3
var i,j int = 1,2
c,python,java = true,false, "no!!"
//go语言自动对你的代码做了判断,很多高级的语法 就是为了减少代码行数

类型转换和推导

【golang 要求显式类型转换,和 java 的不一样,需要程序员手动转换,这样程序员清除自己要做什么】

隐式转换 程序可能运行存在风险,因此 显式转换可以降低运行的错误【但是程序员需要费脑,自己转换】

1
2
3
4
5
6
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
//其他写法===
i := 42
f := float64(i)

数组

数组是一段连续的内存空间

  • 定义方法
    • var identifier [len]type
  • 示例
    • myArray := [3]int{1,2,3}

切片

切片和数组 定义非常类似,但是 没有长度的那种

  • 切片是对数组一个连续片段的引用
  • 数组定义中不指定长度即为切片
    • var identifier []type
    • 切片在未初始化之前 默认为 nil,长度为 0
    • 常用方法
1
2
3
4
5
6
7
8
func main() {
    var arr := [5]{1,2,3,4,5}
    var myslice = arr[1:3]
    fmt.Printf("%+v\n",myslice)
    var fullslice = arr[:]
    fmt.Printf("%+v\n",fullslice)
    
}

数组是固定长度的,但是分离出的切片长度是不固定的

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"fmt"
)

func main() {
	var arr = [1]int{1}
	var arr1 = arr[:]
	arr1 = append(arr1, 1)
	arr1 = append(arr1, 2)

	fmt.Println(arr1)
	fmt.Println(&arr1[0] == &arr[0])
	fmt.Printf("%v, %v", &arr1[0], &arr[0])
	// printSlice[int]([]int{1, 2, 3})
	//新的切片和原来数组 引用的不是同一块空间
    
}

切片删除元素的方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"fmt"
)

func DeleteAt(arr []int, id int) []int {
	return append(arr[:id], arr[id+1:]...)

}

func main() {
	var arr = []int{0, 1, 2, 3}
	arr = DeleteAt(arr, 0)
	fmt.Println(arr)

	// printSlice[int]([]int{1, 2, 3})

}

new 和 make 原理

  • new 返回指针地址
  • make 返回第一个元素, 可以预设内存空间,避免未来 的内存拷贝扩容导致效率低下
1
2
3
4
5
var arr = new([]int)
var arr2 = make([]int, 0)
var arr3 = make([]int,10)
var arr4 = make([]int,10,20)
// 第二个参数是 length,第3个参数是 capacity

切片导致的场景问题

1
2
3
4
a := []int
b :=  []int{1,2,3}
c := a
a = append(b,1)

修改切片的值

1
2
3
4
5
6
7
8
9
for _,value := range mySlice {
    value *= 2
}
//这个是不会修改的,因为 go语言是值拷贝

for index,_ := range mySlice {
    mySlice[index] *= 2
    //这个是可以的,因为是通过下标去访问修改
}

map类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
	"fmt"
)
func main() {
	var mp2 = map[string]func() int{
		"funcA": func() int {
			return 1
		},
	}
	fmt.Println(mp2)
	fun, exits := mp2["a"]
	if exits {
		var ret = fun()
		fmt.Println("result = ", ret)
	} else {
		fmt.Println("没有 a这个函数")
	}

}

接口和结构体

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package main

import "fmt"

type IF interface {
	GetName() string
}

type Human struct {
	Name, LastName string
}

func (c *Human) GetName() string {
	return c.Name + c.LastName
}

func main() {
	var w IF = &Human{"dd", "cc"}

	fmt.Println(w.GetName())

	// printSlice[int]([]int{1, 2, 3})

}

结构体标签

go语言 可以通过反射机制 获取 结构体的配置 信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

import (
	"fmt"
	"reflect"
)

type IF interface {
	GetName() string
}

type Human struct {
	Name string `json:"name"`
}

func main() {

	var w = Human{"aa"}
	var humanKind = reflect.TypeOf(w)
	var name = humanKind.Field(0)
	var tag = name.Tag.Get("json")
	fmt.Println(tag)

	// printSlice[int]([]int{1, 2, 3})

}

反射机制

参考学习视频

  1. oType := reflect.TypeOf(obj)返回被检查对象的类型
  • oType.Kind() kind是对具体系统类型的枚举

  • oType.NumField() 有多少个 Field

  • oType.NumMethod() 有多少个方法

    • oType.Field(i) 获得 第 i 个 Field

      • field.Name 获得 名字
      • field.Type 类型
    • oType.Method(i) 获得 第 i 个 方法

      • method.Name 方法名字
      • methodType := method.Type
        • argNum := method.Type.NumIn() 方法参数个数
        • argType := method.Type.In(0) 第 0 个参数
  • oType.FieldByIndex([]int{0,1}) 通过下标获得 Field

  1. reflect.ValueOf()返回被检查对象的值
    1. fielValue := oValue.Field(i) 第 i 个 value
      1. value.Field(i).interface() 获得 “正射”属性
    2. fv := value.FindByIndex([]int{0,0}).interface() 获得第0个父结构体中的第 0 个属性值
    3. oPtrValue.Elem()
    4. oPtrValue.Elem().CanSet()
    5. oPtrValue.Elem().SetInt(999)
    6. value.SetString("jack")
    7. nameValue := value.FieldByName("Name")
    8. isValid := value.isValid() 判断是不是 零值
    9. kind := value.Kind()
    10. methodValue := oValue.Method(i)
      1. oMethodValue.Call([]reflect.Value{val1,val2})

判断 类型 ,检测类型,用 TypeOf

访问内容,修改 内容的,用 ValueOf 这一套 api

助记口诀:

1
2
3
4
5
6
myMap := make(map[string]string, 10)
myMap["a"] = "b"
t := reflect.TypeOf(myMap)
fmt.Println("type:", t)
v := reflect.ValueOf(myMap)
fmt.Println("value:", v)

方法调用实例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// struct
myStruct := T{A: "a"}
v1 := reflect.ValueOf(myStruct)
for i := 0; i < v1.NumField(); i++ {
	fmt.Printf("Field %d: %v\n", i, v1.Field(i))
}
for i := 0; i < v1.NumMethod(); i++ {
	fmt.Printf("Method %d: %v\n", i, v1.Method(i))
}
// 需要注意 receive 是 struct 还是指针
result := v1.Method(0).Call(nil)
fmt.Println("result:", result)

JSON编解码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
func unmarshal2Struct(humanStr string)Human {
	h := Human{}
	err := json.Unmarshal([]byte(humanStr), &h)
	if err != nil {
		println(err)
	}
	return h
}
func marshal2JsonString(h Human) string {
	h.Age = 30
	updatedBytes, err := json.Marshal(&h)
	if err != nil {
		println(err)
	}
	return string(updatedBytes)
}

json 包使用 map[string]interface{} 和 []interface{} 类型保存任意对象

可通过如下逻辑解析任意 json

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var obj interface{}
err := json.Unmarshal([]byte(humanStr), &obj)
objMap, ok := obj.(map[string]interface{})
for k, v := range objMap {
	switch value := v.(type) {
	case string:
		fmt.Printf("type of %s is string, value is %v\n", k, value)
	case interface{}:
		fmt.Printf("type of %s is interface{}, value is %v\n", k, value)
	default:
		fmt.Printf("type of %s is wrong, value is %v\n", k, value)
}
}

注意事项

• Interface 是可能为 nil 的,所以针对 interface 的使用一定要预 先判空,否则会引起程序 crash(nil panic) • Struct 初始化意味着空间分配,对 struct 的引用不会出现空指针

sql driver.Valuer 和 sql.Sanner

实现 scan 方法 实现获取自定义类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func (j *JsonColumn[T]) Scan(src any) error {
	var bs []byte
	switch val = src.(type) {
	case []byte:
		bs = val
	case *[]byte:
		bs = *val
	case sql.RawBytes:
		bs= val
	case *sql.RawBytes:
		bs= *val
	default:
		return fmt.Errorf("不支持 scan src类型")

	}
	err := json.Unmarshal(bs, &j.Val)
	if err!=nil {
		return err
	}
	j.Valid = true
	return nil
}
//  返回字符串 bytes
func (j JsonColumn[T]) Value() (driver.Value,error) {
	return json.Marshal(j.Val)
}