go学习笔记

go语言学习

go语言常见包

在这里插入图片描述

声明变量方式
// 声明变量格式:var 变量名 变量类型


// 批量声明
	var (
		a int
		b string
		c []float32
		d func() bool
		e struct{
			x int
		}
	)

多重复制

// 常见的交换两个值。在排序算法很常见
func main() {
	var a = 100
	var b = 20
	a,b=b,a
	print(a,b)
}

匿名变量

// 匿名变量,匿名变量不占用命名空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。
func GetData() (int,int) {
	return 100,2000
}
func main() {
	var a = 1
	_,a=GetData()
	print(a
package main

import "fmt"

// 注意点1: package与 func 要一致
// 注意点2:func main 后面的括号要在同一行
func main() {
	var a int
	//1- 变量声明标准方式
	var name string ="ddd"
	//2- 编译器推导方式
	var age=190
	// 3- 更简洁方式。注意 海象符号不能够声明全局变量。否则抛出异常,并且编译也编译不过去
	weight := 13.3

	fmt.Println("age =",age)
	fmt.Println("weight =",weight)
	fmt.Println("hello go",name)
	fmt.Println(a)
	
	// 声明多个变量(同一种类型)
	var x,y  =100,22
	print("x=",x,",y=", y)
	// 声明多个变量(不同种类型)
	var (
		bol =false
		num =1222
	)
	print("bol=",bol,",num=", num)
}

变量的生命周期

编译器觉得变量应该分配在堆和栈上的原则是:
● 变量是否被取地址。
● 变量是否发生逃逸。


// 声明空结构体测试结构体逃逸情况
type Data struct {
}

func dummy() *Data {
// 通过 go run -gcflags "-m -l" HeapAnalysis.go 分析,可以看到 编译器将c变量的地址,放到了堆中。这句话表示,Go编译器已经确认如果将c变量分配在栈上是无法保证程序最终结果的。如果坚持这样做,dummy()的返回值将是Data结构的一个不可预知的内存地址。这种情况一般是C/C++语言中容易犯错的地方:引用了一个函数局部变量的地址。
	var c Data
	// 返回函数局部变量地址
	return &c
}

func main() {
	fmt.Println(dummy())
}
常量与枚举:const与iota
//const 与iota可以花里胡哨
// 注意:iota必须和const一起
const (
	a,b = iota+1,iota+2
	c,d     // iota =1.依次递增
	e,f
	
	g,h=iota*2,iota*3
	m,n
)

func main() {
	// const 关键字作用一:定义常量
	const legth = 110

	print("length=",legth)


	print("Spring=",SPTING)
}

字符串

获取字符串长度
●ASCII字符串长度使用len()函数。
● Unicode字符串长度使用utf8.RuneCountInString函数。go默认是使用UTF-8编码,一个中文字符占3个

// 1-获取字符串长度
func main() {
	// len函数表示字符串的ASCII编码字符个数或者字节长度
	str :="dddddddd"
	fmt.Println(len(str))
	strC :="中国"
	// 或者使用
	fmt.Printf("个数:%d\n",utf8.RuneCountInString("中国"))
	// 下面打印是6,这是由于go使用的是UTF-8 格式保存,每个中文占用3个字节
	fmt.Println(len(strC))

字符串遍历
总结:
● ASCII字符串遍历直接使用下标。(若有中文出现乱码,fori格式)
● Unicode字符串遍历用for range。

	strC :="中国"
	for _, v := range strC {
		fmt.Printf("%c",v)
	}

字符串搜索和截取

//● strings.Index:正向搜索子字符串。start := strings.Index(str,"d")
//● strings.LastIndex:反向搜索子字符串。lastIndex := strings.LastIndex(str,"d")
//● 搜索的起始位置可以通过切片偏移制作。str[3:]  类似java的 subString()
函数与多返回值
// 关键字 函数名 形参 返回值{函数体}
func funName(param1 type,param2 type) [(int,int)]{
    // 函数体
}



// 函数与多返回值
func fun(a int, name string) {
	println("a=",a,",name=",name)
}

func fun2(a int, name string) (int,int) {
	println("-----fun2------")
	println("a=",a,",name=",name)
	return 100,200
}
func fun3(a int, name string) (re1 int ,ret2 int) {
	println("-----fun2------")
	println("a=",a,",name=",name)
	 re1=100
	 ret2=2000
	return
}
func fun4(a int, name string) (re1  ,ret2 int) {
	println("-----fun2------")
	println("a=",a,",name=",name)
	re1=100
	ret2=2000
	return
}
import与init

这个有点类似,java的多重继承。也是先初始化父类的常量,代码块,然后最后才是当前类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iaehCK0E-1630501774968)(D:\root\mdPic\image-20210831204942388.png)]

演示一下

目录结构

---src
    main.go
    ---lib1
    	lib1.go
   	---lib2
    	lib2.go
package main

import "5-init/lib1"
import "5-init/lib2"
func main() {
	lib1.Lib1Test()
	lib2.Lib2Test()
}

package lib1

// todo 若 方法首字母小写,代表此方法是私有方法
func Lib1Test() {
	println("lib1.Lib2Test()......")
}

func init() {
	println("lib1.init()......")
}

package lib2

// todo 若 方法首字母小写,代表此方法是私有方法
func Lib2Test() {
	println("lib2.Lib2Test()......")
}

func init() {
	println("lib2.init()......")
}

输出

lib1.init()......
lib2.init()......
lib1.Lib2Test()......
lib2.Lib2Test()......

注意点:

1-导入包,但是不使用,会报错。但是golang IDE,会自动删除

2-支持匿名:

import (
	_ "5-init/lib1"
	"5-init/lib2"
// 你不使用,这个包的init方法会被执行
)

3-支持给包起别名

import (
	_ "5-init/lib1"
	myLib2 "5-init/lib2"

)
指针

● 类型指针,允许对这个指针类型的数据进行修改。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。
● 切片,由指向起始元素的原始指针、元素数量和容量组成。

func swap(a int,b int)  {
	var temp int
	temp=a
	a=b
	b=temp
}

func swap2(a *int, b *int) {
	var temp int
	temp =*a
	*a=*b
	*b=temp
}
func main() {
	var a ,b= 10,20
	// 值传递
	//swap(a,b)
	// 指针(引用传递),c好像还分二级指针(本质上就是套娃)
	swap2(&a,&b)
	println("a=",a,",b=",b)
	
	// 二级指针(也可以多级指针)
	var p = &a
	var pp **int= &p
	println("p的地址=",p)
	println("pp地址=",pp)
}

defer关键字

作用:让方法结束之后,调用之,类似于finally以及aop的后置通知

func A()  {
	println("A")
}

func B()  {
	println("B")
}
func C()  {
	println("C")
}
func main() {
	// 这里涉及到 函数出入栈的问题
	defer A()
	defer B()
	defer C()
	println("main() ....end...")
}

结果

main() ....end...
C
B
A

defer 与return一起使用。

func deferCall() {
	println("deferCall-----")
}
func returnCall() int {
	println("returnCall........")
	return 0
}
func test() int {
	defer deferCall()
	return returnCall()
}
func main() {

	test()
}

输出

returnCall…
deferCall-----

数组
数组定义与遍历
// 注意点1:传参类型必须是:[10]int。
func testArray(test [10]int)  {
	// 此种for循环,出参是 index key。但是若定义了index就必须使用,可通过匿名变量的方式。定义了,但是又没有完全定义。tmd
	for _,value := range test {
		println("value=",value)
	}
	// 这里还是值传递。和java一样。java里数组属于引用类型了。
	// 这行并不会真正生效
	test[7]=10000
}

func main() {
	var ints [10]int

	// 数组定义方式二
	intArr :=[10]int{1111,2222,777}
	//还可以这样定义
	// names :=[]string{"ssss","eeeeeeee"}
	testArray(intArr)
	// 数组遍历方式一
	for i := 0; i < len(ints); i++ {
		println(ints[i])
	}
	// 数组遍历方式二
	for index,value := range ints {
		println("index=",index,",value=",value)
	}
}


切片(动态数组)
func testArray(test []int)  {
	// 引用传递
	for _,value := range test {
		println("value=",value)
	}
	// 修改生效
	test[2]=10000
}

func main() {
	// 动态数组,切片
	intArr :=[]int{1111,2222,777,444,5555,666,777}

	testArray(intArr)
	println("修改后的值=",intArr[2])

}

输出

value= 1111
value= 2222
value= 777
value= 444
value= 5555
value= 666
value= 777
修改后的值= 10000

声明方式
func main() {
	//方式一:声明并初始化
	intArr := []int {1,2,3,56,33}
	fmt.Printf("len=%d,slice=%v\n",len(intArr),intArr)

	// 方式二:注意:此时只是声明并没有分配内存,此时访问,将抛出越界。
	var ints [] int
	// 此时直接访问,ide会高亮,提示。
	//ints[1]=11
	fmt.Printf("len=%d,slice=%v\n\n", len(ints), ints)

	var ageArr [] int
	// 分配空间
	ageArr=make([]int,3)
	ageArr[2]=10
	fmt.Printf("len=%d,slice=%v\n",len(ageArr),ageArr)

	// 第三种方式
	nameArr :=make([]string,3);
	nameArr[0]="dddd"
	fmt.Printf("len=%d,slice=%v\n",len(nameArr),nameArr)
}

切片操作

动态扩容

func main() {

	// 演示动态数组。len和cap的概念。len指的是数组有效数据的长度,cap是给你数组分配了多少位置
	nameArr :=make([]string,3,6);
	nameArr[0]="dddd"
	nameArr[1]="第二"
	// 追加,也就是下标为3的元素。java数组出来就是容量就不会变了。但是go,动态数组,容量会自动扩容。
	nameArr = append(nameArr, "lalaaa")
	fmt.Printf(nameArr[2])

	fmt.Printf("len=%d,cap=%d,slice=%v\n",len(nameArr),cap(nameArr),nameArr)

	for index, va := range nameArr {
		fmt.Printf("index=%d,value=%s\n",index,va)
	}
}

截取

有很多方式进行截取,以及深拷贝

func main() {

	nameArr :=make([]string,4,6);
	nameArr[0]="dddd"
	nameArr[1]="第二"
	nameArr[2]="第3"
	nameArr[3]="第4"

	// 这里slice是个浅拷贝,slice与nameArr 指向一个地方
	//slice := nameArr[:];
	// 同样是浅copy,对slice2修改,影响到nameArr。
	slice2 := nameArr[0:2];
	slice2[0]="修改"
	// 有点像字符串的截取,不同方式
	//slice3 :=nameArr[:4]
	//slice3 :=nameArr[2:]

	// todo 深copy
	deepSlice :=make([]string,len(nameArr),cap(nameArr)*2)
	copy(deepSlice,nameArr)
	fmt.Printf("len=%d,cap=%d,slice=%v\n",len(deepSlice),cap(deepSlice),deepSlice)
	//for i, value := range nameArr {
	//	println("index=",i,",value=",value)
	//}
}
map类型
声明方式
func main() {
	// 方式一:只是声明,并没有初始化
	var hashMap map[int] string
	if hashMap==nil {
		println("没有初始化")
	}
	hashMap =make(map[int]string,3)
	hashMap[1]="java"
	hashMap[2]="c++"

	// 方式二
	hashMap2 :=make(map[string]string,3)
	hashMap2["one"]="java"
	hashMap2["two"]="c"

	// 方式三
	hashMap3 :=map[string]string{
		"1":"java",
		"2":"c",
		"3":"c++",
	}
	fmt.Printf("map=%v\n",hashMap3)



}
基本操作
func printMap(mat map[string]string )  {
	for k,v:= range mat {
		println("key=",k,",value=",v)
	}
}

func main() {
	hashMap:=map[string]string{
		"1":"java",
		"2":"c",
		"3":"c++",
	}
	// 遍历
	printMap(hashMap)
	// 删除
	delete(hashMap,"1")
	// 修改
	hashMap["2"]="java"
	println("----------------")
	printMap(hashMap)

}

struct定义与使用
type Person struct {
	name string
	age int

}

func changeValue(per Person)  {
	per.name="修改值"
}
func pChangeValue(p *Person)  {
	p.name="入参是指针修改值"
}
func main() {
	var person Person
	person.name="szh"
	person.age=111

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

	// 修改值(竟然是值传递)
	changeValue(person)
	println("---------修改值后打印-------")
	fmt.Printf("person=%v\n",person)

	// 需要传指针
	pChangeValue(&person);
	println("---------指针修改值后打印-------")
	fmt.Printf("person=%v\n",person)
}

面向对象
封装
// 类名若是大写,表其他模块在导入的时候,可以使用,否则表私有
type Hero struct {
	// 若属性名首字母大写,表public,小写表私有
	Name  string
	Ad    int
	level int
}

func (th *Hero) Show() {
	fmt.Println("name=", th.Name)
	fmt.Println("Ad=", th.Ad)
	fmt.Println("level=", th.level)
}

func (th *Hero) GetName() string  {
	return th.Name
}
func (th *Hero)SetName(newName string)  {
	// th 是调用者该方法的对象的一个副本
	th.Name = newName
}
func main() {
	// 创建对象
	hero  :=Hero{"111",12,1}

	hero.Show()

	hero.SetName("szh")
	hero.Show()
}

继承
type Human struct {
	Name  string
	Sex    int
}

func (th *Human) eat() {
	println("Human eat----")
}
func (th *Human) walk()  {
	println("Human walk----")
}

type SuperMan struct {
	// 表 Super继承 Human
	Human
	level int
}
func (th *SuperMan) eat()  {
	println("SuperMan eat。。。。")
}
func (th *SuperMan)walk() {
	println("SuperMan walk.....")
}

func (th *SuperMan) fly()  {
	println("SuperMan fly.....")
}
func main() {
	human :=Human{"huamn",22}
	human.eat()
	// 创建对象方式
	super :=SuperMan{Human{"lu",1},22}
	super.eat()

	// 第二种创建
	println("---------------")
	var superMan SuperMan;
	superMan.Name="szh"
	superMan.Sex=1
	superMan.level=22
	superMan.eat()
	superMan.fly()
}

多态
// 定义一个接口
// 注意:子类若是没有完全实现接口,则表示,这个子类不是该接口的子类
type AnimalI interface {
	Sleep()
	GetColor() string
	GetType() string
}

// 子类去实现
type Cat struct {
	Color string
}

func (th *Cat) Eat() {
	panic("Cat eat")
}

func (th *Cat) GetColor() string{
	return th.Color
}
func (th *Cat) GetType() string{
	return "cat"
}
func (th *Cat) Sleep()  {
	println("cat is sleep")
}
// 子类去实现
type Dog struct {
	Color string
}

func (th *Dog) GetColor() string{
	return th.Color
}
func (th *Dog) GetType() string{
	return "Dog"
}
func (th *Dog) Sleep()  {
	println("Dog is sleep")
}

func showAnimal(i AnimalI) {
	println(i.GetColor())
	println(i.GetType())
}
func main() {
	//感觉离谱
	var animal AnimalI;
	animal=&Cat{"yellow"}
	animal.GetColor()
	animal.Sleep()

	// 多态演示
	dog :=Dog{"yellow"}
	showAnimal(&dog)
}

万能类型

类似于java的Object,入参可接任意类型

// interface{} 万能类型,类似于Object
func test(arg interface{})  {
	println("test is.....")
	println(arg)

	// 判断入参实际类型
	value,flag:= arg.(string)
	if flag {
		fmt.Println("agr是个string类型")
	}else {
		println("arg不是string,arg=",arg,",value=",value)
	}
}
func main() {
	i :=1
	test(i)
	test(2222)
}

Pair结构

任何变量,都有一个这样的结构,类似于java的头信息

type Reader interface  {
	ReadBook()
}

type Writer interface  {
	WriteBook()
}

type Book struct {

}

func (book *Book)ReadBook()  {
	println("ReadBook-------")
}
func (book *Book)WriteBook()  {
	println("WriteBook-------")
}
func main() {
	// b pair<type:book,value:Book{}地址}
	b :=&Book{}
	b.ReadBook()

	var r Reader
	r=b
	r.ReadBook()

	var w Writer
	w=r.(Writer)
	w.WriteBook()
}

反射
import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

// 简单类型
func reflectNum(arg interface{}) {
	fmt.Println("type=", reflect.TypeOf(arg))
	fmt.Println("value=", reflect.ValueOf(arg))

}
func DeFiledAndMethod(input interface{})  {
	// 获取input Type
	inputType :=reflect.TypeOf(input)
	fmt.Println("type=", inputType.Name())
	// 获取input value
	inputValue :=reflect.ValueOf(input)
	fmt.Println("type=", inputValue)

	//得到该类所有属性
	inputType.NumField()

	// 得到该类所有方法。
	//todo 和java类似
	inputType.NumMethod()
}
func main() {
	var num float64 = 1.2
	reflectNum(num)

	println("---------------")
	user := User{1, "sz", 222}

}

获取tag标签
import (
	"reflect"
)

type User struct {
	Id   int    `info:"Id" doc:"主键"`
	Name string `info:"Name" doc:"名称"`
}

func findTag(str interface{}) {
	t := reflect.TypeOf(str).Elem()

	for i := 0; i < t.NumField(); i++ {
		info := t.Field(i).Tag.Get("info")
		doc := t.Field(i).Tag.Get("doc")
		println("info=", info, ",doc=", doc)
	}

}

func main() {
	var use User
	findTag(&use)
}

结构体标签在json中作用
import (
	"encoding/json"
	"fmt"
)

type Movie struct {
	Title  string   `json:"title"`
	Year   int      `json:"Year"`
	Price  int      `json:"price"`
	Actors []string `json:"actors"`
}

func main() {
	movie := Movie{"习题集", 2, 3, []string{"1111", "111"}}
	// 编码的过程,结构体
	jsonStr, err := json.Marshal(movie)
	if err != nil {
		println(err)
		return
	}
	fmt.Printf("jsonStr =%s\n", jsonStr)
	println("jsonStr  --->object")
	myMovie :=Movie{}
	err=json.Unmarshal(jsonStr,&myMovie)
	if err != nil {
		println(err)
		return
	}
	fmt.Printf("jsonStr =%v\n", myMovie)


}

goroutine

匿名方式

import "time"

// 主go,此进程不能退出,若退出则子go也会退出
func main() {
	//定义并调用
	go func() {
		defer println("A defer")
		// 也可以传递参数
		func() {
			defer println("b defer")

			// 退出当前 goroutine
			//runtime.Goexit()

			println("B")
		}()

		println("A")
	}()

	time.Sleep(1 * time.Second)

}

线程通信
无缓冲channel
func main() {
	// 定义一个无缓冲channel
	c :=make(chan int)

	// 子 goroutine
	go func() {
		defer println("子 goroutine end")
		println("子 goroutine  .....")
		// 将666 发送给c
		c <-666
	}()

	// 在主goroutine 中使用num接受,子goroutine的传递的变量
	num := <-c

	println("num = ",num)
	println("main goroutine 结束")

}

无缓冲的channel存在,同步问题。类似,阻塞队列一样。我没有接,你需要等着(阻塞)

有缓存channel
func main() {
	// 定义一个缓冲channel
	c :=make(chan int,3)

	// 子 goroutine
	go func() {
		defer println("子 goroutine end")

		for i := 0; i < 3; i++ {
			c<-i
			println("子 goroutine在运行中,发送元素是:",i,"此时长度len=",len(c))
		}
	}()

	// 主goroutine 休息一会
	time.Sleep(1*time.Second)
	for i := 0; i < 3; i++ {
		// 从channel去元素
		num :=<-c
		println(num)
	}
	
	println("main goroutine 结束")

}

注意:1-channl已经满,再向里面写数据,将会阻塞。2-当channel为空,从里面取数据也会阻塞。

类似于生产者消费者模式

channel关闭特性
func main() {
	c := make(chan int)

	// 子 goroutine
	go func() {
		defer println("子 goroutine end")

		for i := 0; i < 3; i++ {
			c <- i
			println("子 goroutine在运行中,发送元素是:", i, "此时长度len=", len(c))
		}

		// todo 若是使用的是下面的for循环语句,没有关闭的话,将抛出异常
		// close 可以关闭channel
		close(c)
	}()

	// for循环还可这样写
	for {
		// ok为true表channel没有关闭,
		if data, ok := <-c; ok {
			println(data)
		}else{
			break
		}
	}
    
	// todo 也可以这样去写。channel只要不关闭就读取.比上面更加简洁
	for data := range c {
		println(data)
	}

	println("main goroutine 结束")

}

channel与select

若是有同时监视多个channel的需要,可以同时channel与select

func fibonaciii(c, quit chan int) {
	x, y := 1, 1
	for {
		select {
		case c <- x:
			// 若c可写,则case进来
			x, y = y, x+y
		case<-quit:
			println("quit")
			return
		}
	}
}
func main() {
	c:=make(chan int)
	quit :=make(chan int)

	go func() {
		for i := 0; i < 6; i++ {
			println(<-c)
		}
		quit<-0
	}()
	fibonaciii(c,quit)
	println("main goroutine 结束")

}

go modules 模式

一些环境变量的配置

go env -w GO111MODULE=on  // 默认是auto
go env -w GOPROXY=https://goproxy.cn,direct

初始化项目

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值