golang快速入门

环境搭建

  1. 下载源码包

  2. 配置环境变量

  3. 检查安装情况

    go version
    // 或者
    go --help
    

Go语言分析

  1. 优点:容易部署、静态语言、支持并发
  2. 缺点:包管理、不支持泛型、异常处理
  3. 使用场景:云计算、微服务、区块连

初识go

package main // 程序的包名

/*
导包
import "fmt"
import "time"
*/
import (
	"fmt"
	"time"
)

func main() {
	fmt.Print("Hello ")
	time.Sleep(1 * time.Second)
	fmt.Print("Go!")
}

变量的声明方式

  1. 变量声明
    • 第一种:只声明,不赋值,默认值为0
    • 第二种:声明并赋值
    • 第三种:初始化时省去数据类型,通过自动匹配当前变量的数据类型
    • 第四种(最常用):省去var关键字和数据类型
  2. 声明全局变量:除了第四种
  3. 多变量声明
    • 单行写法
    • 多行写法
package main

import (
	"fmt"
)

/*
四种变量声明方式
*/

// 声明全局变量(方法4不能用来声明全局变量)
var g01 int
var g02 string = "tongtianzi"

func main() {
	// 第一种:只声明,不赋值,默认值为0
	var a int
	fmt.Println("a = ", a)
	fmt.Printf("a type is: %T\n", a)

	// 第二种:声明并赋值
	var b int = 100
	fmt.Println("b = ", b)
	fmt.Printf("b type is: %T\n", b)

	// 第三种:初始化时省去数据类型,通过自动匹配当前变量的数据类型
	var c = "haha"
	fmt.Println("c = ", c)
	fmt.Printf("c type is: %T\n", c)

	// 第四种(最常用):省去var关键字和数据类型
	d := 200
	fmt.Println("d = ", d)
	fmt.Printf("d type is: %T\n", d)

	// 访问全局变量
	fmt.Println("g01 = ", g01, "g02 = ", g02)

	// 声明多个变量
	var e, f int = 101, 102
	fmt.Println("e = ", e)
	fmt.Println("f = ", f)
	var g, h = 201, 202
	fmt.Println("g = ", g, "h = ", h)

	var (
		j int = 1001
		k string = "TongTianZi"
	)
	fmt.Println("j = ", j)
	fmt.Println("k = ", k)
}

const和iota

  1. const定义常量

    // const定义常量
    const height int = 100
    
  2. iota和const定义枚举

    // const来定义枚举
    const (
    	// 在const()中通过关键字iota来实现自增枚举,每行iota加1,iota默认为0
    	a = iota  // iota = 0
    	b         // iota = 1
    )
    
    const (
    	c, d = iota + 1, iota + 2  // iota = 0, c = 1, d = 2
    	e, f 						// iota = 1, e = 2, f = 3
    	g, h						// iota = 2, g = 3, h = 4
    
    	j, k = 10 * iota, 20 * iota  // iota = 3, j  = 30, k = 60
    	l, m						// iota = 4, l = 40, m = 80
    )
    

函数的多返回值

  1. 一个返回值

    func f01(name string, age int) int {  // 一个返回值
    	fmt.Println("name = ", name)
    	fmt.Println("age = ", age)
    	return 100
    }
    
  2. 多返回值,匿名

    func f02(a string, b int) (int, int) {  // 多个匿名返回值
    	fmt.Println("a = ", a)
    	fmt.Println("b = ", b)
    	return 10, 20
    }
    
  3. 多返回值,有名称

    func f03(a int, b int) (ret01 int, ret02 int) {  // 多个有名返回值
    	fmt.Println("a = ", a)
    	fmt.Println("b = ", b)
    	// ret01, ret02相当于f03的形参,默认值为0
    	// 作用域为f03{}中的空间
    	ret01 = 10
    	ret02 = 20
    	//return
    	return ret01, ret02
    }
    

import导包和init函数

在这里插入图片描述

init函数

  1. import导包的时候会先执行该包中的init()函数
  2. 包中函数名首字母小写表示该接口只有在该包中能使用
  3. 包中函数名首字母大写表示该接口可以对外提供服务

