快速转go(基本语法篇)

目录

1、window安装go环境

2、基础程序,打印hello,world

3、声明变量的4种方式

4、const常量

5、函数

6、自定义go包

7、指针 

 8、defer函数(类似C++中的析构函数)

 9、数组(array)和动态数组(slice切片)

10、map类型(类似python的dict类型,key:value)

11、struct结构体

12、pair数据类型

13、 reflect反射

14、goroutine协程

15、channel(管道),用于协程之间的通信

16、go modules模式

17、go命令

18、go不支持的语法

19、go中比较有特色的语法 


比较适合已经有过一门比较熟悉的编程语言的人阅读

1、window安装go环境

1. 下载window安装包

2. 配置window下的环境变量,在path中进行编辑,加入go包存放的位置,例如我放在D盘,所以window环境变量为:

 3. 配置GOPATH,该路径为go工程存放的目录

 4. 配置自己喜爱的编辑器ide,这里推荐goland(收费的),vscode(免费的)。 

5. go目录下的src目录,该目录下用于项目的源码。

6. go中文说明文档

2、基础程序,打印hello,world

package main

import (
	"fmt"
	"time")

func main() {
	fmt.Println("Hello, World!")
	time.Sleep(1 * time.Second)
}

3、声明变量的4种方式

  1. 常用的方式:":="的方式最常用,用于推测值为什么类型,变量就定为什么类型;
  2. ":="的方式不能用于全局变量的声明,变量的初始值为0;
// 四种变量声明方式
package main

import "fmt"

// 声明全局变量的时候,方法一、二、三是可以的
var gA int = 100
var gB = 200

// 方法四:=声明,只能用在函数里声明

func main() {
	// 方法一:声明变量,默认值为0
	var a int
	fmt.Println("a=", a)
	fmt.Printf("type of a = %T\n", a)

	// 方法二:声明一个变量,初始化值
	var b int = 100
	fmt.Println("b = ", b)
	fmt.Printf("type of b = %T\n", b)

	// 方法三,省去数据类型,自己匹配变量类型
	var c = 100
	fmt.Println("c = ", c)
	fmt.Printf("type of c = %T\n", c)

	// 方法四:省略var关键字,自动匹配类型
	e := 100.0
	fmt.Println("e = ", e)
	fmt.Printf("type is %T\n", e)

	// 打印全局
	fmt.Println("gA = ", gA, "gB = ", gB)

	// 声明多个变量
	var xx, yy int = 100, 200
	fmt.Println(xx, yy)

	var cc, dd = "hello", "hadoop"
	fmt.Println(cc, dd)

	aa, bb := "hello", 12
	fmt.Println(aa, bb)

	// 多行的多变量声明
	var (
		vv int  = 100
		jj bool = true
	)
	fmt.Println(vv, jj)
}

4、const常量

  1. const关键字用于声明常量,只读,不可修改;
  2. go中的const还有一个iota关键字,用于定义枚举,第一行iota默认为0,逐下+1
package main

import "fmt"

// const 来定义枚举
const (
	// 可以在const()添加一个关键字,每行的iota都会累加1,第一行iota默认值为0,iota只能用于const中使用
	BEIJIN = iota * 10
	SHANGHAI
	SHENZHENG
	CD = iota + 1
	JD
	TB
)

func main() {
	// 常量,只读属性
	const lenght int = 100

	fmt.Println("lenght = ", lenght)

	fmt.Println(BEIJIN)    // 0
	fmt.Println(SHANGHAI)  // 10
	fmt.Println(SHENZHENG) // 20
	fmt.Println(CD)        // 4
	fmt.Println(JD)        // 5
	fmt.Println(TB)        // 6
}
package main

import "fmt"

// go中没有枚举类型,但是我们可以使用const + iota(常量累加器)进行模拟

const (
	MONDAY = iota
	TUESDAY
	WEDNESDAY
	THURSDAY
	FRIDAY
	SATURDAY
	SUNDAY
	M, N = iota, iota // const为预编译,所有不用:=
)

/*
1、 iota是常量组计数器
2、iota从0开始,每一次换行+1
3、如果不赋值,默认和上一行表达式相同
4、如果在同一行出现两个iota,那么值相同
5、如果遇到const, iota会重新清零
*/
func main12() {
	fmt.Println(SUNDAY) // 6
	fmt.Println(M, N)
}

5、函数

package main

import "fmt"

// go中,禁止函数使用默认参数
// 变量类型写在变量后面,函数名后面写类型,表示函数返回的类型
func fool(a string, b int) int {
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)

	c := 100

	return c
}

// 匿名返回值
func foo2(a string, b int) (int, int) {
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)

	c, d := 100, 222

	return c, d
}

// 有名返回值
func foo3(a string, b int) (r1 int, r2 int) {
	fmt.Println("--------foo3--------")
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)
	// 如果r1,r2没有赋值,则会默认初始化为0
	// r1, r2 = 100, 222

	return
}

