GoLang Notes

The beginning of GO

KNOW
package main

import (
	"fmt"
	"unsafe"
)

func main() {
	fmt.Println("Hello, World!")
	age := 18
    //var age int=18
	fmt.Println("age=", age)
	fmt.Println(unsafe.Sizeof(age))
}
IF ELSE
package main

import (
	"fmt"
	"math/rand"
)

func main() {
	var count = rand.Int()
	fmt.Println(count)
	if count < 30 {
		fmt.Println("Too small")
	} else if count == 30 {
		fmt.Println("Very good")
	} else {
		fmt.Println("Too big")
	}
}

Switch
package main

import "fmt"

func main() {
	var score int = 60
	switch score {
	case 100:
		fmt.Println("Very Good")
	case 90:
		fmt.Println("Good")
	case 80, 70, 60:
		fmt.Println("Nice")
	default:
		fmt.Println("a little bad")
	}
}

//fallthrough 使用方法与break等类似 穿透处理 即执行下一层的代码
For
package main

import "fmt"

func main() {
	sum := 0
	for i := 1; i <= 5; i++ {
		sum += i
	}
	fmt.Println(sum)
}

FUNC
package main

import "fmt"

func add(num1 int, num2 int) int {
	return num1 + num2
}
//函数名首字母大写,所有包都可以用
func main() {
	num1 := 10
	num2 := 20
	sum := add(num1, num2)
	fmt.Println(sum)
}
/*
package main

import "fmt"

func add(num1 int, num2 int) (int, int) {
	return num1 + num2, num2 - num1
}

func main() {
	num1 := 10
	num2 := 20
	sum, _ := add(num1, num2)
	fmt.Println(sum)
}
*/

接收多个参数

package main

import "fmt"
//可变参数放在形参列表的最后
func test(args ...int) {
	for i := 0; i < len(args); i++ {
		fmt.Println(args[i])
	}
}

func main() {
	test(1, 2, 3, 4, 5)
}

反引号 ``

以字符串原生形式输出,包括换行和特殊字符

基本数据类型转字符串
package main

import "fmt"

func main() {
	a := 100
	str := fmt.Sprintf("%d", a)
	fmt.Printf("str Type %T\nstr=%v", str, str)
    
    
    var num int64 = 7
	str := strconv.FormatInt(num, 2)    // strconv.Itoa
	fmt.Printf("str=%v %T", str, str)   
}

%d......
package main

import "fmt"

func main() {
	a := 100
	str := fmt.Sprintf("%d", a)
	fmt.Printf("str Type %T\nstr=%v", str, str)
	/*	%v 默认格式
		%+v  输出结构体时会添加字段名
		%#v 值的go语法表示
		%T  值的类型的GO语法表示
		%%  百分号
		%t	bool值
		%b	二进制
		%c	unicode码值
		%d	十进制
		%o	八进制
		%x  16进制 使用a-f
		%X	16进制 使用A-F
		%U 	Unicode格式
	*/
}

string转数据类型
package main

import (
	"fmt"
	"strconv"
)

func main() {
	var str = "true"
	//函数第二个参数是err error
	b, _ := strconv.ParseBool(str)
	fmt.Printf("%T\n", b)

	var str2 = "114514"
	var n1 int64
	n1, _ = strconv.ParseInt(str2, 10, 0)
	fmt.Println(n1)

	var str3 = "123.456"
	var f1 float64
	f1, _ = strconv.ParseFloat(str3, 64)
	fmt.Println(f1)
}
指针
package main

import "fmt"

func main() {
	a := 10
	var p = &a
	fmt.Println(*p)
	fmt.Printf("a %v , p %v\n", &a, p)
	fmt.Printf("%v", &p)
}
值类型和引用类型