import导包

  1. import _ “fmt”:给包取别名,匿名。

    go中有严格的导包规定,导包之后不适用包中的内容,将会报错!

    import _ "fmt"相当于给包取了一个别名,匿名,无法使用保重的内容,但会执行保重的init函数;

  2. import aa “fmt”:给包取别名,aa,就可以通过aa.Println()调用包中的接口;

  3. import . “fmt”:把fmt包中的内容全部导入到当前包中,可以直接使用包中的api,Println(),不需要通过fmt.Println()来调用,但不安全,容易引起歧义,不建议这样用。

指针

在这里插入图片描述

代码示例

package main

import "fmt"

func swap(pa *int, pb *int){  // 表示pa和pb都是指针类型
	temp := *pa  // 用temp保存pa的地址值
	*pa = *pb  // pa的地址值等于pb的地址值
	*pb = temp  // pb的地址值等于pa的地址值
}

func main() {
	var a = 10
	var b = 20
	// 交换a , b的值
	swap(&a, &b)  // 传递的是a和b的地址值
	fmt.Println("a = ", a, "b = ",  b)
}

defer

  1. defer作用:在函数执行的过程中遇到defer + 表达式,先进栈,当函数中所有逻辑执行完,在函数结束出栈一次执行。

    代码示例

    package main
    
    import "fmt"
    
    func test01_defer() {
    	fmt.Println("test01_defer ...")
    }
    
    func test02_defer() {
    	fmt.Println("test02_defer ...")
    }
    
    func main() {
    	defer test01_defer()  // test01_defer进栈
    	defer test02_defer()  // test01_defer进栈
    	fmt.Println("main ...")
    	// 在函数结束前出栈
    }
    
    // 结果
    main ...
    test02_defer ...
    test01_defer ...
    
  2. defer在return后执行(defer在函数所有代码执行完,结束前执行,return仍然是函数中的一部分)

    代码示例

    package main
    
    import "fmt"
    
    func returnFunc() int {
    	fmt.Println("return 执行了...")
    	return 0
    }
    
    func deferFunc() {
    	fmt.Println("defer 执行了...")
    }
    
    func return_defer() int {
    	defer deferFunc()
    	return returnFunc()
    }
    
    func main() {
    	return_defer()
    }
    
    // 执行结果
    return 执行了...
    defer 执行了...
    

defer

  1. defer作用:在函数执行的过程中遇到defer + 表达式,先进栈,当函数中所有逻辑执行完,在函数结束出栈一次执行。

    代码示例

    package main
    
    import "fmt"
    
    func test01_defer() {
    	fmt.Println("test01_defer ...")
    }
    
    func test02_defer() {
    	fmt.Println("test02_defer ...")
    }
    
    func main() {
    	defer test01_defer()  // test01_defer进栈
    	defer test02_defer()  // test01_defer进栈
    	fmt.Println("main ...")
    	// 在函数结束前出栈
    }
    
    // 结果
    main ...
    test02_defer ...
    test01_defer ...
    
  2. defer在return后执行(defer在函数所有代码执行完,结束前执行,return仍然是函数中的一部分)

    代码示例

    package main
    
    import "fmt"
    
    func returnFunc() int {
    	fmt.Println("return 执行了...")
    	return 0
    }
    
    func deferFunc() {
    	fmt.Println("defer 执行了...")
    }
    
    func return_defer() int {
    	defer deferFunc()
    	return returnFunc()
    }
    
    func main() {
    	return_defer()
    }
    
    // 执行结果
    return 执行了...
    defer 执行了...
    

array(数组)