// 有名返回值,返回类型都相同
func foo4(a string, b int) (r1, r2 int) {
	fmt.Println("--------foo4--------")
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)

	r1, r2 = 100, 222

	return
}

func main() {
	c := fool("aa", 123)
	fmt.Println(c)
	/*
		a =  aa
		b =  123
		100
	*/

	d, e := foo2("abc", 321)
	fmt.Println("foo2 is ", d, e)
	/*
		a =  abc
		b =  321
		foo2 is  100 222
	*/

	r1, r2 := foo3("aaa", 89)
	fmt.Println(r1, r2)
	/*
		--------foo3--------
		a =  aaa
		b =  89
		0 0
	*/

	r3, r4 := foo4("aaa", 89)
	fmt.Println(r3, r4)
	/*
		--------foo4--------
		a =  aaa
		b =  89
		100 222
	*/
}

6、自定义go包

注意点:自定义的go包要放入go安装目录下,src目录下(展示了解到这些,还需继续学习)

自定义lib1:

package lib1

import "fmt"

// 当前lib1包提供的API
func Lib1Test() {
	fmt.Println("lib1Test().....")
}

func init() {
	fmt.Println("lib1 init() 被执行了")
}

自定义lib2:

package lib2

import "fmt"

// 当前lib2包提供的API
func Lib2Test() {
	fmt.Println("lib2Test().....")
}

func init() {
	fmt.Println("lib2 init() 被执行了")
}

 1. 导包成功自动调用初始化init函数

package main

// 因为自定义的包,要放入go的src目录下,才能调用成果
// go下的src,用于存放go的包
import (
	"my_go/lib1"
	"my_go/lib2"
)

func main() {
	lib1.Lib1Test()
	lib2.Lib2Test()
}

/*
执行结果
lib1 init() 被执行了
lib2 init() 被执行了
lib1Test().....
lib2Test().....

(1)包内的函数名开头大写,才是对外开放的方法
(2)包内的函数名开头小写,才是对外不开放的方法,只能在包本身内调用
*/

2. 导包的方式(匿名,起别名,.导包)

package main

// 因为自定义的包,要放入go的src目录下,才能调用成果
// go下的src,用于存放go的包
import (
	// 匿名导包,因为在go中,导包,如果不使用则会报错,匿名导包可以解决这个问题,但是不能使用包内的方法
	_ "my_go/lib1"
	// "my_go/lib2"
	// 给包起别名
	// mylib "my_go/lib2"
	// 用.导包,可以直接使用包中的方法
	. "my_go/lib2"
)

func main() {
	// lib1.Lib1Test()
	// 别名包调用方法
	// mylib.Lib2Test()

	Lib2Test()

}

/*
执行结果
lib1 init() 被执行了
lib2 init() 被执行了
lib2Test().....
*/

7、指针 

package main

import "fmt"

// 指针类型
func swap(a *int, b *int) {
	var tmp int
	tmp = *a
	*a = *b
	*b = tmp
}

func main() {
	var a int = 10
	var b int = 20

	// 传入地址
	swap(&a, &b)
	fmt.Println("a = ", a, "b = ", b)
}

 8、defer函数(类似C++中的析构函数)

  1. defer函数会在一个函数生命周期结束后,自动调用
  2. 执行顺序:类似栈,入栈和出栈的顺序
package main

import "fmt"

func main() {
	// defer关键字,类似析c++构函数,在函数执行结束后,自动触发调用
	// 调用顺序,和栈一样,先进后出
	defer fmt.Println("main end1")
	defer fmt.Println("main end2")

	fmt.Println("main:hello go1")
	fmt.Println("main:hello go2")

	/*
		执行结果

		main:hello go1
		main:hello go2
		main end2
		main end1
	*/
}

 3. return表示函数,所以return结束后,才会调用defer

// return 和 defer谁先调用
// defer会在函数生命周期完全结束后,才会调用
package main

import "fmt"

func deferFunc() int {
	fmt.Println("defer func called....")
	return 0
}

func returnFunc() int {
	fmt.Println("return func called....")
	return 0
}

func returnAndDefer() int {
	defer deferFunc()

	return returnFunc()
}

func main() {
	returnAndDefer()
}

/*
执行结果

return func called....
defer func called....
*/

 9、数组(array)和动态数组(slice切片)

1. 固定长度的数组,在函数里发生传递后,只会发生值拷贝,而不是传递引用;

package main

import "fmt"

func printArray(myArray [4]int) {
	// 固定的数组,只会发生值拷贝,这里的修改不会影响myArray原本的值
	for index, value := range myArray {
		fmt.Println("index is ", index, "value is ", value)
	}
	myArray[0] = 99
}