值类型:int float bool string struct 和数组 (变量直接存储值,内存通常在栈中分配

引用类型:指针 slice切片 map 管道chan interface (变量存储一个地址,内存通常在堆上分配

接收用户输入
package main

import "fmt"

func main() {
	var name string
	var age byte
	var salary float64
	var isPass bool
	/*
		fmt.Println("请输入姓名 ")
		fmt.Scanln(&name)

		fmt.Println("请输入年龄 ")
		fmt.Scanln(&age)

		fmt.Println("请输入薪水 ")
		fmt.Scanln(&salary)

		fmt.Println("请输入是否通过考试 ")
		fmt.Scanln(&isPass)

		fmt.Printf("姓名:%v\n年龄:%v\n薪水:%v\n是否通过考试:%v\n", name, age, salary, isPass)
	*/
	fmt.Println("请输入你的姓名,年龄,薪水,是否通过考试,使用空格隔开")
	_, err := fmt.Scanf("%s %d %f %t", &name, &age, &salary, &isPass)
	if err != nil {
		return
	}
	fmt.Printf("姓名:%v\n年龄:%v\n薪水:%v\n是否通过考试:%v\n", name, age, salary, isPass)
}
负数补码

符号位不变 取反+1

空心金字塔
package main

import "fmt"

func main() {
	var n int
	fmt.Scanln(&n)
	for i := 0; i < n; i++ {
		for j := 0; j <= 2*n-1; j++ {
			if j == n-i || j == n+i || (i == n-1 && j >= n-i && j <= n+i) {
				fmt.Print("*")
			} else {
				fmt.Print(" ")
			}
		}
		fmt.Print("\n")
	}
}
goto
package main

import (
	"fmt"
)

func main() {
	fmt.Println("1")
	goto label1
	fmt.Println("2")
	fmt.Println("3")
label1:
	fmt.Println("4")
	fmt.Println("5")
}

导包

go mod(不同目录下

module main
go 1.20

require (
	"ob1" v0.0.0
)
replace (
 	"ob1" => "../ob1"
)

init

初始化 在main前面先执行

执行顺序:全局变量定义->init->main

package main

import "fmt"

func init() {
	fmt.Println("init ...")
}

func main() {
	fmt.Println("main ...")
}
/*
init ...
main ...
*/

匿名函数

定义时直接调用(只能调用一次)
package main

import "fmt"

func main() {
	res := func(n1 int, n2 int) int {
		return n1 + n2
	}(10, 10)
	fmt.Println(res)
}

匿名函数赋给变量
package main

import "fmt"

func main() {
	a := func(n1 int, n2 int) int {
		return n1 + n2
	}
	res := a(10, 10)
	fmt.Println(res)
}

全局匿名函数
package main

import "fmt"

var (
	Fun1 = func(n1 int, n2 int) int {
		return n1 + n2
	}
)

func main() {
	res := Fun1(10, 10)
	fmt.Println(res)
}

闭包

闭包就是一个函数和与其相关的引用环境组合的一个整体(实体)

package main

import (
	"fmt"
)

func AddUpper() func(int) int {
	var n = 10
	return func(i int) int {
		n = n + i
		return n
	}
}

//返回的是一个匿名函数,但是这个匿名函数引用到函数外的n,因此这个匿名函数就和n形成一个整体,构成闭包  还有就是n被视为常驻内存,但并不是全局变量

func main() {
	f := AddUpper()
	fmt.Println(f(1)) //11
	fmt.Println(f(2)) // 13
	fmt.Println(f(3)) //16
}

package main

import (
	"fmt"
	"strings"
)

func makeSuffix(suffix string) func(string) string {

	return func(name string) string {
		if !strings.HasSuffix(name, suffix) {
			return name + suffix
		}
		return name
	}
}

// suffix和匿名函数构成一个闭包
func main() {
	f := makeSuffix(".jpg")
	fmt.Println("文件名处理后", f("winter"))
	fmt.Println("文件名处理后", f("bird.jpg"))
}

defer(延时机制

why?

函数执行完毕后,及时的释放资源。

案例
package main

import "fmt"

func sum(n1 int, n2 int) int {
	defer fmt.Println("ok1 n1=", n1)
	defer fmt.Println("ok2,n2", n2)
	res := n1 + n2
	fmt.Println("ok3 res=", res)
	return res
}

//执行到defer时,会将defer后面的语句(此时的变量值也会)压入到独立的栈中(defer栈
//函数执行完毕后从defer栈中,按照先入后出的方式执行(栈顶进出)
func main() {
	res := sum(1, 2)
	fmt.Println("res=", res)
}

/*
ok3 res= 3
ok2,n2 2
ok1 n1= 1
res= 3
*/

函数变量传递方式

值传递 &&引用传递

传递给函数的都是变量的副本,不同的是 引用传递是地址拷贝,效率高,因为数据量小

len()

str := "hello丁"
fmt.Printf("len str = %v\n", len(str)) //len str = 8

处理字符串遍历时中文问题

str2 := "hello丁"
str2Run := []rune(str2) //转为切片处理中文问题,以便遍历不出现乱码
for i := 0; i < len(str2Run); i++ {
    fmt.Printf("字符=%c\n", str2Run[i])
}

strconv

Atoi
n, err := strconv.Atoi("123")
if err != nil {
    fmt.Println("Atoi_error")
} else {
    fmt.Println("转成的结果是", n)
}
Itoa
str := strconv.Itoa(12345)
fmt.Printf("str=%v type=%T", str, str)
10进制转 2,8,16进制
//10进制转 2,8,16进制 返回相应的字符串
str := strconv.FormatInt(123, 2)
fmt.Println("123对应的二进制", str)

[]byte切片

bytes := []byte("hello golang")
fmt.Println("bytes=", bytes)
//bytes= [104 101 108 108 111 32 103 111 108 97 110 103]

[]byte切片转string

str := string([]byte{104, 101, 108, 108, 111, 32, 103, 111, 108, 97, 110, 103})
fmt.Println(str)
//hello golang

strings

查找子串是否在指定的字符串中 Contains
a := strings.Contains("seafood", "foo")
b := strings.Contains("seafood", "f1")
fmt.Println("a=", a)
fmt.Println("b=", b)
//a= true
//b= false
子串在指定字符串中的个数 Count
num := strings.Count("dyyyyyyyyt", "y")
fmt.Println("num=", num) //8
字符串不区分大小写比较 EqualFold
//==区分字符串大小写
b := strings.EqualFold("abc", "AbC")
fmt.Println("b=", b)//b= true
返回子串在字符串第一次出现的index值 Index
//返回子串在字符串第一次出现的index值  如果没有就返回-1
index := strings.Index("NUTR_Hard", "TR")
fmt.Println("index =", index) //index = 2
返回子串在字符串最后一次出现的index值 LastIndex
//返回子串在字符串最后一次出现的index值  如果没有就返回-1
index := strings.LastIndex("NUTR_Hard_NUTR", "TR")
fmt.Println("index =", index) //index = 12
将指定的子串替换成另外一个子串 Replace
//将指定的子串替换成另外一个子串
//n可以指定你希望替换几个 n=-1则表示全部替换
str := strings.Replace("go hello", "go", "go语言", -1)
fmt.Println("str =", str)//str = go语言 hello
按照某个字符分割字符串 Split
//按照某个字符分割字符串
strArr := strings.Split("golang_learning_GO", "_")
fmt.Println(strArr)//[golang learning GO]
字符串大小写转换
//将字符串的字母进行大小写的转换
str := "goLang Hello"
str = strings.ToLower(str)
fmt.Println(str) //golang hello
str = strings.ToUpper(str)
fmt.Println(str)
//golang hello
//GOLANG HELLO
删除字符串两边的空格 TrimSpace
//删除字符串两边的空格
str := strings.TrimSpace("  GOlang  ")
fmt.Println(str)
删除字符串两边的指定字符 Trim
//删除字符串两边的指定字符
str := strings.Trim("!GoLang!", "!")
fmt.Println(str)
TrimLeft(改左边) TrimRight(改右边)

用法同上

判断字符串是否以指定字符开头 HasPrefix
//判断字符串是否以指定字符开头
a := strings.HasPrefix("GoLang", "Go")
fmt.Println(a)  //True
判断字符串是否以指定字符结尾 HasSuffix
//判断字符串是否以指定字符结尾
a := strings.HasSuffix("GoLang", "Lang")
fmt.Println(a) //True

Time

time.Now
t := time.Now()
fmt.Println(t)   //2023-08-20 19:12:54.8102033 +0800 CST m=+0.001799701
fmt.Printf("%T", t) //time.Time
年月日时分秒
t := time.Now()
fmt.Printf("年=%v\n", t.Year())
fmt.Printf("月=%v\n", int(t.Month()))
fmt.Printf("日=%v\n", t.Day())
fmt.Printf("时=%v\n", t.Hour())
fmt.Printf("分=%v\n", t.Minute())
fmt.Printf("秒=%v\n", t.Second())
格式化输出
t := time.Now()
fmt.Printf("当前年月日 %d-%d-%d %d:%d:%d\n", t.Year(), t.Month(), t.Day(), t.Hour(),t.Minute(), t.Second())
//当前年月日 2023-8-20 19:25:25
t := time.Now()
date := fmt.Sprintf("当前年月日 %d-%d-%d %d:%d:%d\n", t.Year(), t.Month(), t.Day(), t.Hour(),t.Minute(), t.Second())
fmt.Println(date)
t := time.Now()
fmt.Printf(t.Format("2006/01/02 15:04:05"))
时间常量

Sleep
package main

import (
	"fmt"
	"time"
)

func main() {
	i := 0
	for {
		i++
		fmt.Println(i)
		time.Sleep(time.Second)
		if i == 10 {
			break
		}
	}
}
Unix时间戳和UnixNano

Unix: 从 January 1,1970 UTC到时间点所经过的时间(单位s)

UnixNano: 从 January 1,1970 UTC到时间点所经过的时间(单位纳秒)

package main

import (
	"fmt"
	"time"
)

func main() {
	t := time.Now()
	fmt.Println(t.Unix())
	fmt.Println(t.UnixNano()) 
}
//1692532567
//1692532567631470700

内置函数

简介
  1. len 用来求长度,比如string array slice map channel
  2. new 用来分配内存,主要用来分配值类型,比如int float32 struct 返回的时指针
  3. make 用来分配内存 主要用来分配引用类型,比如chan map slice
new
package main

import "fmt"

func main() {
	num := new(int)
	a := 1
	num = &a
	fmt.Printf("a的地址=%v\n", &a)
	fmt.Printf("num的类型%T , num的值=%v , num的地址=%v",
		num, num, &num)
}
//a的地址=0xc00001a0d0
//num的类型*int , num的值=0xc00001a0d0 , num的地址=0xc00000a028
make

var 切片名 []type = make([],len,[cap])

package main

import "fmt"

func main() {
	var slice = make([]int, 4)
	fmt.Println(slice)
	slice[0] = 1
	fmt.Println(slice)
}

//[0 0 0 0]
//[1 0 0 0]

错误处理

简介
  1. 默认情况下,当发生错误后(panic),程序就会退出(崩溃)
  2. 我们希望,当发生错误后,可以捕获到错误,并进行处理,保证程序可以继续执行.
示例
package main

import "fmt"

func test() {
	defer func() {
		err := recover()
		if err != nil {
			fmt.Println("err=", err)
		}
	}()
	num1 := 10
	num2 := 0
	res := num1 / num2
	fmt.Println(res)

}

func main() {
	test()
	fmt.Println("True")
}
//err= runtime error: integer divide by zero
//True
自定义错误
package main

import (
	"errors"
	"fmt"
)

func readConf(name string) (err error) {
	if name == "config.ihi" {
		return nil
	} else {
		return errors.New("读取文件错误")
	}
}

func test() {
	err := readConf("1")
	if err != nil {
        //读取错误 并输出这个错误,然后终止程序
		panic(err)
	}
	fmt.Println("test()继续执行")
}

func main() {
	test()
	fmt.Println("main()继续执行")
}

//panic: 读取文件错误
//goroutine 1 [running]:
//main.test()
//        E:/GOlanguage/main/main.go:19 +0x49
//main.main()
//        E:/GOlanguage/main/main.go:25 +0x19

数组

案例
package main

import "fmt"

func main() {
	var hens [6]float64
	hens[0] = 1.0
	hens[1] = 2.0
	hens[2] = 3.0
	hens[3] = 4.0
	hens[4] = 5.0
	hens[5] = 5.0
	sum := 0.0
	for i := 0; i < len(hens); i++ {
		sum += hens[i]
	}
	fmt.Println(sum)
}
初始化数组
var Array01 [3]int = [3]int{1, 2, 3}
var Array02 = [3]int{1, 2, 3}
var Array03 = [...]int{1, 2, 3}
var Array04 = [3]string{0: "d", 1: "y", 2: "t"}
for_range
package main

import "fmt"

func main() {
	var heroes = [3]string{"d", "y", "t"}
	for i, v := range heroes {
		fmt.Printf("i=%v , v=%v\n", i, v)
	}
}
数组倒序
package main

import (
	"fmt"
	"math/rand"
)

func main() {
	var num [5]int
	for i := 0; i < 5; i++ {
		num[i] = rand.Intn(100) //[0,100]
	}
	fmt.Println(num)
	for i := 0; i < len(num)/2; i++ {
		num[i], num[len(num)-1-i] = num[len(num)-1-i], num[i]
	}
	fmt.Println(num)
}

slice

简介
  1. 切片是数组的一个引用,因此切片是引用类型,在进行传递时,遵守引用传递的机制
  2. 切片的使用与数组累死,遍历切片,访问切片的元素和求切片长度都一样
  3. 切片长度时可以变化的,因此切片是一个动态变化数组
  4. var a []int
案例
代码
package main

import "fmt"

func main() {
	var intArr = [...]int{1, 22, 33, 66, 99}
	slice := intArr[1:3] //[1,3)
	fmt.Println(slice)
	fmt.Printf("%T\n", slice)
	fmt.Printf("%v", cap(slice)) //切片的容量是可以动态变化
}

//[22 33]
//[]int
//4
内存示意图

image

可以看成一个结构体 有三个变量 ptr len cap

切片遍历
package main

import "fmt"

func main() {
	arr := [...]int{10, 20, 30, 40, 50}
	slice := arr[1:4]
	for i, v := range slice {
		fmt.Println(slice[i] == v)
	}
}
append
  1. 本质就是对数组扩容
  2. go底层会创建一个新的数组newArr
  3. 将slice原来包含的元素拷贝到新的数组newArr
  4. slice重新引用到newArr
  5. newArr是在底层维护的,程序员不可见
package main

import "fmt"

func main() {
	slice := []int{1, 2, 3, 4}
	slice = append(slice, 5)
	fmt.Println(slice)
	slice = append(slice, slice...)
	fmt.Println(slice)
}
//[1 2 3 4 5]
//[1 2 3 4 5 1 2 3 4 5]
copy
  1. 参数数据类型是切片
  2. 下面代码a和slice空间独立,互不影响,copy是值传递
package main

import "fmt"

func main() {
	var a = []int{1, 2, 3, 4, 5}

	var slice = make([]int, 10)
	fmt.Println(slice)

	copy(slice, a)
	fmt.Println(slice)
}
//[0 0 0 0 0 0 0 0 0 0]
//[1 2 3 4 5 0 0 0 0 0]
string and slice
  1. string底层是一个byte数组,因此string也可以进行切片处理
  2. string类似于切片,包含ptr与len
  3. string本身不可变
  4. 如果需要修改 可以将string转成[]byte / []rune
package main

import "fmt"

func main() {
	str := "hello@atguigu"
	slice := str[6:]
	fmt.Println(slice)
}
修改字符串
package main

import "fmt"

func main() {
	str := "hello@atguigu" //[]byte是按照字节处理 而中文三个字节
	arr := []byte(str)  //如果有中文的话 转成[]rune便可以 []rune是按照字符处理
	arr[0] = 'z'
	str = string(arr)
	fmt.Println(str)
}

冒泡排序

package main

import "fmt"

func BubbleSort(arr *[5]int) {

	for i := 0; i < len(arr)-1; i++ {
		for j := i + 1; j < len(arr)-i; j++ {
			if arr[j-1] > arr[j] {
				arr[j], arr[j-1] = arr[j-1], arr[j]
			}
		}
	}

}

func main() {

	arr := [...]int{24, 69, 80, 57, 13}
	BubbleSort(&arr)
	fmt.Println(arr)
}

二分法

for循环
func BinaryFind(arr *[6]int, findVal int) {
	left := 0
	right := len(arr) - 1
	for left <= right {
		mid := (left + right) >> 1
		if arr[mid] > findVal {
			right = mid - 1
		} else if arr[mid] < findVal {
			left = mid + 1
		} else if arr[mid] == findVal {
			fmt.Println(mid)
			return
		}
	}
	fmt.Println("Not Found")
	return
}
递归
func BinaryFind(arr *[6]int, leftIndex int, rightIndex int, findVal int) {
	if leftIndex > rightIndex {
		fmt.Println("Not Found")
		return
	}
	mid := (leftIndex + rightIndex) / 2

	if arr[mid] > findVal {
		BinaryFind(arr, leftIndex, mid-1, findVal)
	} else if arr[mid] < findVal {
		BinaryFind(arr, mid+1, rightIndex, findVal)
	} else if arr[mid] == findVal {
		fmt.Println(mid)
	}
}

map

简介
  1. var 变量名 map[key_type]value_type
  2. 如果定义的key与map里已存在的相同,则会对value进行覆盖
  3. 需要用make分配内存
案例
package main

import (
	"fmt"
)

func main() {
	var a map[string]string
	a = make(map[string]string, 10)
	a["1"] = "d"
	a["2"] = "y"
	a["3"] = "t"
	fmt.Println(a)
}
//map[1:d 2:y 3:t]
初始化
package main

import "fmt"

func main() {
	//第一种
	var a map[string]string
	a = make(map[string]string, 10)
	//第二种
	cities := make(map[string]string)
	cities["No1"] = "北京"
	cities["No2"] = "Tianjing"
	fmt.Println(a)
	fmt.Println(cities)
	//第三种
	heroes := map[string]string{
		"hero":  "d",
		"hero2": "yt",
	}
	fmt.Println(heroes)
}
delete

delete(map,"key")

key不存在的话也不会报错

package main

import "fmt"

func main() {
	cities := make(map[string]string)
	cities["No1"] = "北京"
	cities["No2"] = "天津"
	delete(cities, "No2")
	fmt.Println(cities)
}
//map[No1:北京]

如果想全部删除

一一遍历或者直接make一个新空间

map查找
package main

import "fmt"

func main() {
	cities := make(map[string]string)
	cities["No1"] = "北京"
	cities["No2"] = "天津"
	val, found := cities["No1"]
	if found {
		fmt.Println(val)
	} else {
		fmt.Println("False")
	}
}
map遍历
package main

import "fmt"

func main() {
	cities := make(map[string]string)
	cities["No1"] = "北京"
	cities["No2"] = "天津"
	for key, value := range cities {
		fmt.Printf("%v = %v\n", key, value)
	}
}
map 切片
package main

import "fmt"

func main() {
	var monsters []map[string]string
	monsters = make([]map[string]string, 2)
	if monsters[0] == nil {
		monsters[0] = make(map[string]string, 2)
		monsters[0]["name"] = "牛魔王"
		monsters[0]["age"] = "500"
	}

	if monsters[1] == nil {
		monsters[1] = make(map[string]string, 2)
		monsters[1]["name"] = "玉兔精"
		monsters[1]["age"] = "400"
	}

	//append函数,动态增加
	monster := map[string]string{
		"name": "新的妖怪",
		"age":  "200",
	}
	monsters = append(monsters, monster)
	fmt.Println(monsters)

}
细节
  1. map是引用类型
  2. map make完以后,想要再增加元素会自动扩容,并不会发生panic
  3. map的value也经常使用struct类型
package main

import "fmt"

type Stu struct {
	Name    string
	Age     int
	Address string
}

func main() {
	students := make(map[string]Stu, 10)
	stu1 := Stu{Name: "Tom", Age: 18, Address: "北京"}
	stu2 := Stu{Name: "Xiao_Hong", Age: 18, Address: "北京"}
	students["No1"] = stu1
	students["No2"] = stu2
	fmt.Println(students)
	for k, v := range students {
		fmt.Println(k, v.Name, v.Age, v.Address)
	}
}

Struct

字段

字段是结构体的一部分,一般是基本数据类型,也可以是引用类型。

type Cat struct {
	Name  string  //属性/字段/filed
	Age   int
	Color string
}
案例
package main

import "fmt"

type Person struct {
	Name   string //属性/字段/filed
	Age    int
	Scores [5]float64
	ptr    *int
	slice  []int
	map1   map[string]string
}

func main() {
	per := Person{}
	per.slice = make([]int, 1)
	per.slice[0] = 100
	fmt.Println(per)
}
内存布局

image

细节
  1. 结构体之间的拷贝是值拷贝

  2. p := new(Person) //p是一个指针 (*p).Name || p.Name

  3. var p *Person = &Person{}

  4. 结构体字段在内存中连续分布

  5. package main
    
    import "fmt"
    
    type a struct {
    	Num int
    }
    
    type b struct {
    	Num int
    }
    
    func main() {
    	var A = a{1}
    	var B b
    	B = b(A)  //要求字段完全一样 (name type)
    	fmt.Println(A)
    	fmt.Println(B)
    }
    
    //{1}
    //{1}
    
    
反射
package main

import (
	"encoding/json"
	"fmt"
)

type Monster struct {
	Name  string `json:"name"`
	Age   int    `json:"age"`
	Skill string `json:"skill"`
}

func main() {
	monster := Monster{"牛魔王", 500, "芭蕉扇"}
	jsonMonster, err := json.Marshal(monster)
	if err != nil {
		fmt.Println("Json处理错误")
	}
	fmt.Println(string(jsonMonster))
}
//{"name":"牛魔王","age":500,"skill":"芭蕉扇"}

方法

简介
  1. 在某些情况下,我们需要声明(定义)方法.比如结构体除了需要一些字段外有时候还要有一些行为,这时需要方法才能完成

  2. GOlang中的方法是作用在指定的数据类型上的(和指定数据类型绑定),因此自定义类型,都可以由方法,而不仅仅是结构体

  3. func(recevier type) methodName(参数列表) (返回值列表){

    方法体

    return 返回值

    }

案例
  1. test方法和A类型绑定
  2. test方法只能通过A类型的变量来调用,不能使用其他类型变量来调用
package main

import "fmt"

type A struct {
	Name string
}

func (a A) test() {
	fmt.Println(a.Name)
}

func main() {
	var p = A{Name: "雨"}
	p.test()
}
package main

import "fmt"

type A struct {
	Name string
}

func (a A) test(n int) {
	sum := 0
	for i := 1; i <= n; i++ {
		sum += i
	}
	fmt.Println(sum)
}

func main() {
	var p = A{Name: "雨"}
	p.test(5)
}
String()方法

可以自定义fmt.Println()输出

package main

import "fmt"

type Student struct {
	Name string
	Age  int
}

func (stu *Student) String() string {
	str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age)
	return str
}

func main() {
	stu := Student{Name: "Tom", Age: 20}
	fmt.Println(&stu)
	fmt.Println(stu)
}

//Name=[Tom] Age=[20]
//{Tom 20}

方法和函数的区别

  1. 调用方式不一样

    ​ 函数的调用方式 函数名(实参列表)

    ​ 方法的调用方式 变量名.方法名(实参列表)

  2. 对于普通函数,接收者为值类型,不能将指针类型的数据直接传递,反之亦然

  3. 对于方法(如struct的方法),接收者为值类型时,可以直接用指针类型的变量调用方法(但本质依旧是值拷贝),反过来同样也可以

工厂模式

结构体开头小写,并且其他包想调用

package ob1

type student struct {
	Name  string
	score float64
}

func NewStudent(n string, s float64) *student {
	return &student{
		Name:  n,
		score: s,
	}
}

func (s *student) Getscore() float64 {
	return s.score
}

继承

package main

type Goods struct{
	Name string
	Price int
}

type Book struct{
	Goods
	Writer string
}
package main

import "fmt"

type Student struct {
	Name  string
	Age   int
	Score int
}

func (stu *Student) ShowInfo() {
	fmt.Printf("学生名=%v 年龄=%v 成绩=%v\n", stu.Name, stu.Age, stu.Score)
}
func (stu *Student) Setscore(score int) {
	stu.Score = score
}

type Pupil struct {
	Student
}

func (p *Pupil) testing() {
	fmt.Println("小学生正在考试中")
}

func main() {
	pupil := &Pupil{}
	pupil.Name = "tom~"
	pupil.Student.Age = 8
	pupil.testing()
	pupil.Student.Setscore(70)
	pupil.Student.ShowInfo()
}

接口

基本介绍

​ GoLang中多态特性主要通过接口来体现的

​ 接口是一种抽象类型,用于定义某个对象的行为规范。接口定义了一组方法的签名,任何实现了这些方法的类型都能被称之为该接口的实现类型。接口包括两部分:静态类型和动态类型,其中静态类型是指接口的类型定义,而动态类型是指接口的具体实现类型。

​ GoLang中的接口与其他编程语言相比有很多优点,例如:

  1. 实现简单:GoLang中的接口是非常简单的,只需定义一组方法签名即可。
  2. 调用方便:因为接口是一种抽象类型,所以可以轻松实现多态调用。
  3. 扩展性强:接口可以被任何类型实现,因此具有良好的扩展性。
快速入门
package main

import "fmt"

// 声明一个接口
type Usb interface {
	//声明两个没有实现的方法
	Start()
	Stop()
}

type Phone struct {
}

// 让Phone实现Usb接口的方法
func (p Phone) Start() {
	fmt.Println("手机开始工作")
}

func (p Phone) Stop() {
	fmt.Println("手机结束工作")
}

type Computer struct {
}

func (c Computer) Working(usb Usb) {
	usb.Start()
	usb.Stop()
}

func main() {
	computer := Computer{}
	phone := Phone{}
	computer.Working(phone)
}

注意事项
  1. 接口本身不能创建实例,但可以指向一个·实现了该接口的自定义类型的变量(实例)

    package main
    
    import "fmt"
    
    type AInterface interface {
    	Say()
    }
    
    type Stu struct {
    	Name string
    }
    
    func (stu Stu) Say() {
    	fmt.Println("Stu Say()")
    }
    
    func main() {
    	var stu Stu
    	var a AInterface = stu
    	a.Say()
    }
    
    
  2. 接口中所有的方法都没有方法体,即都是没有实现的方法

  3. 在GOlang中,一个自定义类型需要将某个接口的所有方法都实现,我们说这个自定义类型实现了该接口。

  4. 一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型

  5. 只要是自定义数据类型就可以实现接口,不仅是结构体类型

  6. 一个自定义类型可以实现多个接口

    package main
    
    import "fmt"
    
    type AInterface interface {
    	Say()
    }
    type BInterface interface {
    	Hello()
    }
    type Monster struct {
    }
    
    func (m Monster) Hello() {
    	fmt.Println("Monster Hello()")
    }
    
    func (m Monster) Say() {
    	fmt.Println("Monster Say()")
    }
    
    func main() {
    	var monster Monster
    	var a AInterface = monster
    	var b BInterface = monster
    	a.Say()
    	b.Hello()
    }
    
    
  7. GOlang中接口不能有任何变量

  8. 一个接口可以继承多个别的接口。这时如果要实现A接口,也必须将B,C接口的方法也全部实现

    package main
    
    import "fmt"
    
    type Binterface interface {
    	test01()
    }
    
    type Cinterface interface {
    	test02()
    }
    
    type Ainterface interface {
    	Binterface
    	Cinterface
    	test03()
    }
    
    // 如果我们需要实现Ainterface,就需要将Binterface Cinterface的方法都实现
    type Stu struct {
    }
    
    func (stu Stu) test01() {
    	fmt.Println("test01")
    }
    
    func (stu Stu) test02() {
    	fmt.Println("test02")
    }
    
    func (stu Stu) test03() {
    	fmt.Println("test03")
    }
    
    func main() {
    	var stu Stu
    	var a Ainterface = stu
    	a.test01()
    }
    
    
  9. interface类型默认是一个指针(引用类型),如果没有对interface初始化就就使用,那么会输出nil

  10. 空接口interface没有任何方法,所有类型都实现了空接口 可以把任何变量赋给空接口

    package main
    
    import "fmt"
    
    type Stu struct {
    }
    
    func (stu Stu) test01() {
    	fmt.Println("test01")
    }
    
    func (stu Stu) test02() {
    	fmt.Println("test02")
    }
    
    func (stu Stu) test03() {
    	fmt.Println("test03")
    }
    
    type T interface {
    }
    
    func main() {
    	var stu Stu
    	var t T = stu
    	fmt.Println(t)
    	var t2 interface{} = stu
    	num1 := 8.8
    	t2 = num1
    	fmt.Println(t2)
    }
    
    
接口运用实例
结构体切片按特定字段进行排序
package main

import (
	"fmt"
	"math/rand"
	"sort"
)

type Hero struct {
	Name string
	Age  int
}

type HeroSlice []Hero

func (hs HeroSlice) Len() int {
	return len(hs)
}

func (hs HeroSlice) Less(i, j int) bool {
	return hs[i].Age < hs[j].Age
}

func (hs HeroSlice) Swap(i, j int) {
	temp := hs[i]
	hs[i] = hs[j]
	hs[j] = temp
}

func main() {
	var heroes HeroSlice
	for i := 0; i < 10; i++ {
		hero := Hero{
			Name: fmt.Sprintf("英雄~%d", rand.Intn(100)),
			Age:  rand.Intn(100),
		}
		heroes = append(heroes, hero)
	}
	for _, v := range heroes {
		fmt.Println(v)
	}
	fmt.Println("==============")
	sort.Sort(heroes)
	for _, v := range heroes {
		fmt.Println(v)
	}
}

多态

接口来实现的

多态数组
package main

import (
	"fmt"
)

// 声明一个接口
type Usb interface {
	//声明两个没有实现的方法
	Start()
	Stop()
}

type Phone struct {
	name string
}

// 让Phone实现Usb接口的方法
func (p Phone) Start() {
	fmt.Println("手机开始工作")
}

func (p Phone) Stop() {
	fmt.Println("手机结束工作")
}

type Camera struct {
	name string
}

// 让Phone实现Usb接口的方法
func (p Camera) Start() {
	fmt.Println("相机开始工作")
}

func (p Camera) Stop() {
	fmt.Println("相机结束工作")
}
func main() {
	//定义一个Usb接口数组,可以存放Phone和Camear的结构体变量
	var usbArr [3]Usb
	usbArr[0] = Phone{"vivo"}
	usbArr[1] = Phone{"小米"}
	usbArr[2] = Camera{"尼康"}
	fmt.Println(usbArr)
}

类型断言

介绍

​ 当我们在Go语言中使用接口类型时,有时候我们需要将接口类型转换为具体的类型。这就是类型断言(Type Assertion)。

快速入门
package main

import "fmt"

type Point struct {
	x int
	y int
}

func main() {
	var a interface{}
	var point Point = Point{1, 2}
	a = point
	var b Point
	//b=a 不可以
	b = a.(Point)  //类型断言
	fmt.Println(b)
}

//类型检测 检测是否可以强转 这样不会报错 需要自己判断
/*
package main

import "fmt"

type Point struct {
	x int
	y int
}

func main() {
	var a interface{}
	var point Point = Point{1, 2}
	a = point
	var b Point
	var x bool
	//b=a 不可以
	b, x = a.(Point)
	fmt.Println(b, x)
}

*/
实例
1
package main

import (
	"fmt"
)

// 声明一个接口
type Usb interface {
	//声明两个没有实现的方法
	Start()
	Stop()
}

type Phone struct {
	name string
}

// 让Phone实现Usb接口的方法
func (p Phone) Start() {
	fmt.Println("手机开始工作")
}

func (p Phone) Stop() {
	fmt.Println("手机结束工作")
}
func (p Phone) Call() {
	fmt.Println("手机 打电话ing")
}

type Camera struct {
	name string
}

type Computer struct {
}

func (conputer Computer) Working(usb Usb) {
	usb.Start()
	if phone, ok := usb.(Phone); ok {
		phone.Call()
	}
	usb.Stop()
}

// 让Phone实现Usb接口的方法
func (p Camera) Start() {
	fmt.Println("相机开始工作")
}

func (p Camera) Stop() {
	fmt.Println("相机结束工作")
}
func main() {
	//定义一个Usb接口数组,可以存放Phone和Camear的结构体变量
	var usbArr [3]Usb
	usbArr[0] = Phone{"vivo"}
	usbArr[1] = Phone{"小米"}
	usbArr[2] = Camera{"尼康"}

	var computer Computer
	for _, v := range usbArr {
		computer.Working(v)
		fmt.Println()
	}
}

2
package main

import "fmt"

func TypeJudge(items ...interface{}) {
	for index, x := range items {
		switch x.(type) {
		case bool:
			fmt.Printf("第%v个参数是 bool 类型,值是%v\n", index, x)
		case string:
			fmt.Printf("第%v个参数是 string 类型,值是%v\n", index, x)
		case float64:
			fmt.Printf("第%v个参数是 float64 类型,值是%v\n", index, x)
		case float32:
			fmt.Printf("第%v个参数是 float32 类型,值是%v\n", index, x)
		case int, int32, int64:
			fmt.Printf("第%v个参数是 整型 类型,值是%v\n", index, x)
		case Student:
			fmt.Printf("第%v个参数是 Student 类型,值是%v\n", index, x)
		case *Student:
			fmt.Printf("第%v个参数是 *Student 类型,值是%v\n", index, x)
		default:
			fmt.Printf("第%v个参数 类型不确定,值是%v\n", index, x)
		}
	}
}

type Student struct {
}

func main() {
	var n1 = 3.2
	var n2 float32 = 6.4
	var n3 int32 = 30
	var name string = "tom"
	address := "背京"
	n4 := 300
	stu := Student{}
	stu1 := &Student{}
	TypeJudge(n1, n2, n3, name, address, n4, stu, stu1)

}

文件操作

基本介绍

流:数据在数据源(文件)和程序(内存)之间经历的路径

输入流:数据源->程序

输出流:程序->数据源

常用的文件操作
打开文件 Open
func Open(name string)(file *File,err error)
关闭文件 Close
func (f *File) Close() error
package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.Open("./1.txt")
	if err != nil {
		fmt.Println("open file err=", err)
	}

	fmt.Printf("file=%v", file)

	err = file.Close()
	if err != nil {
		fmt.Println("close file err=", err)
	}
}

读取文件的内容并显示在终端(带缓冲区的方式)
package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func main() {
	file, err := os.Open("./1.txt")
	if err != nil {
		fmt.Println("open file err=", err)
	}

	defer func(file *os.File) {
		err := file.Close()
		if err != nil {
			fmt.Printf("关闭文件失败")
		}
	}(file)
	reader := bufio.NewReader(file)
	for {
		str, err := reader.ReadString('\n')
		fmt.Printf(str)
		if err == io.EOF {
			break
		}
	}
}

一次性读取 返回切片
package main

import (
	"fmt"
	"os"
)

func main() {
	file := "./1.txt"
	countent, err := os.ReadFile(file)
	if err != nil {
		fmt.Printf("read file err=%v", err)
	}
	fmt.Printf("%v", string(countent))
}

文件权限

源码:src\os\file.go

const (
	// Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
	O_RDONLY int = syscall.O_RDONLY // open the file read-only.
	O_WRONLY int = syscall.O_WRONLY // open the file write-only.
	O_RDWR   int = syscall.O_RDWR   // open the file read-write.
	// The remaining values may be or'ed in to control behavior.
	O_APPEND int = syscall.O_APPEND // append data to the file when writing.
	O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
	O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
	O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
	O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
)
文件模式·

src\io\fs\fs.go

const (
	// The single letters are the abbreviations
	// used by the String method's formatting.
	ModeDir        FileMode = 1 << (32 - 1 - iota) // d: is a directory
	ModeAppend                                     // a: append-only
	ModeExclusive                                  // l: exclusive use
	ModeTemporary                                  // T: temporary file; Plan 9 only
	ModeSymlink                                    // L: symbolic link
	ModeDevice                                     // D: device file
	ModeNamedPipe                                  // p: named pipe (FIFO)
	ModeSocket                                     // S: Unix domain socket
	ModeSetuid                                     // u: setuid
	ModeSetgid                                     // g: setgid
	ModeCharDevice                                 // c: Unix character device, when ModeDevice is set
	ModeSticky                                     // t: sticky
	ModeIrregular                                  // ?: non-regular file; nothing else is known about this file

	// Mask for the type bits. For regular files, none will be set.
	ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular

	ModePerm FileMode = 0777 // Unix permission bits
)
打开文件 OpenFile
func OpenFile(name string,flag int,perm FileMode)(file *File,err error)
第一个参数:文件名
第二个参数:文件权限  '|'用来连接
第三个参数:文件模式(linux)
写文件
package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	filePath := "1.txt"
	file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open file err=%v", err)
		return
	}
	str := "Hello,World\n"

	writer := bufio.NewWriter(file)
	for i := 0; i < 5; i++ {
		_, err := writer.WriteString(str)
		if err != nil {
			return
		}
	}
	//writer是带缓存的
	err = writer.Flush()
	if err != nil {
		fmt.Printf("Flush err=%v", err)
		return
	}
	err = file.Close()
	if err != nil {
        fmt.Printf("close file err=%v", err)
		return
	}
}
func WriteFile(filename string,data []byte,prem os.FileMode) error
判断文件是否存在
package io
func Stat(name string)(file FileInfo,err error)
Stat返回一个描述name指定的文件对象的Fileinfo。如果指定的文件对象是个符号链接,返回的FileInfo描述该符号链接指向的文件的信息,本函数会尝试跳转该链接。如果出错,错误值为*PathError类型。
如果返回的错误类型用os.IsNotExist()判断为true,说明文件或文件夹不存在
如果是false则不确定是否存在
拷贝文件
package io
func Copy(dst Writer,src Reader)(written int64,err error)
返回拷贝的字节数和遇到的第一个error
func CopyFile(dstFilename string, srcFilename string) (written int64, err error) {
	srcFile, err := os.Open(srcFilename)
	if err != nil {
		fmt.Printf("open file err=%v\n", err)
		return
	}
	defer srcFile.Close()
	reader := bufio.NewReader(srcFile)

	dstFile, err := os.OpenFile(dstFilename, os.O_WRONLY|os.O_CREATE, 0666)
	if err != nil {
		fmt.Printf("open file err=%v\n",err)
		return
	}
	writer := bufio.NewWriter(dstFile)
	defer dstFile.Close()
	return io.Copy(writer, reader)
}