静态数组

  1. 静态数组就是长度固定的数组;

  2. 代码示例

    package main
    
    import (
    	"fmt"
    )
    
    func array(){
    	// 定义一个长度为10的静态数组,默认值为0
    	var array01 [10]int
    	for i:=0; i < len(array01); i++ {
    		fmt.Println(array01[i])
    	}
    	fmt.Println("*****************")
    	// 定义一个静态数组
    	array02 := [10]int{1, 2, 3}
    	for idx, val := range array02 {
    		fmt.Println(idx, val)
    	}
    	fmt.Println("+++++++++++++++++++")
    }
    
    func printArray(array [10]int){
    	array[0] = 110
    	// 静态数组作为参数,为值传递,且参数类型一致
    	for _, val := range array{  // _:表示匿名变量,只用来占位,不参与程序运行
    		fmt.Println(val)
    	}
    	fmt.Println("++++++++++++++++++++")
    }
    
    
    func main(){
    	array()
    	// 静态数组作为参数
    	array03 := [10]int{1,2,3,4,5}
    	printArray(array03)
    	fmt.Println("------------------------")
    	for _, val := range array03{
    		fmt.Println(val)
    	}
    }
    
  3. 注意事项

    • 静态数组的默认值为0;
    • 静态数组作为参数传递时,传递的是
    • 静态数组作为参数传递时,实参类型和形参类型一致(即长度一致);

动态数组(slice切片)

  1. 动态数组就是长度可变的数组;

  2. 代码示例

    package main
    
    import "fmt"
    
    // 遍历(动态)数组
    func test01_slice(array []int)  {
    	array[0] = 110
    	for _, val := range array{
    		fmt.Println(val)
    	}
    	fmt.Println("++++++++++++++++++")
    }
    
    func main()  {
    	// 定义一个动态数组
    	array01 := []int{1,2,3,4}
    	test01_slice(array01)  // 动态数组作为参数传递时传递的是引用
    
    	for _, val := range array01{
    		fmt.Println(val)
    	}
    }
    
  3. 注意事项

    • 动态数组作为参数传递时,传递的是引用
    • 动态数组作为参数时不需要考虑类型(长度)。
切片声明的四种方式
package main

import "fmt"

func main() {
	// 切片的四种声明方式
	// 方式一:声明slice1是一个切片,并且初始化,默认值是1,2,3。 长度len是3
	slice01 := []int{1, 2, 3}
	fmt.Println(slice01)
	fmt.Println("+++++++++++++++++++++")

	// 方式二:
	var slice02 []int  // 声明slice02是一个切片,并没有给slice02分配空间
	slice02 = make([]int, 3)  // 给slice02开辟3个空间,默认值为0
	fmt.Println(slice02)
	fmt.Println("+++++++++++++++++++++")

	// 方式三:声明slice03是一个切片,并且开辟3个空间,默认值为0
	var slice03 []int = make([]int, 3)
	fmt.Println(slice03)
	fmt.Println("+++++++++++++++++++++")

	// 方式四(常用):声明slice1是一个切片,同时给slice分配空间,3个空间,初始化值是0, 通过:=推导出slice是一个切片
	slice04 := make([]int, 3)
	fmt.Println(slice04)
	fmt.Println("+++++++++++++++++++++")
}
切片的容量
  1. 切片的长度(len)表示左指针和右指针之间的距离(切片实际的元素的数量)

  2. 切片的容量(cap)表示左指针到底层数组末尾的距离(切片所能容纳元素的数量)
    在这里插入图片描述

切片的扩容机制

切片在append时,如果长度超过了容量,容量将增减为原来的2倍

代码示例

package main

import fmt "fmt"

func main() {
	// 声明一个切片,长度为3,容量为5
	var slice01 = make([]int, 3, 5)
	fmt.Printf("length = %d, cap = %d, slice = %v\n", len(slice01), cap(slice01), slice01)
	// 追加一个元素
	slice01 = append(slice01, 10)
	// 追加第二个元素
	slice01 = append(slice01, 20)
	fmt.Printf("length = %d, cap = %d, slice = %v\n", len(slice01), cap(slice01), slice01)
	// 追加第三个元素
	slice01 = append(slice01, 30)
	fmt.Printf("length = %d, cap = %d, slice = %v\n", len(slice01), cap(slice01), slice01)
	fmt.Println("++++++++++++++++++++++++++++")

	var slice02 = make([]int, 2)  // 如果不指定容量,容量默认和长度相同
	fmt.Printf("length = %d, cap = %d, slice = %v\n", len(slice02), cap(slice02), slice02)
	slice02 = append(slice02, 100)
	fmt.Printf("length = %d, cap = %d, slice = %v\n", len(slice02), cap(slice02), slice02)
	slice02 = append(slice02, 101)
	fmt.Printf("length = %d, cap = %d, slice = %v\n", len(slice02), cap(slice02), slice02)
	slice02 = append(slice02, 102)
	fmt.Printf("length = %d, cap = %d, slice = %v\n", len(slice02), cap(slice02), slice02)
}

