Go基础学习

很久之前做的笔记…整理了一下

语法注意点

  • 函数的 { 一定和函数名在同一行,否则编译错误

  • 分号加与不加都可以,一般不加

  • main函数一定在main包里

  • 导多个包:

    import(
    	"fmt"
        "time"
    )
    

常见的四种变量声明方式与多变量声明方式

// 声明全局变量,方法一、二、三是可以的
var gA int

var gB int = 10

var c = 10

//不能用方法四来声明全局变量
//gD := 100
//:= 只能够用在函数体中来声明

func main() {
	//方法一:声明一个变量 默认初始值是0
	var a int

	//方法二:声明一个变量,初始化一个值
	var b int = 10

	//方法三:在初始化的时候,可以省去数据类型,通过值自动匹配当前变量的数据类型
	var c = 10

	//方法四:(常用方法)省去var关键字,直接自动匹配
	d := 100

	//声明多个变量
	var xx, yy int = 100, 200

	var kk, ll = 100, "hello"

	var (
		vv int  = 100
		jj bool = true
	)
}

const与iota知识点注意事项

// const来定义枚举类型
const (
	//可以添加关键字 iota 每行的iota都会累加1,第一行的iota默认值是0,这样就不需要一个个赋值了
	BEIJING = iota
	SHANGHAI
	SHENZHEN
)
const (
	a, b = iota + 1, iota + 2 // iota = 0, a = iota + 1, b = iota + 2, a = 1, b = 2
	c, d                      // iota = 1, c = iota + 1, d = iota + 2, c = 2, d = 3
	e, f                      // iota = 2, e = iota + 1, f = iota + 2, e = 3, f = 4

	g, h = iota * 2, iota * 3 // iota = 3, g = iota * 2, h = iota * 3, g = 6, h = 9
	i, k                      // iota = 4, i = iota * 2, k = iota * 3, i = 8, k = 12
)

func main() {
	//常量(只读属性)
	const length int = 10
}

函数的多返回值

// go函数
func fool(s string, b int) int {
	res := 100
	return res
}

// 返回多个返回值,匿名的
func fool2(s string, b int) (int, int) {
	return 666, 777
}

// 返回多个返回值,有形参名称的
func fool3(s string, b int) (r1 int, r2 int) {
	r1 = 1000
	r2 = 2000
	return
}

// 同fool3,若r1、r2返回值类型相同,可以这样写
func fool4(s string, b int) (r1, r2 int) {
	r1 = 1000
	r2 = 2000
	return
}

init函数与import导包

Go基础学习_第1张图片

在 Go 语言中,标识符(函数、变量、结构体、方法等),首字母大写,可以在包外被访问,小写,只能在包内被访问

import匿名及别名导包方式

导入进来的包不使用会报错

import中包名前面加上“_ ” 下划线+空格,表示匿名,这样这个包不被使用也能通过编译

_ “packageName”

myPackgae "packageName" :给包起别名

. "packageName":前面加上点和空格,在使用包内方法的时候,不需要packageName.fun(),只需func()就可以调用 不要轻易使用

在 Go 语言中,如果你给导入的包起了别名或匿名,那么即使没有在代码中显式使用这个包,也能通过编译

defer语句调用顺序

defer后接的语句,在当前函数执行结束前执行

多个defer语句的调用顺序:栈的形式,1先入栈后出栈,所以是2先调用1后调用

func main() {
	defer fmt.Println("main end 1")
	defer fmt.Println("main end 2")

	fmt.Println("hello go 1")
	fmt.Println("hello go 2")
}

同一个函数,reutrn比defer先被调用

Go中的数组与动态数组的区别

普通数组:

func printArray(array [10]int) {
	for i := range array {
		fmt.Println(i)
	}
}

func main() {
	//定义固定长度的数组
	var myArray1 [10]int
	myArray2 := [10]int{1, 2, 3, 4, 5}

	//myArray1和myArray2的数据类型是[10]int

	for i := 0; i < len(myArray1); i++ {
		fmt.Println(myArray1[i])
	}
	
	for i := range myArray2 {
		fmt.Println(myArray2[i])
	}
	
	for index, value := range myArray2 {
		fmt.Println(index, value)
	}

	printArray(myArray2)
}

传参的时候,必须指定数组长度,且为值传递

slice(动态数组):

func printArray(array []int) {
	for _, value := range array {
		fmt.Println(value)
	}
}
func main() {
	//定义slice
	myArray := []int{1, 2, 3, 4, 5}
	printArray(myArray)
}

引用传递

slice的四种声明定义方式

func main() {
	//声明slice1是一个切片,并且初始化,默认值是1,2,3,长度len是3
	slice1 := []int{1, 2, 3}

	//声明slice1是一个切片,但是并没有分配空间 此时 slice2[0] = 1 这样赋值会报错
	var slice2 []int
	//开辟空间
	slice2 = make([]int, 3)
	slice2[0] = 1

	//声明slice3是一个切片,同时分配3个空间,初始值是0
	var slice3 = make([]int, 3)

	if slice2 == nil {
		fmt.Println("slice2是一个空切片")
	} else {
		fmt.Println("slice2是有空间的")
	}
}

Slice切片追加与截取

func main() {
	//3:长度(len),5:容量(capacity)
	var numbers = make([]int, 3, 5)

	//向number是切片追加一个元素1,此时len为4
	numbers = append(numbers, 1)
	//len变为5
	numbers = append(numbers, 2)
	//len等于cap时仍然可以追加,此时会自动开辟当前cap的容量,即5。此时cap=10
	numbers = append(numbers, 3)

	//截取
	s := []int{1, 2, 3}
	s1 := s[0:2] //{1, 2}

	//此时s1和s指向的地址相同
	s1[0] = 100 //s1和s的第一个1元素都会被修改

	s2 = make([]int, 3)
	copy(s2, s) //此时s2不与s指向相同空间
}

map的三种定义方式

func main() {
	//第一种声明方式
	//key:int  value:string 但仅仅这样声明的话,这个map是无法使用的
	var myMap1 map[int]string

	//在使用map前,需要先用make给map分配数据空间
	myMap1 = make(map[int]string, 10)
	myMap1[1] = "one"
	myMap1[2] = "two"
	myMap1[3] = "three"

	//第二种声明方式
	//容量不写也可以,在赋值的时候会追加
	myMap2 := make(map[string]string)
	myMap2["one"] = "one"
	myMap2["two"] = "two"
	myMap2["three"] = "three"
	fmt.Println(myMap2)

	//第三种声明方式
	myMap3 := map[string]string{
		"one":   "one",
		"two":   "two",
		"three": "three",
	}
}

map的使用方式

func printMap(cityMap map[string]string) {
	//cityMap是一个引用传递
	for key, value := range cityMap {
		fmt.Println(key, ":", value)
	}
}
func main() {
	cityMap := make(map[string]string)

	//添加
	cityMap["China"] = "Beijing"
	cityMap["Japan"] = "Tokyo"

	//遍历
	for key, value := range cityMap {
		fmt.Println(key, ":", value)
	}

	//删除
	delete(cityMap, "China")
}

struct基本定义与使用

// 声明数据类型myint,是int的一个别名
type myint int

// 定义一个结构体
type Book struct {
	name   string
	author string
}

func changeBook1(book Book) {
	//传入book副本,即值传递
	fmt.Println(book.author, book.name)
}

func changeBook2(book *Book) {
	//引用传递
	fmt.Println(book.author, book.name)
}

func main() {
	var myint num = 1

	var book1 Book
	book1.name = "GoLang"
	book1.author = "zhangsan"

	changeBook1(book1)

	changeBook2(&book1) //此时就要传地址了
}

面向对象 类的表示与封装

type Hero struct {
	Name  string
	Age   int
	Level int
}

//func (this Hero) GetName() string {
//	return this.Name
//}
//
//func (this Hero) SetName(newName string) {
//	//this是调用该方法的对象的一个副本 拷贝
//	this.Name = newName
//}

func (this *Hero) GetName() string {
	return this.Name
}

func (this *Hero) SetName(newName string) {
	this.Name = newName
}

func main() {
	//创建一个对象
	hero := Hero{Name: "zhangsan", Age: 20, Level: 1}
}

面向对象 继承

type Human struct {
	name string
	sex  string
}

func (this *Human) Eat() {
	fmt.Println("调用human的Eat方法")
}

type SuperMan struct {
	Human //表示SuperMan继承了Human类的方法

	level int
}

// 重定义父类的方法Eat
func (this *SuperMan) Eat() {
	fmt.Println("调用SuperMan的Eat方法 ")
}
func main() {
	h := Human{"zhangsan", "man"}
	h.Eat()

	superMan := SuperMan{Human{"lisi", "female"}, 5}
	superMan.Eat()
}

面向对象 多态的实现

// 本质是一个指针
type AnimalIF interface {
	Sleep()
	GetColor() string
	GetType() string
}

// 具体的类 不需要像继承那样把接口名写在类中,只需实现接口的所有方法
type Cat struct {
	color string
}

func (this *Cat) Sleep() {
	fmt.Println("猫正在睡觉")
}
func (this *Cat) GetColor() string {
	return this.color
}
func (this *Cat) GetType() string {
	return "cat"
}

type Dog struct {
	color string
}

func (this *Dog) Sleep() {
	fmt.Println("狗正在睡觉")
}
func (this *Dog) GetColor() string {
	return this.color
}
func (this *Dog) GetType() string {
	return "dog"
}

func showAnimal(animal AnimalIF) {
	animal.Sleep() //多态
}

func main() {
	var animal AnimalIF         //接口的数据类型,父类指针
	animal = &Cat{color: "red"} //需要加&
	animal.Sleep()              //调用cat的Sleep()

	animal = &Dog{color: "yellow"}
	animal.Sleep()

	cat := &Cat{color: "red"}
	dog := &Dog{color: "yellow"}
	showAnimal(cat)
	showAnimal(dog)
}

interface空接口万能类型与类型断言机制

// interface{}是万能数据类型
func MyFunc(arg interface{}) {
	fmt.Println(arg)

	//interface{} 如何区分此时引用的数据类型是什么? -> 给interface{}提供 类型断言 机制
	value, ok := arg.(string) //返回两个值
	if !ok {
		fmt.Println("arg is not a string")
	} else {
		fmt.Println("arg is a string")
		fmt.Println("value:", value) //arg是string,value是arg的值 否则,value会是string类型的零值,即空字符串""
	}
}

type Book struct {
	author string
}

func main() {
	book := Book{"zhangsan"}
	MyFunc(book)
	MyFunc(100)
	MyFunc("abc")
}

变量的内置pair结构

Go基础学习_第2张图片

func main() {
	var a string
	//pair 无论怎样赋值,pair会一直传递下去
	a = "abc"

	//pair
	var allType interface{}
	allType = a

	value, _ := allType.(string)
	fmt.Println(value)
}

反射reflect机制

在 Go 语言中,反射机制通过 reflect.Value.Interface() 方法获取值时,会严格遵循可见性规则。即使对同一包内的未导出(小写字母开头)结构体字段,该方法的调用仍会触发 panic。这是反射机制对封装性原则的贯彻体现,旨在防止通过非正常途径访问私有数据,即便这种访问发生在同一个包内。

func reflectNum(arg interface{}) {
	argType := reflect.TypeOf(arg)
	value := reflect.ValueOf(arg)
	fmt.Println(argType, value)
}

type User struct {
	Name string
	Age  int
}

func (this *User) Call() {
	fmt.Println("user is calling")
	fmt.Println("%v\n", this)
}
func DoFiledAndMethod(input interface{}) {
	inputType := reflect.TypeOf(input)
	inputValue := reflect.ValueOf(input)

	//通过type获取里面的字段
	//1.通过Type得到NumField
	//2.得到每个field,即数据类型
	//3.通过field的Interface()方法得到value
	for i := 0; i < inputType.NumField(); i++ {
		field := inputType.Field(i)
		value := inputValue.Field(i).Interface()

		//Name string zs
		//Age int 18
		fmt.Println(field.Name, field.Type, value)

	}
}
func main() {
	var num float64 = 1.2345
	reflectNum(num)

	user := User{"zs", 18}
	DoFiledAndMethod(user)
}

反射解析结构体标签tag

type resume struct {
	Name string `info:"name" doc:"我的名字"` //标签 同一个字段可以有多个
	Sex  string `info:"sex"`
}

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

	for i := 0; i < t.NumField(); i++ {
		tagInfo := t.Field(i).Tag.Get("info")
		tagDoc := t.Field(i).Tag.Get("doc")
		fmt.Println(tagInfo)
		fmt.Println(tagDoc)
	}
}
func main() {
	re := resume{"Momentary_SixthSense", "man"}
	findTag(&re)
}