func main() {
	// 固定长度的数组
	var myArray [10]int
	for i := 1; i < len(myArray); i++ {
		fmt.Println(myArray[i])
	}
	/*
		0
		0
		0
		0
		0
		0
		0
		0
		0
	*/
	myArray2 := [10]int{1, 2, 3, 4}
	for index, value := range myArray2 {
		fmt.Println("index = ", index, "value = ", value)
	}
	/*
		index =  0 value =  1
		index =  1 value =  2
		index =  2 value =  3
		index =  3 value =  4
		index =  4 value =  0
		index =  5 value =  0
		index =  6 value =  0
		index =  7 value =  0
		index =  8 value =  0
		index =  9 value =  0
	*/
	// 查看数组的数据类型
	fmt.Printf("myArray1 types = %T\n", myArray)
	fmt.Printf("myArray2 types = %T\n", myArray2)
	/*
		myArray1 types = [10]int
		myArray2 types = [10]int
	*/
	myArray3 := [4]int{11, 22, 33, 44}
	printArray(myArray3)
	/*
		index is  0 value is  11
		index is  1 value is  22
		index is  2 value is  33
		index is  3 value is  44
	*/
	fmt.Println("-----------")
	printArray(myArray3)
	/*
		-----------
		index is  0 value is  11
		index is  1 value is  22
		index is  2 value is  33
		index is  3 value is  44
	*/
}

2. 动态数组,也就是不先声明数组长度,使用":="进行推导,发生函数的参数传递时,传递的是引用;

package main

import "fmt"

func printArray(myArray []int) {
	// _表示匿名,也就是无法访问
	for _, value := range myArray {
		fmt.Println("value is ", value)
	}
	// 动态数组会进行,引用传递
	myArray[0] = 100
}

func main() {
	// 动态数组,切片(slice类型)
	myArray := []int{1, 2, 3, 4}
	fmt.Printf("type is %T\n", myArray)
	printArray(myArray)
	/*
		type is []int
		value is  1
		value is  2
		value is  3
		value is  4
	*/
	fmt.Println("---------")
	printArray(myArray)
	/*
		---------
		value is  100
		value is  2
		value is  3
		value is  4
	*/
}

 3. 动态数组的声明方式(4种)

package main

import "fmt"

func main() {

	// 方法一,声明slice是一个切片,并且初始化,默认值为1,2,3,长度len是3
	// slice1 := []int{1, 2, 3}

	// 方法二,声明slice是一个切片,但是并没给slice分配空间
	var slice1 []int
	// slice1[0] = 1 会报错,因为slice1没有空间
	// 开辟空间
	// slice1 = make([]int, 3)

	// 方法三,
	// var slice1 []int = make([]int, 3)

	// 方法四,:=自动推到出
	// slice1 := make([]int, 3)

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

	// 判断一个slice是否为空
	if slice1 == nil {
		fmt.Println("slice1 是空的")
	} else {
		fmt.Println("slice1 是有空间的")
	}

}

4. 动态数组的追加元素和扩容

package main

import "fmt"

func main() {
	var numbers = make([]int, 3, 5)

	// cap为容量, len = 3, cap = 5, slice = [0 0 0]
	fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)

	// 追加元素1, len = 4, cap = 5, slice = [0 0 0 1]
	numbers = append(numbers, 1)
	fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)

	// 追加元素2, len = 5, cap = 5, slice = [0 0 0 1 1]
	numbers = append(numbers, 1)
	fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)

	// 空间满后继续追加元素,slice自动扩容,会一次性扩容一个上一次cap的容量
	// len = 6, cap = 10, slice = [0 0 0 1 1 3]
	numbers = append(numbers, 3)
	fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)

	var numbers2 = make([]int, 3)
	// len = 3, cap = 3, slice = [0 0 0],不声明cap,自动把len定为cap
	fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers2), cap(numbers2), numbers2)
}

5. 数组的截取(类似python的切片)

package main

import "fmt"

func main() {
	s := []int{1, 2, 3} //len=3, cap=3

	// 数组的截取
	s1 := s[0:2]
	fmt.Printf("s1 is %v\n", s1) // s1 is [1 2]

	// 如果s1发生改变,s也会发生改变
	s1[0] = 100
	fmt.Printf("s1 is %v\n", s1) // s1 is [100 2]
	fmt.Printf("s is %v\n", s)   // s is [100 2 3]

	// copy函数,可以将底层的数组slice一起拷贝
	s2 := make([]int, 4) // s2 = [0,0,0]
	// 将s中的值依次拷贝到s2,,这种方法s和s2指向的是不同空间
	copy(s2, s)
	fmt.Printf("s2 is %v\n", s2) // s2 is [100 2 3 0]
}
package main

import "fmt"

func main8() {
	names := [7]int{1, 2, 3, 4, 5, 6, 7}

	// 基于names创建一个新数组

	// 切片可以基于一个数组,灵活的创建新的数组
	// 这种方式为浅拷贝,修改names1的时候names也会变
	names1 := names[:4]
	names1[0] = 100
	fmt.Println(names) // [100 2 3 4 5 6 7]

	// 如果想让切片完成独立,得使用深拷贝,使用copy()自由函数来完成
	// copy(目标数组变量(slice), 被拷贝的数组(slice))
	namesCopy := make([]int, len(names))
	copy(namesCopy, names[:])
	fmt.Println(namesCopy)

	// 截取方式也可以用于字符串
	// 5、创建空切片的时候,可以明确指定切片的容量, make(类型,长度,容量)
	names3 := make([]int, 0, 20)
	names3 = append(names3, 1)
	names3 = append(names3, 2)
	fmt.Println(names3)
}