// 打印结果
length = 3, cap = 5, slice = [0 0 0]
length = 5, cap = 5, slice = [0 0 0 10 20]
length = 6, cap = 10, slice = [0 0 0 10 20 30]
++++++++++++++++++++++++++++
length = 2, cap = 2, slice = [0 0]
length = 3, cap = 4, slice = [0 0 100]
length = 4, cap = 4, slice = [0 0 100 101]
length = 5, cap = 8, slice = [0 0 100 101 102]
切片的截取
  1. 截取是一种引用
  2. copy(s1, s0):将s0中的元素一次拷贝到s1中

代码示例

package main

import "fmt"

func main()  {
	// 截取(即python中的切片):切片是一种引用
	s0 := []int{1, 2, 3}  // [1, 2, 3]
	fmt.Println(s0)
	s1 := s0[0:2]  // [1, 2]
	fmt.Println(s1)
	// 修改
	s0[0] = 100  // [100, 2, 3]
	fmt.Println(s0)  // [100, 2, 3]
	fmt.Println(s1)  // [100, 2]
	fmt.Println("===========================")

	// copy
	s3 := make([]int, 3)  // [0, 0, 0]
	copy(s3, s0) // 将s0中的值 依次拷贝到s3中
	fmt.Println(s3)  // [100, 2, 3]
}

map(字典)

map的定义方式

  1. map的三种声明方式如下

    package main
    
    import "fmt"
    
    func main() {
    	// 声明map有三种方式
    	// 方式一:
    	var map01 map[string]string  // 声明一个map01是一种map类型,key为string类型,value也是string类型,默认为map为空
    	if map01 == nil {
    		fmt.Println("map01 为空map")
    	}
    	map01 = make(map[string]string, 10)  // 给map01开辟容量为10的空间(扩容机制和array相同)
    	map01["name"] = "TongTianZi"  // 给map01添加元素
    	map01["age"] = "18"
    	map01["gender"] = "women"
    	fmt.Println(map01)
    	fmt.Println("====================")
    
    	// 方式二:不指定容量,表示容量为任意个
    	map02 := make(map[string]string)
    	map02["first"] = "python"
    	map02["second"] = "go"
    	map02["third"] = "java"
    	fmt.Println(map02)
    	fmt.Println("====================")
    
    	// 方式三:声明一个map,并进行初始化
    	map03 := map[int]string{
    		1: "python",
    		2: "go",
    		3: "java",
    	}
    	fmt.Println(map03)
    }
    
  2. 注意事项

    • map的扩容机制和array相同;
    • 一般不知道map的内容时使用第二种方式,知道map内容的情况下使用第三种方式。

map的使用

  1. 代码示例(增删改查)

    package main
    
    import "fmt"
    
    func main()  {
    	// 声明一个map
    	map0 := make(map[string]string)
    	// 添加数据
    	map0["陕西"] = "西安"
    	map0["湖南"] = "长沙"
    	map0["江西"] = "南昌"
    
    	// 遍历
    	for key, value := range map0{
    		fmt.Printf("%s = %s\n", key, value)
    	}
    	fmt.Println("===================")
    
    	// 修改
    	map0["陕西"] = "长安"
    
    	// 函数遍历
    	forMap(map0)
    
    	// 删除
    	delete(map0, "陕西")
    	forMap(map0)
    
    }
    
    func forMap(myMay map[string]string)  {
    	// map作为参数,传递的是引用(指针/内存地址值)
    	for key, value := range myMay{
    		fmt.Printf("%s = %s\n", key, value)
    	}
    	fmt.Println("===================")
    }
    
  2. 注意事项

    • map作为参数,传递的是引用(指针/内存地址值)

面向对象