命令行参数

基本介绍

我们希望能够获得到命令行输入的各种参数

os.Args是一个string切片,可以用来存储所有的命令行参数

package main

import (
	"fmt"
	"os"
)

func main() {
	fmt.Println("命令行的参数有", len(os.Args))
	for i, v := range os.Args {
		fmt.Printf("args[%v]=%v\n", i, v)
	}
}

flag包
package main

import (
	"flag"
	"fmt"
)

func main() {
	var user string
	var pwd string
	var host string
	var port int

	//&user 就是接收用户命令行中输入的-u 后面的参数值
	//“u” 就是-u指定参数
	//“” 默认值
	// “用户名默认为空” 说明
	flag.StringVar(&user, "u", "", "用户名默认为空")
	flag.StringVar(&pwd, "pwd", "", "密码默认为空")
	flag.StringVar(&host, "h", "localhost", "主机名默认为localhost")
	flag.IntVar(&port, "port", 3306, "端口默认为3306")
	//转换 必须调用该方法
	flag.Parse()

	fmt.Printf("user=%v pwd=%v host=%v port=%v\n", user, pwd, host, port)
}

json

序列化
package main

import (
	"encoding/json"
	"fmt"
)

type Monster struct {
	Name     string
	Age      int
	Birthday string
	Sal      float64
	Skill    string
}