10、map类型(类似python的dict类型,key:value)

package main

import "fmt"

func main() {

	// 方法一,map类似python中的字典
	// 声明myMap1是一种map类型,key是string,value是string
	var myMap1 map[string]string
	if myMap1 == nil {
		fmt.Println("myMap1是一个空")
	}
	// 使用make后,map类型可以自动扩容
	myMap1 = make(map[string]string, 1)
	myMap1["one"] = "java1"
	myMap1["two"] = "C++1"
	myMap1["three"] = "python1"
	fmt.Println(myMap1) // map[one:java1 three:python1 two:C++1]

	// 方法二
	myMap2 := make(map[int]string)
	myMap2[1] = "java"
	myMap2[2] = "C++"
	myMap2[3] = "python"
	fmt.Println(myMap2) // map[1:java 2:C++ 3:python]

	// 方法三,创建即初始化,注意逗号
	myMap3 := map[string]string{
		"one":   "php",
		"two":   "python",
		"three": "golang",
	}
	fmt.Println(myMap3) // map[one:php three:golang two:python]
}
package main

import "fmt"

func main() {
	// 这个麻烦
	var idNames map[int]string // 不能直接赋值,它是空的
	// 分配空间,可以不指定,建议直接指定,提高性能
	idNames = make(map[int]string, 1)
	idNames[0] = "hello"
	idNames[1] = "word"
	fmt.Println(idNames) // map[0:hello 1:word]

	// 常用
	idNames1 := make(map[int]string)
	idNames1[0] = "wjf"
	fmt.Println(idNames1) //map[0:wjf]

	// 5、如何确定一个key是否存在map中
	// map中访问一个不存在越界问题,它认为所有的key都是有效的,所有访问一个不存在的key不会崩溃,返回这个类型的零值
	// 零值: bool => false   数字 => 0    字符串 => 空
	name9 := idNames[9]
	fmt.Println(name9)

	// 无法通过value来判断一个key是否存在,万一恰巧这个key的value就是零值
	// 使用ok机制, 也是go的异常获取
	value, ok := idNames[1] // 如果id=1存在,ok为true,反之为false
	if ok {
		fmt.Println(value) // word
	} else {
		fmt.Println("不存在")
	}

	// 6、删除map中元素,使用自由函数delete()
	// 删除不存在的key并不会报错
	delete(idNames, 0)
	fmt.Println("删除后:", idNames) //删除后: map[1:word]

	// 并发任务的时候, 要对map上锁
}

11、struct结构体

1. 结构体的基本应用

package main

import "fmt"

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

// 定义一个结构体
type Book struct {
	title string
	auth  string
}

func printBook(book Book) {
	// 传递一个book的副本,发生修改,原book1不会发生改变
	book.auth = "888"
}

func changeBook(book *Book) {
	// 指针传递
	book.auth = "999"
}

func main() {
	/*
		var a myint = 10
		fmt.Println("a = ", a)
		fmt.Printf("type is %T\n", a)*/

	var book1 Book
	book1.title = "Golang"
	book1.auth = "zhangs"
	fmt.Printf("%v\n", book1) // {Golang zhangs}

	fmt.Println("-----------")

	printBook(book1)
	fmt.Printf("%v\n", book1) // {Golang zhangs}

	fmt.Println("-----------")

	changeBook(&book1)
	fmt.Printf("%v\n", book1) // {Golang 999}
}

2. 面向对象的go语言结构体,类似python中的class

package main

import "fmt"

// go语言面向对象,使用的struct结构体的方式
// 如果类的首字母大写,表示公有的,表示其他类也能访问
// 否则只能在本类中访问,类方法的命名也是这样
type Hero struct {
	Name  string
	Ad    int
	Level int
}

/*// this Hero表示:绑定Hero结构体
func (this Hero) Show() {
	fmt.Println("Name = ", this.Name)
	fmt.Println("Ad = ", this.Ad)
	fmt.Println("Level = ", this.Level)
}

func (this Hero) GetName() string {
	fmt.Println("Name = ", this.Name)
	return this.Name
}

func (this Hero) SetName(newName string) {
	// this:调用该方法的对象的一个副本(拷贝),无法真正修改
	this.Name = newName
}*/

// this Hero表示:绑定Hero结构体
// 用*表示,传递的是引用,可以完成修改
func (this *Hero) Show() {
	fmt.Println("Name = ", this.Name)
	fmt.Println("Ad = ", this.Ad)
	fmt.Println("Level = ", this.Level)
}

func (this *Hero) GetName() string {
	fmt.Println("Name = ", this.Name)
	return this.Name
}

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

