go语言学习笔记


最近因为实习原因要学习go语言,这里把语法之类的都进行简要的记录。
go对redis的操作参考 redisdoc.com

语法基础

(一)几种变量定义的方法

通常方法

	var i int
	fmt.Printf("i默认为:%v\n", i)//i=0
	i = 6;
	fmt.Println("i =", i)
	
	var j int = 10
	fmt.Println("j =", j)

根据值自行判断

	var num = 6
	fmt.Println("num =", num)

省略var

	name := "HKC"
	fmt.Println("name =", name)

查看变量类型

	//只能在fmt.Printf()中使用%T
	name := "HKC"
	fmt.Printf("type(name) = %T", name)

(二)fmt包

Printf()

常用
%v    查看变量值
%T    查看变量数据类型
%b   查看对应二进制 
%d    查看对应十进制 
%o    查看对应八进制
%x    查看对应十六进制

(三)常量与iota

	const(
		a = "start" //iota = 0
		b = iota	//iota = 1
		c = "hello"	//iota = 2
		d = 1.456	//iota = 3
		e = iota	//iota = 4
	)
	fmt.Println("a:", a) //a: start
	fmt.Println("b:", b) //b: 1
	fmt.Println("c:", c) //c: hello
	fmt.Println("d:", d) //d: 1.456
	fmt.Println("e:", e) //e: 4

(四)单元测试——testing

需要注意的两点

1.代码文件命名:必须要以“_test.go”结尾
在这里插入图片描述
2.函数命名形式必须为

//func TestXxxXxx(*testing T)
//举例:

//测试对象
func addUpper(n int) int{
    ret := 0
    for i:=1; i<=n; i++{
        ret += i
    }
   	return ret
}
//测试函数
func TestAddUpper(t *testing.T){
    ret := addUpper(10)
    if ret != 55 {
        t.Fatalf("error")
    }
   	t.Logf("正确")
}

3.运行命令

	//在目录下运行
	go test -v
	//将自动运行所有名称以_test.go结尾的文件

4.测试单个文件

	//在目录下运行
	go test -v xxx_test.go xxx.go

5.测试单个方法

	//在目录下运行
	go test -v -test.run TestXxxXxx

(五)JSON化后写入文件与逆向操作

JSON化后写入文件

//import必要的包
import (
	"encoding/json"

	//用于读写文件
	"io/ioutil"
)

实现如下

func (this *Monster) Store() bool {

	//先序列化
	data, err := json.Marshal(this)
	//此时data为二进制串
	if err != nil {
		fmt.Println("marshal err =", err)
		return false
	} 

	//保存到文件
	filePath := "./monster.ser"
	//0666代表权限
	//0	owner 	group 	other
	//0	-rwx	-rwx	-rwx
	//0	 6		 6		 6
	err = ioutil.WriteFile(filePath, data, 0666)
	if err != nil {
		fmt.Println("write file err =", err)
		return false
	}
	return true
}

从文件读出序列后对byte串反序列化

func (this *Monster) ReStore() bool {

	//1. 先从文件中,读取序列化的字符串
	filePath := "./monster.ser"
	data, err := ioutil.ReadFile(filePath)
	if err != nil {
		fmt.Println("ReadFile err =", err)
		return false
	}
	//此时data为字符串
	
	//2.对反序列化
	err = json.Unmarshal(data, this)
	//将data反序列化并存入this指针
	if err != nil {
		fmt.Println("UnMarshal err =", err)
		return false
	}
	return true
}

(六)strconv

import(
    "strconv"
)
func test(){
	str := strconv.Itoa(100)
	val := strconv.Atoi(str)
    time.Sleep(time.Second)
}

(七)runtime

import (
	"runtime"
	"fmt"
)

func main(){
	cpuNum := runtime.NumCPU()
	fmt.Println("cpuNum=", cpuNum)

	//可以自己设置使用多个cpu
	runtime.GOMAXPROCS(cpuNum - 1)
	fmt.Println("ok")
}

(八)channel

1.简单使用方法

package main
import (
	"fmt"
)

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

    fmt.Println("intChan = %v, &intChan = %p", intChan, &intChan)

    intChan<- 10
    num := 20
    intChan<- num
    intChan<- 50
    //此时不能再往管道中加入数据了,否则报错DeadLock
    //intChan<- 90

    fmt.Printf("len(intChan) = %v cap(intChan)=%v \n", len(intChan), cap(intChan))//3 3

    //取数据操作
    front := <-intChan
    front = <-intChan
    front = <-intChan

    fmt.Printf("front = %v\n", front)

    //此时队列已经空了,再从管道中拿数据出来报错DeadLock
    //front = <-intChan
}

2.Channel数据类型

map类型
func main() {

	//定义一个存放任意数据类型的管道 3个数据
	//var mapChan chan map[string]string
	mapChan := make(chan map[string]string, 3)

	m1 := make(map[string]string, 10)
	m1["city1"] = "西安"
	m1["city2"] = "北京"
	
	m2 := make(map[string]string, 10)
	m2["historical1"] = "兵马俑"
	m2["historical2"] = "故宫"

	mapChan<- m1;
	mapChan<- m2;

}
interface类型
package main
import (
	"fmt"
)

type Cat struct {
	Name string
	Age int
}