结构体

  1. 结构体:自定义的数据类型;

  2. 代码示例

    package main
    
    import "fmt"
    
    // 声明一种新的数据类型myInt,是int的别名
    type myInt int
    
    
    // 定义一个结构体
    type Book struct {
    	title string
    	author string
    }
    
    func changeBook0(book Book){
    	book.title = "活着"
    	book.author = "余华"
    	fmt.Printf("书名:%s, 作者:%s\n", book.title, book.author)
    	fmt.Println("==========================")
    }
    
    
    func changeBook1(book *Book){
    	book.title = "白鹿原"
    	book.author = "陈忠实"
    	fmt.Printf("书名:%s, 作者:%s\n", book.title, book.author)
    	fmt.Println("==========================")
    }
    
    func main()  {
    	// myInt
    	var num01 myInt = 100
    	fmt.Println(num01)
    	fmt.Printf("%T\n", num01)
    	fmt.Println("==========================")
    
    	// 结构体的使用
    	var book0 Book
    	book0.title = "平凡的世界"
    	book0.author = "路遥"
    	fmt.Printf("书名:%s, 作者:%s\n", book0.title, book0.author)
    	fmt.Println("==========================")
    
    	// 结构体作为参数传递的一个副本(值传递)
    	changeBook0(book0)
    	fmt.Printf("书名:%s, 作者:%s\n", book0.title, book0.author)
    	fmt.Println("==========================")
    
    	// 如果要把结构体的地址值作为参数传递,需要指明形参为指针类型,实参传递地址值
    	changeBook1(&book0)
    	fmt.Printf("书名:%s, 作者:%s\n", book0.title, book0.author)
    	fmt.Println("==========================")
    }
    
  3. 注意事项

    • 结构体作为参数传递的一个副本(值传递)
    • 如果要把结构体的地址值作为参数传递,需要指明形参为指针类型,实参传递地址值

封装

  1. go中类的实现其实是给结构体绑定方法。

  2. 示例代码

    package main
    
    import (
    	"fmt"
    )
    
    // 定义一个Person的结构体
    //如果类名首字母大写,表示其他包也能够访问
    type Person struct {
    	//如果说类的属性首字母大写, 表示该属性是对外能够访问的,否则的话只能够类的内部访问
    	Name   string
    	age    int
    	Gender string
    }
    
    // 给结构体绑定方法
    
    /*
    func (this Person) Show() {
    	fmt.Printf("name = %s, age = %d, gender = %s\n", this.Name, this.age, this.Gender)
    	fmt.Println("==================")
    }
    
    func (this Person) GetInfo() (string, int, string) {
    	return this.Name, this.age, this.Gender
    }
    
    func (this Person) SetInfo(name string, age int, gender string) {
    	this.Name = name
    	this.age = age
    	this.Gender = gender
    }
    */
    
    // 方法名首字母大写表示对外开放(即其他包中也能调用),否则仅限于本包中使用
    func (this *Person) Show() {
    	// this表示调用该方法的对象的副本(拷贝)
    	fmt.Printf("name = %s, age = %d, gender = %s\n", this.Name, this.age, this.Gender)
    	fmt.Println("==================")
    }
    
    func (this *Person) GetInfo() (string, int, string) {
    	return this.Name, this.age, this.Gender
    }
    
    func (this *Person) SetInfo(name string, age int, gender string) {
    	this.Name = name
    	this.age = age
    	this.Gender = gender
    }
    
    func main() {
    	// 创建一个对象
    	p := Person{Name: "TongTianZi", age: 18, Gender: "women"}
    	p.Show()
    	// 修改属性
    	p.SetInfo("高圆圆", 20, "女")
    	p.Show()
    }
    
  3. 注意事项

    • 方法参数传递仅仅是值(副本)传递,要想传递地址值需要指明参数为指针类型;
    • 类、方法名、属性首字母大写表示对外(其他包)可以访问,否则只能在本包中访问。