func testStruct() {
	monster := Monster{
		Name:     "牛魔王",
		Age:      500,
		Birthday: "2011-11-11",
		Sal:      8000.0,
		Skill:    "牛魔拳",
	}
	data, err := json.Marshal(&monster)
	if err != nil {
		fmt.Println("序列化错误 err=", err)
	}
	fmt.Printf("monster序列化后=%v\n", string(data))
}

func testmap() {
	var a map[string]interface{}
	a = make(map[string]interface{})
	a["name"] = "红孩儿"
	a["age"] = 30
	a["address"] = "火云洞"

	data, err := json.Marshal(&a)
	if err != nil {
		fmt.Println("序列化错误 err=", err)
	}
	fmt.Printf("a序列化后=%v\n", string(data))
}

func testslice() {
	var slice []map[string]interface{}
	var m1 map[string]interface{}
	m1 = make(map[string]interface{})
	m1["name"] = "Jack"
	m1["age"] = 18
	m1["address"] = "北京"
	slice = append(slice, m1)

	var m2 map[string]interface{}
	m2 = make(map[string]interface{})
	m2["name"] = "isn"
	m2["age"] = 17
	m2["address"] = "上海"
	slice = append(slice, m2)

	data, err := json.Marshal(&slice)
	if err != nil {
		fmt.Println("序列化错误 err=", err)
	}
	fmt.Printf("slice序列化后=%v\n", string(data))
}
func main() {
	testStruct()
	testmap()
	testslice()
}