func main() {

	//定义一个存放任意数据类型的管道 3个数据
	//var allChan chan interface{}
	allChan := make(chan interface{}, 3)

	allChan<- 10
	allChan<- "tom jack"
	cat := Cat{"小花猫", 4}
	allChan<- cat

	//我们希望获得到管道中的第三个元素,则先将前2个推出
	<-allChan
	<-allChan

	// newCat := <-allChan //从管道中取出的Cat是什么?
	// fmt.Printf("newCat=%T , newCat=%v\n", newCat, newCat)
	// //下面的写法是错误的!编译不通过
    // //error: newCat.Name undefined (type interface{} has no field or method Name)
	// fmt.Printf("newCat.Name=%v", newCat.Name)

	//使用类型断言
    front := <-allChan //从管道中取出的Cat是什么?
    fmt.Printf("front=%T , front=%v\n", front, front)
    newCat := front.(Cat)//类型断言

    //必须使用类型断言,否则这一句将报错
	fmt.Printf("newCat.Name=%v\n", newCat.Name)
	

}

Channel的close

package main
import (
	"fmt"
)

func main() {

	intChan := make(chan int, 3)
	intChan<- 100
	intChan<- 200
	close(intChan) // close
	//这时不能够再写入数到channel
	//intChan<- 300
     
	fmt.Println("already closed")
	//当管道关闭后,读取数据是可以的
	n1 := <-intChan
	fmt.Println("n1=", n1)


	//遍历管道
	intChan2 := make(chan int, 100)
	for i := 0; i < 100; i++ {
		intChan2<- i * 2  //放入100个数据到管道
	}

	//遍历管道不能使用普通的 for 循环
	// for i := 0; i < len(intChan2); i++ {}

	//在遍历时,如果channel没有关闭,则会出现deadlock的错误
	//在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历
	close(intChan2)//关闭数据一般由发送方发起
	for v := range intChan2 {
		fmt.Println("v=", v)
	}
}

Channel中Select的用法

有时我们不确定是否要关闭一个管道,但是有需要从中读取数据。此时可以用Select避免DeadLock。

package main
import (
	"fmt"
	"time"
)

func main() {

	//使用select可以解决从管道取数据的阻塞问题

	//1.定义一个管道 10个数据int
	intChan := make(chan int, 10)
	for i := 0; i < 10; i++ {
		intChan<- i
	}
	//2.定义一个管道 5个数据string
	stringChan := make(chan string, 5)
	for i := 0; i < 5; i++ {
		stringChan <- "hello" + fmt.Sprintf("%d", i)
	}

	//传统的方法在遍历管道时,如果不关闭会阻塞而导致 deadlock

	//问题,在实际开发中,可能我们不好确定什么关闭该管道.
	//可以使用select 方式可以解决
	//label:
	for {
		select {
			//注意: 这里,如果intChan一直没有关闭,不会一直阻塞而deadlock
			//,会自动到下一个case匹配
			case v := <-intChan : 
				fmt.Printf("从intChan读取的数据%d\n", v)
				time.Sleep(time.Second)
			case v := <-stringChan :
				fmt.Printf("从stringChan读取的数据%s\n", v)
				time.Sleep(time.Second)
			default :
				fmt.Printf("都取不到了,不玩了, 程序员可以加入逻辑\n")
				time.Sleep(time.Second)
				return 
				//break label
		}
	}
}

单向Channel及其应用场景

package main
import (
	"fmt"
)
//单向channel一般配合函数使用

//参数列表定义为只可写channel
func send(ch chan<- int){
    for i:=1; i<=500; i++{
        ch<- (i*3-19)/2
    }
    close(ch)
}

//参数列表定义为从一个只可读channel读出数据后
//存入一个只可写channel
func recv(ch <-chan int, recvCh chan<- int, exitCh chan bool){
    for{
        num, ok := <-ch
        if !ok {
            break
        }
        recvCh <- num
    }
    fmt.Println("协程退出!")
    exitCh<- true
}

func main(){
    dataCh := make(chan int, 1000)
    exitCh := make(chan bool, 8)
    recvCh := make(chan int, 1000)

    go send(dataCh)
    for i:=0; i!=8; i++{
        go recv(dataCh, recvCh, exitCh)
    }

    go func(){
        for i:=0; i!=8; i++{
            <-exitCh
        }

        close(recvCh)
    }()

    for{
        num, ok := <-recvCh
        if !ok{
            break
        }
        fmt.Println("收到数据:", num)
    }

    fmt.Println("主线程退出")
}

gorountine和channel协同工作案例

package main
import (
	"fmt"
	"time"
)

func writeData(intChan chan int){
    for i:=1; i<=50; i++{
        intChan<- i
        fmt.Println("write data:", i)
        time.Sleep(time.Second)
    }
    close(intChan)
}


func readData(intChan chan int, exitChan chan bool){
    for{
        num, ok := <-intChan
        if !ok{
            break;
        }
        time.Sleep(time.Second)
        fmt.Println("got value: ", num)
    }
    exitChan<- true
    close(exitChan)
}


func main(){
    intChan := make(chan int, 10)
    exitChan := make(chan bool, 1)

    go writeData(intChan)
    go readData(intChan, exitChan)

	//为了使主线程在两个下协程执行完毕后再退出
    for{
        _, ok := <-exitChan
        if !ok{
        	//执行到这里说明readData()函数中将exitChan关闭了
            break
        }
    }
}

(九)reflect

类似于指针的应用场景

package main
import (
	"reflect"
	"fmt"
)

//通过反射,修改,
// num int 的值
// 修改 student的值

func reflect01(b interface{}) {
	//2. 获取到 reflect.Value
	rVal := reflect.ValueOf(b)
	// 看看 rVal的Kind是 
	fmt.Printf("rVal kind=%v\n", rVal.Kind())//kind = ptr
	//3. rVal
	//Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
	rVal.Elem().SetInt(20)
}

func main() {

	var num int = 10
	reflect01(&num)
	fmt.Println("num=", num) // 20


	//你可以这样理解rVal.Elem()
	// num := 9
	// ptr *int = &num
	// num2 := *ptr  //=== 类似 rVal.Elem()
}

持续更新中…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值