继承

  1. go中继承是通过结构体嵌套实现的;

  2. 代码示例

    package main
    
    import (
    	"fmt"
    )
    
    // 定义一个Animal类
    type Animal struct {
    	Color string
    }
    
    func (this *Animal) Eat(){
    	fmt.Println("eating...")
    }
    
    func (this *Animal) walk(){
    	fmt.Println("walking...")
    }
    
    type Dog struct {
    	Animal  // 表示Dog类继承了Animal(结构体嵌套)
    	Age int 
    	Nick string
    }
    
    func (this *Dog) Eat(){  // 重写父类的方法
    	fmt.Println("dog is eating ...")
    }
    
    func (this *Dog) Shout()  {  // 子类新增方法
    	fmt.Println("Dog wang wang ...")
    }
    
    func main()  {
    	// 创建父类对象
    	a := Animal{Color: "red"}
    	fmt.Println(a.Color)
    	a.Eat()
    	fmt.Println("========================")
    
    	// 创建子类对象
    	d := Dog{Animal{Color: "black"}, 18,  "XiaoHei"}
    	d.Shout()
    	fmt.Println("========================")
    	// 或者
    	var d1 Dog
    	d1.Color = "black"
    	d1.Age = 2
    	d1.Nick = "XiaoHei"
    	d1.Eat()
    }
    
  3. 注意事项

    • 通过子类创建对象时,父类可以指定属性名,子类不需要指定属性名

多态

  1. 多态:同一个方法的不同表现形式(例如:同样eat(),dog对象调用时表示dog吃东西,cat调用时表示cat吃东西)

  2. go实现多态的方式

    • 有一个父类(接口:即抽象方法的集合)

    • 子类(继承)实现父类中所有的抽象方法

    • 父类数据类型标量指向(引用)子类数据类型变量

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-05bcd8I6-1617288175781)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210328231932028.png)]

  3. 代码示例

    package main
    
    import (
    	"fmt"
    )
    
    // 父类(接口),本质是一个指针
    type AnimalIF interface {
    	// 接口就是抽象方法的集合
    	// 声明抽象方法
    	Sleep()
    	GetColor() string
    	GetType() string
    }
    
    // 声明一个Dog子类
    type Pig struct {
    	// 继承Animal类(可省略不写)
    	Color string
    }
    
    // 实现父类的所有抽象方法
    func (this *Pig) Sleep()  {
    	fmt.Println("Pig is sleeping ...")
    }
    
    func (this *Pig) GetColor() string {
    	return this.Color
    }
    
    func (this *Pig) GetType() string {
    	return "Dog"
    }
    
    // 声明一个Cat子类
    type Cat struct {
    	// 继承Animal类(可省略不写)
    	Color string
    }
    
    // 实现父类的所有抽象方法
    func (this *Cat) Sleep()  {
    	fmt.Println("Dog is sleeping ...")
    }
    
    func (this *Cat) GetColor() string {
    	return this.Color
    }
    
    func (this *Cat) GetType() string {
    	return "Cat"
    }
    
    func Show(animal AnimalIF)  {  // 接口本质是一个指针
    	animal.Sleep()
    	fmt.Println(animal.GetColor())
    	fmt.Println(animal.GetType())
    	fmt.Println("========================")
    }
    
    // main方法
    func main()  {
    	// 声明一个父类类型对象
    	var animal AnimalIF
    	animal = &Pig{Color: "Black"}   // 接口本质是指针,故需要传递地址值
    	animal.Sleep()
    	fmt.Println(animal.GetColor())
    	fmt.Println(animal.GetType())
    	fmt.Println("========================")
    	animal = &Cat{Color: "yellow"}
    	animal.Sleep()
    	fmt.Println(animal.GetColor())
    	fmt.Println(animal.GetType())
    	fmt.Println("========================")
    
    	// 或者
    	Show(&Cat{Color: "yellow"})  // 需要传递引用
    	Show(&Pig{Color: "black"})
    }
    

万能类型

  1. interface{}空接口,也是go中的通用类型,int, string, float32, float64等都实现了interface{}的方法,因此就可以用interface{}类型引用任意的数据类型。

  2. 类型断言:interface{}提供了类型断言,用于判断是否是指定的类型。

  3. 代码示例

    package main
    
    import (
    	"fmt"
    )
    
    // interface{}:通用万能类型
    func test_interface(args interface{}){
    	fmt.Println(args)
    
    	// 通过断言判断args到底是什么类型
    	val, isString:= args.(string)  // 判断args是否是string类型
    	if isString {
    		fmt.Printf("%s type is %T\n", val, val)
    	}else {
    		fmt.Printf("%s type is %T\n", val, val)
    	}
    	fmt.Println("==================")
    }
    
    type Book01 struct {
    	BookName string
    	Author string
    }
    
    func main()  {
    	// 万能类型测试
    	test_interface(10)
    	test_interface("haha")
    
    	b := Book01{BookName: "傻吊英雄传", Author: "金庸"}
    	test_interface(b)
    }
    