结构体标签在json中的应用

import (
	"encoding/json"
	"fmt"
)

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

func main() {
	movie := Movie{"喜剧之王", 2000, []string{"zhouxingchi", "zhangbozhi"}}

	//编码的过程:结构体 -> json
	jsonStr, err := json.Marshal(movie)
	if err != nil {
		fmt.Println("json marshal err:", err)
		return
	}
	fmt.Println(string(jsonStr))

	//解码的过程:json -> 结构体   jsonStr = {"title":"喜剧之王","year":2000,"Actors":["zhouxingchi","zhangbozhi"]}
	my_movie := Movie{}
	err = json.Unmarshal(jsonStr, &my_movie)
	if err != nil {
		fmt.Println("json unmarshal err:", err)
		return
	}
	fmt.Println(my_movie)
}

json中显示的是标签内容title

goroutine基本模型和调度设计策略

创建goroutine

// 从goroutine
func newTask() {
	i := 0
	for {
		i++
		fmt.Println(i)
		time.Sleep(1 * time.Second)
	}
}

// 主goroutine
func main() {
	//创建一个goroutine,去执行newTask流程
	go newTask()

	i := 0
	for {
		i++
		fmt.Println(i)
		time.Sleep(1 * time.Second)
	}
}
func main() {
	//创建goroutine 调用匿名函数 无参
	go func() {
		defer fmt.Println("A defer")

		func() {
			defer fmt.Println("B defer")
			fmt.Println("B")
		}()

		fmt.Println("A")
	}() //此处加上括号,表示调用

	//有参
	go func(a, b int) bool {
		fmt.Println(a, b)
		return true
	}(10, 20)

	for {
		time.Sleep(1 * time.Second)
	}
}