反序列化
json.Unmarshal([]byte(str),&obj)

反序列化map时不需要make

单元测试

快速入门

测试文件格式一定是???_test.go

package main

import "testing"

func TestAddUpper(t *testing.T) {
	res := AddUpper(20)
	if res != 55 {
		t.Fatalf("期望值=%v 实际值=%v\n", 55, res)
	}
	t.Logf("执行正确")
}

go test -v
go test -v cal_test.go cal.go //测试单个文件 一定要带上被测试的原文件
go test -v test.run TestAddUpper  //测试单个方法

go routine and channel

并发与并行
并发

多线程程序在单核上运行 就是并发

特点:

  1. 多个任务作用在一个Cpu
  2. 从微观的角度看,在一个时间点上,其实只有一个任务在执行
并行

多线程程序在多核上运行 就是并行

特点:

  1. 多个任务作用在多个Cpu
  2. 从微观角度看,在一个时间点,多个任务在同时执行
GO协程和主线程
  1. Go主线程(有程序员直接称之为线程/可理解为进程); 一个Go线程上,可以起多个协程;协程是轻量级的线程(资源耗费相对较少)

  2. 特点:

    ​ 有独立的栈空间

    ​ 共享程序堆空间

    ​ 调度由用户控制

    ​ 协程是轻量级的线程