reflect(反射)

变量的结构

变量的结构:type + value
在这里插入图片描述

反射

获取封装好的变量的结构(类型和值)

  • reflect.Valueof(val interface{}:获取接口参数的数据值,如果接口为空,返回0
  • reflect.Typeof(val interface{}):获取接口参数的类型,如果接口为空,返回nil

结构体标签

  1. 结构体标签:标签就是对结构体属性的补充说明;
  2. 结构体标签的定义方式
package main

import (
    "fmt"
    "reflect"
)

type Person struct {
    // 标签就是对结构体属性的补充说明
    Name string `info:"姓名" nick:"Yang"`
    Age int `info:"year"`
}

func FindTag(args interface{}){
    els := reflect.TypeOf(args).Elem()  // 获取结构体所有元素
    for i := 0; i < els.NumField(); i++ {
        j := els.Field(i).Tag.Get("info")  // 获取第i行属性的标签
        k := els.Field(i).Tag.Get("nick")
        fmt.Println("info = ", j, "nick = ", k)
    }
}


func main(){
    var p Person
    FindTag(&p)
}

json编解码(序列化和反序列化)

  1. 编码(序列化):结构体——>json
  2. 解码(反序列化):json——>结构体
  3. 代码示例
package main

import (
    "encoding/json"
    "fmt"
)

type Movie struct{
    Title string    `json:"电影名字"`  // 该属性在json格式的字符串中显示"电影名字"
    TicketPrice int `json:"票价"`
    Actors []string `json:"演员表"`
}


func main(){
    // 声明一个对象
    movie := Movie{"泰坦尼克号", 20, []string{"Jack", "Rose"}}
    // 编码 结构体——>json (序列化)
    jsonStr, err := json.Marshal(movie)
    if err != nil {
        fmt.Println("序列化出错!")
        return
    }
    fmt.Printf("jsonStr = %s\n", jsonStr)

    // 解码 json——>结构体 (反序列化)
    myMovie := Movie{}
    err = json.Unmarshal(jsonStr, &myMovie)
    if err != nil {
        fmt.Println("反序列化错误!")
    }
    fmt.Printf("myMovie = %s\n", myMovie)
}

ORM映射关系

goroutine(协成)

  1. 协成:又叫微线程,相比于线程消耗的资源更少,效率更高。
  2. 使用go关键字开启一个协成
    代码示例
package main

import (
	"fmt"
	"time"
)

func GoroutineTest(){
    for {  // 不写控制条件就是死循环
        fmt.Println("Goroutine running...")
        time.Sleep(1 * time.Second)
    }
}

func main(){
    // 协成(goroutine):又名微线程,相比于线程,消耗资源更少
    go GoroutineTest()  // 使用go关键字开启一个协成

    for {
        fmt.Println("main running...")
        time.Sleep(1 * time.Second)
    }
}

# 测试效果
main running...
Goroutine running...
main running...
Goroutine running...
main running...
Goroutine running...
......
  1. 协成依赖于主线程,主线程结束协成也会结束,可以使用runtime.Goexit()退出当前的协成。
    代码示例
package main

import (
    "fmt"
    "runtime"
)

func main(){
    // go开启一个协程序
    go func(){  // 定义一个匿名函数
        defer fmt.Println("defer A...")
        func(){
            defer fmt.Println("defer B...")
            // return "B"不会被打印,"A"还是会被打印  相当于continue
            runtime.Goexit()  // 退出当前goroutine,"B"和"A"都不会被打印  相当于break
            fmt.Println("B")
        }()
        fmt.Println("A ")
    }()  // 调用匿名函数

    fmt.Println("main running ...")
}

channel(管道)

  1. channel:中文叫管道,用于协程之间的通信。
    在这里插入图片描述

  2. channel的使用
    在这里插入图片描述

package main

import "fmt"

func main(){
    // 声明一个channel
    c01 := make(chan int)

    // goroutine1
    go func(){
        defer fmt.Println("goroutine1 end ...")
        fmt.Println("goroutine1 start ...")
        c01 <- 666 // 将666发送到c01中
    }()

    // goroutine2
    go func(){
        defer fmt.Println("goroutine2 end ...")
        fmt.Println("goroutine2 start ...")
        c02 := <- c01  // 从c01中读取666
        fmt.Println(c02)
    }()

    fmt.Println("main end ...")
}

// 测试结果
goroutine2 start ...
goroutine1 start ...
goroutine1 end ...
main end ...
666
goroutine2 end ...
  1. channel在goroutine中的通信过程
    在这里插入图片描述
    由于main go和sub go的执行顺序是随机的,因此我们并不知道是会先执行”读取“还是先执行”写入“
  • 假如先执行写入操作
    sub go执行写入操作后会阻塞,直到main go执行读取操作后sub go才会解阻塞,继续往下执行。
  • 假如先执行读取操作
    main go执行读取操作时发现channel中没有内容,会阻塞,直到sub go往channel中写入一个值后,main go才会解阻塞,执行读取操作。

无缓冲的channel

在这里插入图片描述

有缓冲的channel

  1. 示意图
    在这里插入图片描述
  2. 特点:
  • 当channel已经满,在向里面写数据,就会阻塞
  • 当channel为空,从里面取数据也会阻塞
  1. 代码示例
package main

import "fmt"

func main(){
    c01 := make(chan int, 3) // 初始化带有缓冲的channel
    fmt.Println("c01 = ", c01, ", len() = ", len(c01), ", cap() = ", cap(c01))

    // write
    go func() {
        defer fmt.Println("write end")
        for i:=0; i < cap(c01); i++ {
            c01 <- i
            fmt.Println("write in ", i)
        }
    }()

    // read
    go func() {
        defer fmt.Println("read end")
        for i:=0; i<cap(c01); i++ {
            c02 := <-c01
            fmt.Println("read in ", c02)
        }
    }()

    fmt.Println("main end")
}

关闭channel

  1. 代码示例
package main

import "fmt"

func main(){
    // 声明一个channel
    c := make(chan int)

    go func() {
        defer fmt.Println("goroutine end")
        for i:=0; i<5; i++{
            c <- i
        }
        close(c)  // 关闭channel
    }()

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

    fmt.Println("main end")
}
  1. 注意事项
    (1)channel不像⽂件⼀样需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显式的结束range循环之类的,才去关闭channel;
    (2)关闭channel后,⽆法向channel 再发送数据(引发 panic 错误后导致接收⽴即返回零值);
    (3)关闭channel后,可以继续从channel接收数据;
    (4)对于nil channel,⽆论收发都会被阻塞。

channel与range

可以使用range不断从channel中取数据

package main

import "fmt"

func main(){
    // 声明一个channel
    c := make(chan int)

    go func() {
        defer fmt.Println("goroutine end")
        for i:=0; i<5; i++{
            c <- i
        }
        close(c)  // 关闭channel
    }()

    /*for {
        // ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
        if data, ok := <-c; ok{
            fmt.Println(data)
        } else {
            break
        }
    }*/

    // 可以使用range不断迭代从channel中取出数据
    for data := range c {
        fmt.Println(data)
    }

    fmt.Println("main end")
}

channel与select

单流程下⼀个go只能监控⼀个channel的状态,select可以实现多路channel的状态监控。
在这里插入图片描述
代码示例

package main

import (
    "fmt"
    "time"
)


func WriteInto(c01, c02 chan int) {
    fmt.Println("开始写入数据...")
    for i:=0; i<10; i++{
        c01 <- i
    }
    time.Sleep(1 * time.Second)
    c02 <- 10
}

func main(){
    // 声明两个channel
    c01 := make(chan int)
    c02 := make(chan int)

    // 开启一个协成,往c01中写数据
    go WriteInto(c01, c02)

    for {
        select {
            case <-c01:  // 监控c01中是否有数据,如果有数据,就从c01中取出来,添加到c02中
                fmt.Println(<-c01)
            case <-c02:
                return
        }
    }

    fmt.Println("main end ...")
}

p33?

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值