channel的基本定义与使用

func main() {
	//channel定义
	c := make(chan int)

	go func() {
		defer fmt.Println("goroutine 结束")
		fmt.Println("goroutine 正在运行")

		c <- 666 //将666发送给c
	}()

	num := <-c //从c中接收数据,赋值给num
	fmt.Println(num)
	fmt.Println("main goroutine 结束")
}

channel有缓冲与无缓冲同步问题

在Go语言的并发模型中,无缓冲channel的发送和接收操作遵循同步通信机制。发送协程会阻塞直至存在对应的接收操作就绪,接收协程同样会阻塞直到发送操作就绪,这种双向等待机制确保数据传输的原子性和时序一致性。

相比之下,有缓冲channel的发送和接收操作则具有一定的异步性。有缓冲channel在创建时会分配一个固定大小的缓冲区,用于暂存发送的数据。当缓冲区未满时,发送操作可以立即完成,而不需要等待接收方就绪;当缓冲区为空时,接收操作也可以立即完成,而不需要等待发送方就绪。只有当缓冲区满时,发送操作才会阻塞,直到有接收操作从缓冲区中取出数据;同样,当缓冲区为空时,接收操作才会阻塞,直到有发送操作向缓冲区中放入数据。

func main() {
	//有缓冲的channel
	c := make(chan int, 3)

	go func() {
		defer fmt.Println("goroutine 结束")
		fmt.Println("goroutine 正在运行")

		for i := 0; i < 3; i++ {
			c <- i
		}
	}()

	for i := 0; i < 3; i++ {
		num := <-c
		fmt.Println(num)
	}
	fmt.Println("main goroutine 结束")
}