func main() {
	hero := Hero{Name: "zhangs", Ad: 100, Level: 1}
	hero.Show()
	/*
		Name =  zhangs
		Ad =  100
		Level =  1
	*/

	fmt.Println("---------")

	hero.SetName("li4")
	hero.Show()
	/*
		Name =  li4
		Ad =  100
		Level =  1
	*/
}

3. 子类继承

// 继承
package main

import "fmt"

type Human struct {
	name string
	sex  string
}

func (this *Human) Eat() {
	fmt.Println("Human eat....")
}

func (this *Human) Walk() {
	fmt.Println("Human walk....")
}

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

	level int
}

// 重定义父类方法
func (this *SuperMan) Eat() {
	fmt.Println("Superman eat....")
}

// 子类的新方法
func (this *SuperMan) Fly() {
	fmt.Println("Superman fly....")
}

func (this *SuperMan) Print() {
	fmt.Println("name = ", this.name)
	fmt.Println("sex = ", this.sex)
	fmt.Println("level = ", this.level)
}

func main() {
	h := Human{"zhangs", "female"}
	h.Eat()
	h.Walk()
	/*
		Human eat....
		Human walk....
	*/

	fmt.Println("-------------")

	// 定义一个子类
	// s := SuperMan{Human{"li4", "female"}, 88}
	var s SuperMan
	s.name = "li4"
	s.sex = "man"
	s.level = 88

	s.Walk()
	s.Eat()
	s.Fly()
	s.Print()
	/*
		Human walk....
		Superman eat....
		Superman fly....
		name =  li4
		sex =  man
		level =  88

	*/
}

4. 多态,go的多态是通过创建接口,然后实现所有接口后自动绑定的

// 多态,利用接口的方式实现方式
package main

import "fmt"

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

// 具体的类,要继承这个接口,只要全部重写了这个接口的方法(所有接口)后,就自动继承
type Cat struct {
	color string // 猫的颜色
}

func (this *Cat) Sleep() {
	fmt.Println("Cat is Sleep")
}

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("Dog is Sleep")
}

func (this *Dog) GetColor() string {
	return this.color
}

func (this *Dog) GetType() string {
	return "Dog"
}

func showAnimal(animal AnimalIF) {
	animal.Sleep()
	fmt.Print("color is ", animal.GetColor())
	fmt.Println("kind is ", animal.GetType())
}

func main() {

	// 第一种方式
	/*
		var animal AnimalIF // 接口的数据类型,父类指针
		animal = &Cat{color: "Green"}
		animal.Sleep() // 调用Cat的sleep方法
		animal = &Dog{"yellow"}
		animal.Sleep()*/

	// 第二种方式
	cat := Cat{"green"}
	dog := Dog{"yellow"}

	showAnimal(&cat)
	/*
		Cat is Sleep
		color is greenkind is  Cat
	*/
	showAnimal(&dog)
	/*
		Dog is Sleep
		color is yellowkind is  Dog
	*/
}

5. interface{}空接口,能传入任何值,类似是python的函数传值,能接收任何值

// 通用万能类型interface{}:空接口,,能接任何类型
package main

import "fmt"

func myFunc(arg interface{}) {
	fmt.Println("myFunc is called....")
	fmt.Println(arg)

	//interface{}如何区分,此时引用的底层数据类型到底是什么

	//给interface{}提供"类型断言"的机制
	value, ok := arg.(string) // 判断arg是不是string类型,value存该值,ok存是否(true,false)
	if !ok {
		fmt.Println("arg is not string")
	} else {
		fmt.Println("arg is string type, value = ", value)
	}
}

type Book struct {
	auth string
}

func main() {
	book := Book{"Golang"}

	myFunc(book)
	/*
		myFunc is called....
		{Golang}
		arg is not string
	*/
	fmt.Println("--------------")
	myFunc(100)
	/*
		myFunc is called....
		100
		arg is not string
	*/
	fmt.Println("--------------")
	myFunc("funce")
	/*
		myFunc is called....
		funce
		arg is string type, value =  funce
	*/
}

12、pair数据类型

每个声明的变量都有type:存放变量类型,value:存放值

// pair数据类型,每个声明的变量都有
package main

import "fmt"

func main() {

	var a string
	// pair<type:string, value:"aceld">
	a = "aceld"

	// pair<type:string, value:"aceld">
	var allType interface{}
	allType = a

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

13、 reflect反射

1. 用于反射会类型和值

// 反射机制
package main

import (
	"fmt"
	"reflect"
)

// 通过reflect放射回来,类型和值
func reflectNum(arg interface{}) {
	fmt.Println("type = ", reflect.TypeOf(arg))
	fmt.Println("value = ", reflect.ValueOf(arg))
}

func main() {
	var num float64 = 1.2345
	reflectNum(num)
	/*
		type =  float64
		value =  1.2345
	*/
}

2. 反射在结构体中的应用

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	Id   int
	Name string
	Age  int
}