MPG模式

M:操作系统的主线程(是物理线程)

P:代表调度的上下文,可以把它看做一个局部的调度器,使 go 代码在一一个线程上跑,是实现从 N:1到 N:M 映射的关键。

G:协程

image

image

runtime包
NumCpu
func NumCpu()int
//返回本地计算器的逻辑cpu个数
GOMAXPROCS
func GOMAXPROCS(n int) int
//设置可同时执行最大CPU数
互斥锁
package main

import (
	"fmt"
	"sync"
)

var (
	myMap = make(map[int]int64, 10)
	lock  sync.Mutex
)

func test(n int) {
	var res int64 = 1
	for i := 1; i <= n; i++ {
		res *= int64(i)
	}
	lock.Lock()
	myMap[n] = res
	lock.Unlock()
}

func main() {
	for i := 1; i <= 20; i++ {
		go test(i)
	}
	for i, v := range myMap {
		fmt.Println(i, v)
	}
}

channel
基本介绍
  1. 本质就是数据结构的队列
  2. 先进先出 【FIFO: first in first out】
  3. 线程安全,多go routine访问时不需要加锁,就是channel本身时线程安全的
  4. channel由类型的 一个string的channel只能存放string类型数据
基本使用
var obj_name chan data_type

说明:

1. channel是引用类型
2. channel必须make后才能使用
3. 管道有类型 intChan只能写入整数int
写入&&读取数据
package main