channel的关闭特点

func main() {
	c := make(chan int)

	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}

		//关闭一个channel
		close(c)
	}()

	for {
		//ok为true表示当前channel没有关闭
		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			break
		}
	}
	fmt.Println("Main Finished...")
}

channel与range

for {
		//ok为true表示当前channel没有关闭
		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			break
		}
	}

	//上方代码可以改成:
	//可以使用range来不断迭代操作range
	for data := range c {
		fmt.Println(data)
	}

channel与select

单流程下一个go只能监控一个channel的状态,select可以完成监控多个channel的状态

func fibonacii(c, quit chan int) {
	x, y := 1, 1
	for {
		select {
		case c <- x:
			x = y
			y = x + y

		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}
func main() {
	c := make(chan int)
	quit := make(chan int)

	go func() {
		for i := 0; i < 6; i++ {
			fmt.Println(<-c)
		}

		quit <- 0
	}()

	fibonacii(c, quit)
}

channel
close©
}()

for {
	//ok为true表示当前channel没有关闭
	if data, ok := <-c; ok {
		fmt.Println(data)
	} else {
		break
	}
}
fmt.Println("Main Finished...")

}




# channel与range

```go
for {
		//ok为true表示当前channel没有关闭
		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			break
		}
	}

	//上方代码可以改成:
	//可以使用range来不断迭代操作range
	for data := range c {
		fmt.Println(data)
	}

channel与select

单流程下一个go只能监控一个channel的状态,select可以完成监控多个channel的状态

func fibonacii(c, quit chan int) {
	x, y := 1, 1
	for {
		select {
		case c <- x:
			x = y
			y = x + y

		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}
func main() {
	c := make(chan int)
	quit := make(chan int)

	go func() {
		for i := 0; i < 6; i++ {
			fmt.Println(<-c)
		}

		quit <- 0
	}()

	fibonacii(c, quit)
}

你可能感兴趣的:(golang,学习,开发语言)