func (this User) Call() {
	fmt.Println("user is called....")
	fmt.Printf("%v\n", this)
}

func main() {
	user := User{1, "Amy", 18}
	DoFiledAndMethod(user)
}

// 利用放射,打印出类型和值
func DoFiledAndMethod(input interface{}) {
	// 获取input的type
	inputType := reflect.TypeOf(input)
	fmt.Println("inputType is ", inputType.Name()) // inputType is  User
	// 获取input的value
	inputValue := reflect.ValueOf(input)
	fmt.Println("inputValue is ", inputValue) // inputValue is  {1 Amy 18}
	// 通过type获取里面的字段
	// 1、获取interface的reflect,type,通过Type得到NumField,进行遍历
	// 2、得到每个field,数据类型
	// 3、通过filed有一个Interface()方法得到对应的value
	for i := 0; i < inputType.NumField(); i++ {
		field := inputType.Field(i)
		value := inputValue.Field(i).Interface()

		fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
		/*
			执行结果
			Id: int = 1
			Name: string = Amy
			Age: int = 18
		*/
	}
	fmt.Println("----------------")
	// 通过type获取里面的方法,调用
	for i := 0; i < inputType.NumMethod(); i++ {
		m := inputType.Method(i)
		fmt.Printf("%s: %v\n", m.Name, m.Type) // Call: func(main.User) // Call: func(main.User)
	}
}

3. 反射中获取tag标签的内容

package main

// 利用反射,获取Tag标签的信息
import (
	"fmt"
	"reflect"
)

type resume struct {
	// tag标签会被用于web中的json进行判断,,这个形式是给该字段绑定一个tag标签(用于说明)
	Name string `info:"name" doc:"我的名字"`
	Sex  string `info:"sex"`
}

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

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

func main() {
	var re resume

	findTag(&re)

	/*
		info:  name
		doc:  我的名字
		info:  sex
		doc:

	*/
}

4. 利用方式中的tag标签,实现struct--->json,json--->struct

// tag标签在json中的应用,,tag写明的字段名,最后会变成json中的key
// struct ---> json    json ---> struct
package main

import (
	"encoding/json"
	"fmt"
)

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

func main() {
	movie := Movie{"喜剧之王", 2000, 10, []string{"zxc", "zbz"}}
	// 编码码过程,结构体--》json
	jsonStr, err := json.Marshal(movie)
	if err != nil {
		fmt.Println("json marshal error ", err)
		return
	}

	fmt.Printf("jsonStr = %s\n", jsonStr) // jsonStr = {"title":"喜剧之王","rmb":2000,"year":10,"actors":["zxc","zbz"]}

	// 解码过程,jsonstr ---》 结构体
	// jsonStr = {"title":"喜剧之王","rmb":2000,"year":10,"actors":["zxc","zbz"]}
	myMovie := Movie{}
	err = json.Unmarshal(jsonStr, &myMovie)
	if err != nil {
		fmt.Println("json unmarshal error ", err)
		return
	}
	fmt.Printf("%v\n", myMovie) // {喜剧之王 2000 10 [zxc zbz]}
}

14、goroutine协程

go语言天生高并发,使用的就是协程,协程运行于线程之上,有线程进行控制

1. go中协程基本用法

package main

// 协程的基本使用
import (
	"fmt"
	"time"
)

// 协程的使用

// 子goroutine
func newTask() {
	i := 0
	for {
		i++
		fmt.Printf("new Goroutine : i = %d\n", i)
		time.Sleep(1 * time.Second)
	}
}

// 主goroutine,如果主的死亡,则子的全部死亡
func main() {
	// 创建一个go程 去执行newTask()流程
	go newTask()

	i := 0
	for {
		i++
		fmt.Printf("main goroutine : i = %d\n", i)
		time.Sleep(1 * time.Second)
	}
}

2. 匿名函数

package main

import (
	"fmt"
	"time"
)



func main() {
	// 用go创建一个形参为空,返回值为空的一个函数
	// 匿名无参数函数,在匿名函数写完后加一个()就可以实现函数的调用
	/*go func() {
		defer fmt.Println("A.defer")
		func() {
			defer fmt.Println("B.defer")
			// 退出当前goroutine,也就是所有的,为什么不使用return,因为return后只会退出当前函数
			runtime.Goexit()
			fmt.Println("B")
		}()

		fmt.Println("A")
	}()
	// 执行结果
	// B.defer
	// A.defer*/

	go func(a int, b int) bool {
		fmt.Println("a = ", a, "b = ", b)
		return true
	}(10, 20)
	// 死循环
	for {
		time.Sleep(1 * time.Second)
	}
}

3、return、os.EXIT、runtime.Goexit的区别

package main

/*

1、Goexit ===> 提前退出当前go程
2、return ===> 返回当前函数
3、exit =====> 退出当前进程

*/

import (
	"fmt"
	"runtime"
	"time"
)