import "fmt"

func main() {
	var intChan chan int
	intChan = make(chan int, 3)

	intChan <- 10
	num := 211
	intChan <- num
	intChan <- 50
	fmt.Printf("channel len=%v \ncap=%v\n", len(intChan), cap(intChan))
	//3    3

	var num2 int
	num2 = <-intChan
	fmt.Println(num2)
	fmt.Printf("channel len=%v \ncap=%v\n", len(intChan), cap(intChan))
	//2    3
	
}

注意事项
  1. channel只能存放指定数据类型
  2. channel的数据放满后,就不能再放入了
  3. 如果从channel中取出数据,则可以继续放入
  4. 在没有使用协程的情况下,如果channel数据取完了,再取,就会报deadlock
  5. go routine中使用recover,解决协程中出现panic,导致程序崩溃问题
关闭管道

一旦关闭 只能读不能写

close(objChan)
遍历管道

for-range遍历

  1. 在遍历时 如果channel没有关闭,则会出现deadlock错误
  2. 在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
只读只写
var chan2 chan<- int  //只写
var chan3 <-chan int //只读
select

select可以解决从管道取数据堵塞的问题

package main

import "fmt"

func main() {
	intChan := make(chan int, 10)
	for i := 0; i < 10; i++ {
		intChan <- i
	}

	intChan2 := make(chan int, 10)
	for i := 10; i < 20; i++ {
		intChan2 <- i
	}
label:
	for {
		select {
		case v := <-intChan:
			fmt.Println("intChan读取数据为", v)
		case v := <-intChan2:
			fmt.Println("intChan2读取数据为", v)
		default:
			fmt.Println("都取不到")
			break label
		}
	}
}

routine&&channel
package main

import (
	"fmt"
)

func writeData(intChan chan int) {
	for i := 0; i < 50; i++ {
		intChan <- i
		fmt.Printf("readData 写入数据=%v\n", i)
	}
	close(intChan)
}

func readData(intChan chan int, exitChan chan bool) {
	for {
		v, ok := <-intChan
		if !ok {
			break
		}
		fmt.Printf("readData 读到数据=%v\n", v)
	}
	exitChan <- true
	close(exitChan)
}

func main() {
	intChan := make(chan int, 50)
	boolChan := make(chan bool, 1)

	go writeData(intChan)
	go readData(intChan, boolChan)

	for range boolChan {

	}
}

多线程求素数
package main

import (
	"fmt"
	"math"
	"time"
)

func putNum(intChan chan int) {
	for i := 1; i <= 8000; i++ {
		intChan <- i
	}
	close(intChan)
}

func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
	flag := true
	for {
		time.Sleep(time.Millisecond * 10)
		num, ok := <-intChan
		if !ok {
			break
		}
		flag = true
		for i := 2; i < int(math.Sqrt(float64(num)))+1; i++ {
			if num%i == 0 {
				flag = false
				break
			}
		}

		if flag {
			primeChan <- num
		}
	}

	fmt.Println("有一个协程结束")
	exitChan <- true
}

func main() {
	intChan := make(chan int, 1000)
	primeChan := make(chan int, 2000)
	exitChan := make(chan bool, 4)

	go putNum(intChan)
	for i := 0; i < 4; i++ {
		go primeNum(intChan, primeChan, exitChan)
	}
	go func() {
		for i := 0; i < 4; i++ {
			<-exitChan
		}
		close(primeChan)
	}()

	for {
		res, ok := <-primeChan
		if !ok {
			break
		}
		fmt.Printf("素数=%d\n", res)
	}
}

反射

基本介绍
  1. 反射可以在运动时动态获取变量的各种信息,比如变量的类型(type)
  2. 如果时结构体变量,还可以获取到结构体本身的信息(包括结构体的字段 方法)
  3. 通过反射,可以修改变量的值,可以调用关联的方式。
  4. 使用反射,需要import "reflect"
TypeOf
reflect.TypeOf(obj)  //获取obj类型 返回reflect.Type类型  
ValueOf
reflect.ValueOf(obj)  //获取变量的值,返回reflect.Value->结构体类型
interface{}和reflect.Value相互转换
rVal := reflect.Value(b) //interface{} -> reflect.Value
iVal :=rVal.Interface()  //reflect.Value -> interface{}
type接口

/src/reflect/type.go