func main() {
	go func() {
		func() {
			fmt.Println("这是子go程内部的函数")
			// return // 返回当前函数
			// os.Exit(-1) // 退出进程
			runtime.Goexit() // 退出当前go程
		}()

		fmt.Println("子go程结束")
	}()

	fmt.Println("这是主go程")
	time.Sleep(5 * time.Second)
	fmt.Println("Over")
}

/*

return执行的结果:
	这是主go程
	这是子go程内部的函数
	子go程结束
	Over

os.Exit:
	这是主go程
	这是子go程内部的函数
	exit status 0xffffffff

runtime.Goexit:
	这是主go程
	这是子go程内部的函数
	Over
*/

15、channel(管道),用于协程之间的通信

1. 无缓冲channel

// channel(管道),协程主键的通信
package main

import "fmt"

func main() {
	// 定义一个channel,无缓冲,会发生阻塞, chan为关键字
	c := make(chan int)

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

		// 将666发送给c
		c <- 666
	}()
	// 从c中接受数据,并赋值给num
	num := <-c
	fmt.Println("num = ", num)
	fmt.Println("main gotoutine 结束....")

	/*
		执行结果:
		goroutine 正在运行
		goroutine结束
		num =  666
		main gotoutine 结束....
	*/
}

2. 带缓冲的channel

package main

import (
	"fmt"
	"time"
)

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

	fmt.Println("len(c) = ", len(c), ", cap(c) = ", cap(c))
	go func() {
		defer fmt.Println("子go程结束")
		// 如果缓冲区满了,则会进行阻塞,等待有空闲的空间出现
		for i := 0; i < 4; i++ {
			c <- i
			fmt.Println("子go程正在运行,发送的元素是=", i, " len(c)=", len(c), ", cap(c)=", cap(c))
		}
	}()
	time.Sleep(2 * time.Second)
	for i := 0; i < 4; i++ {
		num := <-c // 从c中接收数据,并赋值给num
		fmt.Println("num = ", num)
	}
	time.Sleep(1 * time.Second)
	defer fmt.Println("main 结束")
}

/*
执行结果:

len(c) =  0 , cap(c) =  3
子go程正在运行,发送的元素是= 0  len(c)= 1 , cap(c)= 3
子go程正在运行,发送的元素是= 1  len(c)= 2 , cap(c)= 3
子go程正在运行,发送的元素是= 2  len(c)= 3 , cap(c)= 3
num =  0
num =  1
num =  2
num =  3
子go程正在运行,发送的元素是= 3  len(c)= 3 , cap(c)= 3
子go程结束
main 结束

*/

3. 关闭channel,channel关闭后,其中剩下的数据还可以继续读取

// 关闭channel
package main

import "fmt"

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

	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		// close可以关闭一个channel
		close(c)
	}()
	for {
		// ok如果为true表示channel没有关闭,如果为false表示channel已经关闭
		if data, ok := <-c; ok {
			fmt.Println(data)
		} else {
			break
		}
	}

	fmt.Println("Main finished .....")
}

/*
0
1
2
3
4
Main finished .....
*/

4. 可以使用range的方法来读channel

// 关闭channel与range
package main

import "fmt"

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

	go func() {
		for i := 0; i < 5; i++ {
			c <- i
		}
		// close可以关闭一个channel
		close(c)
	}()

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

	fmt.Println("Main finished .....")
}

5. chanael和select方法结合使用,可以检测到哪个channel可以使用,实现斐波那契数列

// channel与select
// 使用select监控多个channel,看哪个可读

package main

import "fmt"

func fibonacii(c, quit chan int) {
	x, y := 1, 1
	for {
		select {
		case c <- x:
			// 如果c可写,则该case就会进来
			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
	}()

	// main go
	fibonacii(c, quit)
}

/*
执行结果:
1
1
2
3
5
8
quit
*/

16、go modules模式

1. go mod命令

  • go mod init:生成go.mod文件
  • go mod download:下载go.mod文件中指明的所有依赖
  • go mod tidy:整理现有的依赖
  • go mod graph:查看现有的依赖结构
  • go mod edit:编辑go.mod文件
  • go mod vendor:导出项目所有的依赖到vendor目录
  • go mod verify:校验一个模块是否被篡改过
  • go mod why:查看为什么需要依赖某模块

2. go env查看go mod环境变量

  • GO111MODULE:是否开启go modules模式,建议go v1.11之后都设置1为on。
  • GOPROXY:项目第三方依赖的下载源地址(阿里云:https://mirrors.aliyun.com/goproxy,七牛云:https://goproxy.cn,direct);direct:用于指示Go回溯到模块版本的源地址去抓取。
  • GOSUMDB:用于校验拉取的第三方库是否完整,默认是国外的网站如果设置了GOPROXY,这个就不用设置。

3. go env -w  环境变量名=值 | linux下使用在export配置环境变量也可以;

4. 更多的go modules知识,需要更详细的学习。

17、go命令

提示:在window下有些目录在终端会运行不了,我们需要下载一个git bash作为终端进行使用,同时可以将git bash配置为ide的终端使用,下载链接 Git for Windows

1、编译.go文件, -o 指定生成文件的命令

        1. go build -o test.exe main.go xxx.go

        2. go build *.go

2、直接运行程序,不会编译为exe文件

        1. go run *.go

3、安装程序

        1. 拿到一个c源码,想自己编译出exe

                1. ./configure

                2. make

                3. make install ====>将编译好的程序安装到指定的目录/user/bin

        2. 使用go install,可以将应用程序安装到GOBIN下面:$GOPATH/bin

                1. go install ====> 需要实现在环境变量中设置GOBIN目录

4、go env查看当前go的配置

18、go不支持的语法

1、自增--i, ++i不支持

2、不支持地址加减

3、不支持三目运算符(x = 2 > 3 ? 1 : 0)

4、只有false才能表示代码逻辑假,数字0和nil不能

19、go中比较有特色的语法 

1、switch

package main

import (
	"fmt"
	"os"
)

// 从命令行输入参数,在switch中进行处理

func main10() {
	// go; os.Args ==> 直接可以获取命令输入,是一个字符串切片
	// os.Args[0] ===> 程序名字
	// os.Args[1] ===> 第一个参数,以此类推
	cmds := os.Args
	for key, cmd := range cmds {
		fmt.Println("key:", key, ", cmd: ", cmd)
	}

	if len(cmds) < 2 {
		fmt.Println("请正确输入")
		return
	}

	switch cmds[1] {
	case "hello":
		fmt.Println("hello")
		// 默认在后面有增加了break
		// 如果需要穿透,需要加入fallthrough,,打印hello后,还会执行后面的代码
		fallthrough
	case "world":
		fmt.Println("world")
	default:
		fmt.Println("default")
	}
}

2、标签与goto、continue、break配合

package main

import "fmt"

func main11() {
	// 标签 LABEL1(名字可以随便起)
	// goto LABEL1 ==> 下次进入循环时,i不会保存之前的状态,从0开始,会死循环
	// break LABEL1 ===> 会跳到指定位置,但是会记录之前的状态,i变成1,以此类推
	// continue LABEL1 ==> 会直接退出到LABEL1的位置,在本程序中,直接退出了所有循环
LABEL1:
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if j == 3 {
				// goto LABEL1
				// continue LABEL1
				break LABEL1
			}
			fmt.Println("i:", i, ", j:", j)
		}
	}
	fmt.Println("overs")
}

20、json编解码

在网络中传输的时候,把Student结构体,编码成json字符串,传输=》结构体=》字符串=》编码

接收字符串,需要将字符串转换成结构体,然后操作=》字符串=》结构体=》解密

package main

import (
	"encoding/json"
	"fmt"
)

type Student struct {
	Id     int
	Name   string
	Age    int
	gender string // 小写字母开头的,进行json编码会忽略
}

func main() {
	// 在网络中传输的时候,把Student结构体,编码成json字符串,传输=》结构体=》字符串=》编码

	// 接收字符串,需要将字符串转换成结构体,然后操作=》字符串=》结构体=》解密

	lily := Student{1, "Lily", 20, "女"}

	fmt.Println(lily)

	// func json.Marshal(v interface{}) ([]byte, error)
	// 编码  结构体=》字符串
	encodeInfo, err := json.Marshal(&lily) // 传指针和直接传都没问题
	if err != nil {
		fmt.Println("json.Marshal err: ", err)
		return
	}

	fmt.Println("encodingInfo: ", string(encodeInfo))

	// 对端接收到
	// 反序列化(解码):字符串=》结构体
	var lily2 Student
	if err := json.Unmarshal([]byte(encodeInfo), &lily2); err != nil {
		fmt.Println("json.Unmarshal err: ", err)
		return
	}

	fmt.Println("name: ", lily2.Name)
	fmt.Println("gender: ", lily2.gender)
	fmt.Println("age: ", lily2.Age)

}

/*

encodingInfo:  {"Id":1,"Name":"Lily","Age":20}
name:  Lily
gender:
age:  20

*/

struct结构体标签(tag)用法

package main

import (
	"encoding/json"
	"fmt"
)

type Teacher struct {
	Name    string `json:"-"`                 // 这个编码不参与,json编码
	Subject string `json:"Subject_name"`      // json编码时,这个字段会起一个别名
	Age     int    `json:"age,string"`        // 同时改名和改类型, 两个字段(名字,类型), 不能有空格
	Address string `json:"address,omitempty"` // 如果是空的则忽略
	gender  string // 小写字母开头的,进行json编码会忽略
}

func main() {
	t1 := Teacher{
		Name:    "duck",
		Subject: "goland",
		Age:     22,
		gender:  "M",
		// Address: "beijing",
	}

	fmt.Println("t1:", t1)

	encodeInfo, _ := json.Marshal(t1)
	fmt.Println("encodeInfo:", string(encodeInfo)) // encodeInfo: {"Subject_name":"goland","age":"22"}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值