type Type interface {
	// Methods applicable to all types.

	// Align returns the alignment in bytes of a value of
	// this type when allocated in memory.
	Align() int

	// FieldAlign returns the alignment in bytes of a value of
	// this type when used as a field in a struct.
	FieldAlign() int

	// Method returns the i'th method in the type's method set.
	// It panics if i is not in the range [0, NumMethod()).
	//
	// For a non-interface type T or *T, the returned Method's Type and Func
	// fields describe a function whose first argument is the receiver,
	// and only exported methods are accessible.
	//
	// For an interface type, the returned Method's Type field gives the
	// method signature, without a receiver, and the Func field is nil.
	//
	// Methods are sorted in lexicographic order.
	Method(int) Method

	// MethodByName returns the method with that name in the type's
	// method set and a boolean indicating if the method was found.
	//
	// For a non-interface type T or *T, the returned Method's Type and Func
	// fields describe a function whose first argument is the receiver.
	//
	// For an interface type, the returned Method's Type field gives the
	// method signature, without a receiver, and the Func field is nil.
	MethodByName(string) (Method, bool)

	// NumMethod returns the number of methods accessible using Method.
	//
	// For a non-interface type, it returns the number of exported methods.
	//
	// For an interface type, it returns the number of exported and unexported methods.
	NumMethod() int

	// Name returns the type's name within its package for a defined type.
	// For other (non-defined) types it returns the empty string.
	Name() string

	// PkgPath returns a defined type's package path, that is, the import path
	// that uniquely identifies the package, such as "encoding/base64".
	// If the type was predeclared (string, error) or not defined (*T, struct{},
	// []int, or A where A is an alias for a non-defined type), the package path
	// will be the empty string.
	PkgPath() string

	// Size returns the number of bytes needed to store
	// a value of the given type; it is analogous to unsafe.Sizeof.
	Size() uintptr

	// String returns a string representation of the type.
	// The string representation may use shortened package names
	// (e.g., base64 instead of "encoding/base64") and is not
	// guaranteed to be unique among types. To test for type identity,
	// compare the Types directly.
	String() string

	// Kind returns the specific kind of this type.
	Kind() Kind

	// Implements reports whether the type implements the interface type u.
	Implements(u Type) bool

	// AssignableTo reports whether a value of the type is assignable to type u.
	AssignableTo(u Type) bool

	// ConvertibleTo reports whether a value of the type is convertible to type u.
	// Even if ConvertibleTo returns true, the conversion may still panic.
	// For example, a slice of type []T is convertible to *[N]T,
	// but the conversion will panic if its length is less than N.
	ConvertibleTo(u Type) bool

	// Comparable reports whether values of this type are comparable.
	// Even if Comparable returns true, the comparison may still panic.
	// For example, values of interface type are comparable,
	// but the comparison will panic if their dynamic type is not comparable.
	Comparable() bool

	// Methods applicable only to some types, depending on Kind.
	// The methods allowed for each kind are:
	//
	//	Int*, Uint*, Float*, Complex*: Bits
	//	Array: Elem, Len
	//	Chan: ChanDir, Elem
	//	Func: In, NumIn, Out, NumOut, IsVariadic.
	//	Map: Key, Elem
	//	Pointer: Elem
	//	Slice: Elem
	//	Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField

	// Bits returns the size of the type in bits.
	// It panics if the type's Kind is not one of the
	// sized or unsized Int, Uint, Float, or Complex kinds.
	Bits() int

	// ChanDir returns a channel type's direction.
	// It panics if the type's Kind is not Chan.
	ChanDir() ChanDir

	// IsVariadic reports whether a function type's final input parameter
	// is a "..." parameter. If so, t.In(t.NumIn() - 1) returns the parameter's
	// implicit actual type []T.
	//
	// For concreteness, if t represents func(x int, y ... float64), then
	//
	//	t.NumIn() == 2
	//	t.In(0) is the reflect.Type for "int"
	//	t.In(1) is the reflect.Type for "[]float64"
	//	t.IsVariadic() == true
	//
	// IsVariadic panics if the type's Kind is not Func.
	IsVariadic() bool

	// Elem returns a type's element type.
	// It panics if the type's Kind is not Array, Chan, Map, Pointer, or Slice.
	Elem() Type

	// Field returns a struct type's i'th field.
	// It panics if the type's Kind is not Struct.
	// It panics if i is not in the range [0, NumField()).
	Field(i int) StructField

	// FieldByIndex returns the nested field corresponding
	// to the index sequence. It is equivalent to calling Field
	// successively for each index i.
	// It panics if the type's Kind is not Struct.
	FieldByIndex(index []int) StructField

	// FieldByName returns the struct field with the given name
	// and a boolean indicating if the field was found.
	FieldByName(name string) (StructField, bool)

	// FieldByNameFunc returns the struct field with a name
	// that satisfies the match function and a boolean indicating if
	// the field was found.
	//
	// FieldByNameFunc considers the fields in the struct itself
	// and then the fields in any embedded structs, in breadth first order,
	// stopping at the shallowest nesting depth containing one or more
	// fields satisfying the match function. If multiple fields at that depth
	// satisfy the match function, they cancel each other
	// and FieldByNameFunc returns no match.
	// This behavior mirrors Go's handling of name lookup in
	// structs containing embedded fields.
	FieldByNameFunc(match func(string) bool) (StructField, bool)

	// In returns the type of a function type's i'th input parameter.
	// It panics if the type's Kind is not Func.
	// It panics if i is not in the range [0, NumIn()).
	In(i int) Type

	// Key returns a map type's key type.
	// It panics if the type's Kind is not Map.
	Key() Type

	// Len returns an array type's length.
	// It panics if the type's Kind is not Array.
	Len() int

	// NumField returns a struct type's field count.
	// It panics if the type's Kind is not Struct.
	NumField() int

	// NumIn returns a function type's input parameter count.
	// It panics if the type's Kind is not Func.
	NumIn() int

	// NumOut returns a function type's output parameter count.
	// It panics if the type's Kind is not Func.
	NumOut() int

	// Out returns the type of a function type's i'th output parameter.
	// It panics if the type's Kind is not Func.
	// It panics if i is not in the range [0, NumOut()).
	Out(i int) Type

	common() *rtype
	uncommon() *uncommonType
}

快速入门
package main

import (
	"fmt"
	"reflect"
)

func reflectTest01(b interface{}) {
	//通过反射获取传入变量的type kind
	rType := reflect.TypeOf(b)
	fmt.Println("rType=", rType)

	rValue := reflect.ValueOf(b)
	fmt.Println("rValue=", rValue)
	fmt.Println("rValue+2=", rValue.Int()+2)

	iv := rValue.Interface()
	num2 := iv.(int)
	fmt.Println("num2=", num2)
}

func main() {
	var num = 100
	reflectTest01(num)
}

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	name string
	Age  int
}

func reflectTest01(b interface{}) {
	//通过反射获取传入变量的type kind
	rType := reflect.TypeOf(b)
	fmt.Println("rType=", rType)

	rValue := reflect.ValueOf(b)
	fmt.Println("rValue=", rValue)

	iv := rValue.Interface()
	fmt.Printf("iv=%v  T=%T\n", iv, iv)

	fmt.Println("rType.Kind=", rType.Kind())
	fmt.Println("rValue.Kind=", rValue.Kind()) //获得变量的类别 返回的是常量

}

func main() {
	stu := Student{"li", 18}
	reflectTest01(stu)

}

//rType= main.Student
//rValue= {li 18}
//iv={li 18}  T=main.Student
//rType.Kind= struct
//rValue.Kind= struct

注意事项
  1. reflect.Value.Kind()获得变量的类别 返回一个常量

  2. Type是类型 Kind是类别 可能相同也可能不同 例如结构体就不同

  3. 使用反射获取变量值 要求类型匹配 x是int 你就用reflect.Value.Int()

  4. 通过反射来修改变量 当使用SetXxx方法来设置需要通过对应指针类型来完成,这样才能改变传入变量的值,同时需要使用reflect.Value.Elem()

    val.Elem().SetInt(10)
    
  5. //fn.Elem()
    var num=20
    var b *int =&num
    *b=10
    
posted @ 2023-10-05 18:26  m0feng  阅读(28)  评论(0编辑  收藏  举报