1.环境搭建

地址:https://golang.google.cn/dl/

UNIX/Linux/Mac OS X, 和 FreeBSD 安装

以下介绍了在UNIX/Linux/Mac OS X, 和 FreeBSD系统下使用源码安装方法(用命令安装的话版本比较旧):

1、下载二进制包:go1.18.1.linux-amd64.tar.gz。

2、将下载的二进制包解压至 /usr/local目录。

sudo tar -zxvf go1.18.1.linux-amd64.tar.gz -C /usr/local/

3、将 /usr/local/go/bin 目录添加至 PATH 环境变量:

sudo vim ~/.bashrc

结尾添加 

export GOPATH=$HOME/go
export GOBIN=$GOPATH/bin
export GOMODCACHE=$GOPATH/pkg/mod
export PATH=$PATH:$GOBIN
export PATH=$PATH:/usr/local/go/bin
source  ~/.bashrc

终端输入go,下面输出表示成功

yys@yys:~$ go
Go is a tool for managing Go source code.

Usage:

    go <command> [arguments]

The commands are:

    bug         start a bug report
    build       compile packages and dependencies
    clean       remove object files and cached files
    doc         show documentation for package or symbol
    env         print Go environment information
    fix         update packages to use new APIs
    fmt         gofmt (reformat) package sources
    generate    generate Go files by processing source
    get         add dependencies to current module and install them
    install     compile and install packages and dependencies
    list        list packages or modules
    mod         module maintenance
    work        workspace maintenance
    run         compile and run Go program
    test        test packages
    tool        run specified go tool
    version     print Go version
    vet         report likely mistakes in packages

Use "go help <command>" for more information about a command.

Additional help topics:

    buildconstraint build constraints
    buildmode       build modes
    c               calling between Go and C
    cache           build and test caching
    environment     environment variables
    filetype        file types
    go.mod          the go.mod file
    gopath          GOPATH environment variable
    gopath-get      legacy GOPATH go get
    goproxy         module proxy protocol
    importpath      import path syntax
    modules         modules, module versions, and more
    module-get      module-aware go get
    module-auth     module authentication using go.sum
    packages        package lists and patterns
    private         configuration for downloading non-public code
    testflag        testing flags
    testfunc        testing functions
    vcs             controlling version control with GOVCS

Use "go help <topic>" for more information about that topic.
 

1.1 GOPROXY 环境变量

 

要永久设置 GOPROXY 环境变量,你需要根据你所使用的操作系统进行相应的设置。下面是在不同操作系统上设置永久环境变量的方法:

**在 Windows 上设置永久环境变量:**

1. 在 Windows 桌面上,右键点击"此电脑"(或"我的电脑")图标,选择"属性"。

2. 在左侧面板中,点击"高级系统设置"。

3. 在"系统属性"对话框中,点击"环境变量"按钮。

4. 在"环境变量"对话框中,可以设置两种类型的环境变量:
   - 用户变量(只适用于当前用户):在"用户变量"部分点击"新建"按钮,在"变量名"中输入 "GOPROXY",在"变量值"中输入 "https://mirrors.aliyun.com/goproxy/"(或你想要的阿里云源地址)。
   - 系统变量(适用于所有用户):在"系统变量"部分点击"新建"按钮,在"变量名"中输入 "GOPROXY",在"变量值"中输入 "https://mirrors.aliyun.com/goproxy/"(或你想要的阿里云源地址)。

5. 点击"确定"保存设置,并关闭对话框。

6. 重启你的计算机,使环境变量生效。

**在 macOS 和 Linux 上设置永久环境变量:**

1. 打开终端。

2. 输入以下命令来编辑 `.bashrc`(或 `.bash_profile`)文件:

 nano ~/.bashrc

   或

nano ~/.bash_profile

3. 在打开的文件中,在文件末尾添加以下行:

 export GOPROXY=https://mirrors.aliyun.com/goproxy/

   或你想要的阿里云源地址。

4. 按下 Ctrl + X,然后输入 "Y" 保存文件,然后按下 Enter 退出编辑器。

5. 在终端中执行以下命令来使环境变量生效:

source ~/.bashrc

   或

source ~/.bash_profile

   这将重新加载配置文件。

现在,你已经成功设置了永久的 GOPROXY 环境变量。在以后的会话中,该环境变量将自动应用,并且 Go 模块将从阿里云源下载。

1.2 第一装库要go mod init

go mod init 是用于在 Go 项目中初始化一个新的模块的命令。它会创建一个名为 go.mod 的文件,其中定义了模块的名称和版本。

要使用 go mod init 命令,在终端或命令提示符中打开,并导航到你的 Go 项目的根目录。然后,运行以下命令:

go mod init <模块名称>

<模块名称> 替换为你想要的模块名称。模块名称应该是一个字符串,唯一标识你的模块,通常使用反向域名的格式(例如 github.com/username/project)。

例如,如果你想要初始化一个名为 "example.com/myproject" 的新模块,你可以运行以下命令:

go mod init example.com/myproject

执行该命令后,Go 会在当前目录中创建一个 go.mod 文件。该文件将包含模块的名称和版本信息。然后,你可以使用 Go 模块来管理依赖项并构建你的项目。

请注意,go mod init 通常用于创建新模块或将现有项目转换为使用 Go 模块。如果你正在一个现有的模块中工作,不需要再次运行 go mod init

2.从一个main函数初见Golang语法注意点
 

package main //程序的包名字

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

//main 函数
func main(){  // 函数{一定和函数名在同一行的,否则编译:
	//golang中的表达式,加";",和不加 都可以,建议是不加
	fmt.Println("hello go!")
	time.Sleep(1 * time.Second)
}

运行命令 

go run hello.go

hello go!
 

编译成可执行文件 

go build hello.go

语法注意事项

(1〕源文件以"go"为扩展名。
(2〕程序的执行入口是main()函数。( 3)严格区分大小写。
(4)方法由一条条语句构成,每个语句后不需要分号(Go语言会在每行后自动加分号),这也体现出Golang的简洁性。(5)Go编译器是一行行进行编译的,因此我们一行就写一条语句,不能把多条语句写在同一个,否则报错
(6)定义的变量或者import的包如果没有使用到,代码不能编译通过。
(7)大括号都是成对出现的,缺一不可
 

gofmt使用
对于一门编程语言来说,代码格式化是最容易引起争议的一个问题,不同的开发者可能会有不同的编码风格和习惯,但是如果所有开发者都能使用同一种格式来编写代码,开发者就可以将宝贵的时间专注在语言要解决的问题上。

​ Golang的开发团队制定了统一的官方代码风格,并且推出了gofmt工具(gofmt或go fmt)来帮助开发者格式化他们的代码到统一的风格。

​ Gofmt格式化Go程序。它使用制表符进行缩进,使用空格进行对齐。如果没有显式路径,它将处理标准输入。给定一个文件,它对文件进行操作;给定一个目录,它递归地操作该目录中的所有.go文件。(忽略以句点开头的文件。)

有关gofmt的更多信息,请参见“go doc cmd/gofmt”。

用法:
    gofmt [flags] [path ...]

The flags are:
      -cpuprofile string
        将CPU配置文件写入此文件
    -d
        不将重新格式化的源打印到标准输出。
        如果一个文件的格式与gofmt的不同,则将差异部分打印到标准输出。
    -e
        打印所有(包括虚假)错误。
    -l
        不将格式化的源打印到标准输出。
        如果文件的格式与 gofmt 不同,则将其名称打印到标准输出。
    -r rule(string)
        在重新格式化之前,对源应用重写规则(例如,'a[b:len(a)] -> a[b:]')
    -s
        尝试简化代码(在应用重写规则后,如果有的话)。
    -w
           不将重新格式化的源打印到标准输出。
           如果文件的格式与 gofmt 不同,则用 gofmt 的版本覆盖它。
        如果在覆盖期间发生错误,原始文件将从自动备份中恢复
 

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

package main

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

import (
	"fmt"
)

// 声明全局变量 方法一和方法二和方法三都是可以的
var gA int = 100
var gB = 200

// 用方法四声明全局变量
// :=只能够用在 函数体内来声明
//gC := 200

func main(){
	//方法1:声明一个变量 默认值是0
	var a int
	fmt.Println("a = ", a)
	fmt.Printf("type of a = %T\n", a)
	// 方法二:声明一个变量,初始化一个值
	var b = 100
	fmt.Println("b = ", b)
	fmt.Printf("type of b = %T\n", b)

	var bb string = "abcd"
	fmt.Printf("bb = %s,type of bb = %T\n", bb, bb)
	// 方法三:在初始化时候,可以省去数据类型,通过值自动匹配当前变量的数据类型
	var c = 100
	fmt.Println("c = ", c)
	fmt.Printf("type of c = %T\n", c)
	var cc string = "abcdef"
	fmt.Printf("cc = %s,type of cc = %T\n", cc, cc)
	// 方法四:(常用方法)省去var关键字,直接自动匹配
	e := 100
	fmt.Println("e = ", e)
	fmt.Printf("type of e = %T\n", e)

	f := "abcd"
	fmt.Println("f = ", f)
	fmt.Printf("type of f = %T\n", f)

	g := 3.14
	fmt.Println("g = ", g)
	fmt.Printf("type of g = %T\n", g)

	fmt.Printf("gA=%d, gB=%d\n", gA, gB)
	//声明多个变量
	var xx, yy int = 100, 200
	fmt.Println("xx=", xx, "yy=", yy)

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

a =  0
type of a = int
b =  100
type of b = int
bb = abcd,type of bb = string
c =  100
type of c = int
cc = abcdef,type of cc = string
e =  100
type of e = int
f =  abcd
type of f = string
g =  3.14
type of g = float64
gA=100, gB=200
xx= 100 yy= 200
kk= 100 ll= abcd
vv =  100 , jj =  true
 

4.const与iota知识点注意事项
iota只能在const()的括号使用

package main

import "fmt"

// const 来定义枚举类型
const(
	// 可以在const()添加一个关键字iota , 每行iota都会增加1, 第一行的iota的 默认是0
	Beijing = 10 * iota
	Shanghai
	Shenzhen
)

func main(){
	// 常量(只读属性)
	const length int = 10
	fmt.Println("length = ", length)
	//length = 100 //常量是不允许修改的 
	fmt.Println("Beijing = ", Beijing)
	fmt.Println("Shanghai = ", Shanghai)
	fmt.Println("Shenzhen = ", Shenzhen)

}

 length =  10
Beijing =  0
Shanghai =  10
Shenzhen =  20

 5.Golang中函数的多返回值三种写法

package main

import "fmt"

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)

	return 666, 777
}

// 返回多个返回值,有形参的
func foo3(a string, b int) (r1, r2 int) {
	fmt.Println("--- foo3---")
	fmt.Println("a = ", a)
	fmt.Println("b = ", b)
	// r1, r2属于foo3 的形参,初始值为0
	// r1 r2 作用域空间是foo3整个函数体的{}空间
	fmt.Println("r1 = ", r1)
	fmt.Println("r2 = ", r2)
	// 给有名称的返回值变量赋值
	r1 = 1000
	r2 = 2000
	return 
}

func main(){
	c := fool("abc", 555)
	fmt.Println("c=", c)
	ret1, ret2 := foo2("haha", 434)
	fmt.Println("ret1=", ret1, "ret2=", ret2)

	ret3, ret4 := foo3("hahaa2", 3434)
	fmt.Println("ret3=", ret3, "ret4=", ret4)
}

a =  abc
b =  555
c= 100
a =  haha
b =  434
ret1= 666 ret2= 777
--- foo3---
a =  hahaa2
b =  3434
r1 =  0
r2 =  0
ret3= 1000 ret4= 2000
 

6.import导包路径问题与init方法调用流程

init
├── lib1
│   └── lib1.go
├── lib2
│   └── lib2.go
└── main.go
 

package main
import (
	"init/lib1"
	"init/lib2"
)

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

main.go 

package lib1
import "fmt"

func Lib1Test(){
	fmt.Println("lib1Test()...")
}

func init(){
	fmt.Println("lib1,init()...")
}

lib1/lib1.go

package lib2
import "fmt"

func Lib2Test(){
	fmt.Println("lib2Test()...")
}

func init(){
	fmt.Println("lib2,init()...")
}

lib2/lib2.go

7.go语言导入包的几种方式

1.操作,注意前面的点

将当前fmt包中的全部⽅法,导⼊到当前本包的作⽤中,fmt包中 的全部的⽅法可以直接使⽤API来调⽤,不需要fmt.API来调⽤
package main

import . "fmt" // 调用此函数,无须通过包名,但是不推荐这样,如果多个包都这样,有重名的函数会出现错乱

func main(){
	Println("this is a test go")
}

2.给包起个别名

给fmt包起⼀个别名,aa, aa.Println()来直接调⽤

package main

import long "fmt"

func main(){
	long.Println("this is a test go")
}

3.忽略此包

给fmt包起⼀个别名,匿名, ⽆法使⽤当前包的⽅法,但是 会执⾏当前的包内部的init()⽅法
package main

import _ "fmt" // 前面加一个下划线即可

func main(){

}

8.defer语句调用顺序
defer 执行顺序先进后出栈的顺序,如果defer和return在一块时候先执行return 再执行defer

package main
import "fmt"

func deferFunction() int {
	fmt.Println("defer func called 11111")
	return 0
}
func deferFunction2() int {
	fmt.Println("defer func called 22222")
	return 0
}
func returnFunction() int {
	fmt.Println("return func called")
	return 0
}

func returnAndeDeger() int {

	defer deferFunction()
	defer deferFunction2()
	return returnFunction()
}

func main(){

	returnAndeDeger()
}

return func called
defer func called 22222
defer func called 11111

9.Golang中的数组与动态数组区别

package main
import "fmt"

func printArray(myArray []int){
	// _表示匿名的变量
	for _, value := range myArray{
		fmt.Println("value=", value)
	}
	//改变数值
	myArray[0] = 100
}

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

	// 引用传递,传递指针,可以改变值
	fmt.Println("修改值后的结果")
	for _, value := range myArray{
		fmt.Println("value 2=", value)
	}
}

myArray type= []int
value= 1
value= 2
value= 3
value= 4
修改值后的结果
value 2= 100
value 2= 2
value 2= 3
value 2= 4
 

10.slice切片追加与截取 

一般通过append添加元素

append可以添加任意数量相同类型的元素

number:头指针
ptr:尾指针永远指向合法元素的最后一个位置
后面两个实际上是非法的,到那时底层已经分配了内存

总结:

切片的长度和容量不同,长度表示左指针到右指针之间的距离,容量表示左指针至底层数组末尾的距离
切片的扩容机制,append的时候,如果长度增加后超过容量,则将容量增加2倍,可通过运行以下代码(1.1)进行实践
如果想修改切片的
 

//1.1切片的追加
package main

import "fmt"

func main(){
    var numbers = make ([]int,3,5)//make定义长度,提供容量 3长度,5容量
    fmt.Printf("len = %d, cap = %d, slice = %v\n", len(numbers), cap(numbers), numbers)
    //向numbers切片追加一个元素1,numbers len = 4 ,[0,0,0,1] cap = 5
    numbers = append(numbers,1)
    //向numbers切片追加一个元素1,numbers len = 5 ,[0,0,0,1,2] cap = 5
    numbers = append(numbers,2)
    //向一个容量已经满的切片增加一个元素的时候,会自动开辟一个与原来相同容量的空间numbers len = 6 ,[0,0,0,1,2,3] cap = 10
    numbers = append(numbers,3)
    fmt.Println("----------")
    var numbers2 = make([]int,3)
    fmt.Printf("len = %d, cap = %d, slice = %v\n",len(numbers2), cap(numbers2),numbers2)
    //向一个容量已经满的切片增加一个元素的时候,会自动开辟一个与原来相同容量的空间numbers此时len = 4, [0,0,0,1], cap=6
    numbers2 = append(numbers2,1)
}

len = 3, cap = 5, slice = [0 0 0]
----------
len = 3, cap = 3, slice = [0 0 0]
 

截取

一般通过[:]的形式截取数组并赋值。

但此时因为s和s1是指向同一个地址的,所以如果s1[0]跟更改了s和s1都会被更改
想两个不同的话,go提供了一个copy函数,可以将底层数组的slice一起进行拷贝

可通过运行以下代码(1.2)进行实践

//1.2切片的截取
package main

import "fmt"

func main(){
    s := []int{1,2,3}//len = 3, cap = 3
    //[0,2)
    s1 := s[0:2]//[1,2]
    fmt.Println(s1)
    s1[0] = 100
    fmt.Println(s)
    fmt.Println(s1)
    //因为s和s1是指向同一个地址的,所以如果s1[0]跟更改了s和s1都会被更改/
    //想两个不同的话,go提供了一个copy函数
    //copy可以将底层数组的slice一起进行拷贝
    s2 := make([]int,3)//s2 = [0,0,0]
    //将s中的值,一次拷贝到s2中
    copy(s2,s)
    fmt.Println(s2)
}

[1 2]
[100 2 3]
[100 2]
[100 2 3]
 

11.map

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

package main

import "fmt"

func main(){

	var myMap1 map[string]string
	if myMap1 == nil{
		fmt.Println("这是一个空map")
	}
	// 在使用map前,给make给map分配数据空间

	myMap1 = make(map[string]string, 10)
	myMap1["one"] = "python"
	myMap1["tow"] = "c++"
	myMap1["three"] = "go"
	fmt.Println(myMap1)
	// 方法2:第二种方法无容量
	myMap2 := make(map[int]string)
	myMap2[0] = "python"
	myMap2[1] = "c++"
	myMap2[2] = "go"
	fmt.Println(myMap2)

	// 方法3:第3种方法无容量
	myMap3 := map[string]string{
		"one": "python",
		"two": "c++",
		"three" : "go",
	}
	fmt.Println(myMap3)
}

这是一个空map
map[one:python three:go tow:c++]
map[0:python 1:c++ 2:go]
map[one:python three:go two:c++]
 

map增删改查

package main
import "fmt"
func printMap(mapcity map[string]string){
	for key, value := range(mapcity){
		fmt.Println("key =", key, "value =", value)
	}
}

func chageMap(mapcity map[string]string){
	mapcity["日本"] = "纽约"
}
func main(){
	cityMap := make(map[string]string)
	// 添加
	cityMap["中国"] = "北京"
	cityMap["日本"] = "东京"
	cityMap["韩国"] = "首尔"
	fmt.Println(cityMap)
	//遍历
	printMap(cityMap)

	fmt.Println("删除后------------")
	//删除
	delete(cityMap, "中国")
	printMap(cityMap)

	fmt.Println("修改后------------")
	// 修改
	chageMap(cityMap)
	printMap(cityMap)

}

map[中国:北京 日本:东京 韩国:首尔]
key = 中国 value = 北京
key = 日本 value = 东京
key = 韩国 value = 首尔
删除后------------
key = 日本 value = 东京
key = 韩国 value = 首尔
修改后------------
key = 日本 value = 纽约
key = 韩国 value = 首尔
 

 12.struct结构体

go和其他语言一样,可以申明新的“结构体”,struct可以作为其他类型的属性活字段的容器

 

package main
import "fmt"

// 定义一个结构体
//type关键字申明 Book为 struct类型,person包含tile 属性和auth 属性,对应的类型为string和string

type Book struct{
	tile string
	auth string
}

func changeBook(book Book){
	// 传递一个Book的副本
	book.tile = "python"
}

func changeBook2(book *Book){
	// 指针传递
	book.tile = "python"
}
func main(){
	var book1 Book
	book1.tile = "go"
	book1.auth = "张三"
	fmt.Println(book1)
	changeBook(book1)
	fmt.Println(book1)

	fmt.Println("指针传递")
	changeBook2(&book1)
	fmt.Println(book1)

}

{go 张三}
{go 张三}
指针传递
{python 张三}
 

 13.面向对象

package main

import "fmt"

type Hero struct{
	Name string
	ad int
	Level int
}

// 如果类名大写的,其他包也可以访问
func (this *Hero) show(){
	fmt.Println("name=", this.Name)
	fmt.Println("ad=", this.ad)
	fmt.Println("Level=", this.Level)
}

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

func (this *Hero) setname(newname string){
	this.Name = newname
}

func main(){

	hero := Hero{Name:"张三", ad: 200}
	hero.show()
	hero.setname("李四")
	fmt.Println(hero.getname())
}

name= 张三
ad= 200
Level= 0
李四

继承

package main

import "fmt"

type Human struct{
	Name string
	sex string
}

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

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


type Superman struct{
	Human	// Superman继承Human类的方法
	level int
}


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

func (this *Superman) fly(){
	fmt.Println("Superman.fly")
}
func main(){
	// 方法1:
	//s := Superman{Human{"张三", "男"}, 99}
	// 方法2:
	var s Superman
	s.Name = "李四"
	s.sex = "男"
	s.level = 100
	s.eat()
	s.walk()
	s.fly()

}

Superman.eat
Human.walk
Superman.fly
 

对象多态的实现与基本要素
 

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 sleeping")
}


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 sleeping")
}


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

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

func showAnimal(animal AnimalIF){
	animal.Sleep() // 多态
	fmt.Println("color=", animal.GetColor())
	fmt.Println("type=", animal.GetType())

}
func main(){
	/*
	var animal AnimalIF  //接口数据类型,父类指针
	animal  = &Cat{"green"}
	animal.Sleep() // 调用就是Cat的Sleep()方法
	animal = &Dog{"black"}
	animal.Sleep() // 调用就是Dog的Sleep()方法
	*/
	cat := Cat{"green"}
	dog := Dog{"black"}
	showAnimal(&cat)
	showAnimal(&dog)



}

cat is sleeping
color= green
type= Cat
dog is sleeping
color= black
type= Dog
 

 

package main
import "fmt"

func myFunc(arg interface{}){

	fmt.Println("myFunc is Called")
	fmt.Println(arg)
	value, ok := arg.(string)
	if !ok{
		fmt.Printf("不是字符串类型,而是%T类型\n",arg)
	}else{
		fmt.Printf("%s是字符串类型", value)
	}
}

type Book struct{
	auth string
}

func main(){
	book := Book{"哈利波特"}

	myFunc(book)
	myFunc("ceshi")
	myFunc(123)
	myFunc(3.14)
}

myFunc is Called
{哈利波特}
不是字符串类型,而是main.Book类型
myFunc is Called
ceshi
ceshi是字符串类型myFunc is Called
123
不是字符串类型,而是int类型
myFunc is Called
3.14
不是字符串类型,而是float64类型
 

14.反射reflect
 

package main
import (
	"fmt"
	"reflect"
)

type User struct{
	Id int
	Name string
	Age int
}

func(this *User) Call(){
	fmt.Println("user this call ")
	fmt.Println("%v\n", this)

}

func main(){
	user := User{1, "343", 18}
	DoFIledAndMethird(user)
}

func DoFIledAndMethird(input interface{}){
	// 获取input的type
	inputValue := reflect.ValueOf(input)
	fmt.Println("inputValue =", inputValue)
	// 获取input 的value
	inputType := reflect.TypeOf(input)
	fmt.Println("inputType =", inputType)
	// 通过type 获取里面字段
	// 1. 获取interface 的reflect.Type,通过Type得到NumField,进行遍历
	// 2. 得到每个filed,数据类型
	// 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)
	}
	//通过type获取里面方法,调用
	for i := 0; i< inputType.NumMethod(); i++ {
		m := inputType.Method(i)
		fmt.Printf("%s: %v\n", m.Name, m.Type)
	}
}

inputValue = {1 343 18}
inputType = main.User
Id : int = 1
Name : string = 343
Age : int = 18
 

标签Tag
 

package main

import (
	"reflect"
	"fmt"
)

type resume struct{
	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, "doc", tagdoc)

	}
}


func main(){
	var re resume
	findTag(&re)
}

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

结构体标签在json中的应用
 

package main

import (
	"encoding/json"
	"fmt"
)

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

}

func main(){
	move := Move{"戏剧之王", 2000, 10, []string{"周星驰", "张伯之"}}
	// 编成过程,结构体-->json
	jsonStr, err := json.Marshal(move)
	if err != nil{
		fmt.Println("json marshal error", err)
		return
	}
	fmt.Printf("jsonStr=%s\n", jsonStr)
	// 解码过程, jsonStr-->结构体
	myMovie := Move{}
	err2 := json.Unmarshal(jsonStr, &myMovie)
	if err2 != nil{
		fmt.Println("json unmarshal error", err2)
		return
	}
	fmt.Printf("myMovie=%v\n", myMovie)
}

jsonStr={"title":"戏剧之王","year":2000,"rmb":10,"actors":["周星驰","张伯之"]}
myMovie={戏剧之王 2000 10 [周星驰 张伯之]}
 

15.goroutine

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)
	}
}

main goroutine i=1
new goroutine i=1
new goroutine i=2
main goroutine i=2
new goroutine i=3
main goroutine i=3
main goroutine i=4
new goroutine i=4
^Csignal: interrupt
 

goexit退出主流程

package main

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

func main(){
	go func(){
		defer fmt.Println("A defer")
		func (){
			defer fmt.Println("B defer")
			// 退出goroutine
			runtime.Goexit()
			fmt.Println("B")
		}()
		fmt.Println("A")
	}()
	// 死循环

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

B defer
A defer
^Csignal: interrupt
 

 

channel的基本定义与使用

package main
import "fmt"

func main(){
	// 定义一个chnannel
	c := make(chan int)
	go func(){
		defer fmt.Println("goroutine结束")
		fmt.Println("goroutine结束正在运行")
		c <- 666 // 将666 发送给c
	}()
	num := <-c // 从c中接收数据并赋值给num


	fmt.Println("num=", num)
	fmt.Println("main goroutine结束")

}

 goroutine结束正在运行
goroutine结束
num= 666
main goroutine结束

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=", i, "len(c)=", len(c), "cap(c)=", cap(c))

		}
	}()

	time.Sleep(2 * time.Second)

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

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

分子go程分4个,当执行3个阻塞,子go程没有结束,取数了i=3结束

在第 1 步,右侧的 goroutine 正在从通道接收⼀个值。
在第 2 步,右侧的这个 goroutine独⽴完成了接收值的动作,⽽左侧的 goroutine 正在发送⼀个新值到通道⾥。
在第 3 步,左侧的goroutine 还在向通道发送新值,⽽右侧的 goroutine 正在 从通道接收另外⼀个值。这个步骤⾥的两个操作既不是同步的,也不会互相阻塞。
最后,在第 4 步,所有的发送和接收都完成,⽽通道⾥还有⼏个值,也有⼀些空间可以存更多的值。
特点:1.当channel已经满,再向⾥⾯写数据,就会阻塞 2.当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{
		if data, ok := <-c; ok{
			fmt.Println(data)
		}else{
			break
		}
	}
	fmt.Println("main finished...")
}

0
1
2
3
4
main finished...
 

 

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

16.GoModules模式

 挂国内镜像源 

go env -w GO111MODULE=on

 

go env -w GOPROXY=https://goproxy.cn,direct

查看是否生效 

go env GOPROXY

go-module迁移解决的问题:

1:可以选择依赖的第三方库版本,这样可以解决有些新旧版本依赖出现问题的情况

2:可以解决本地代码的冗余问题,他会在$HOME/go/pkg/mod下面建立目录保存库的情况

 

迁移步骤:

当前会话打开go-module:export GO111MODULE="on" 全局打开go-module用:go env -w GO111MODULE=on
初始化go.mod:go mod init [host/namespace/module-name] 比如:go mod init code.aliyun.com/nextdata/xxx
将库替换为域名+本地路径。
go install xxxx.go执行
 

报错1:Get "https://goproxy.io/github.com/cos-go/@v/list": x509: certificate has expired or is not yet valid: current time 2020-06-03T10:23:37+08:00 is after 2020-05-30T10:48:38Z
解决1:将go env -w GOPROXY=https://goproxy.io,direct改为go env -w GOPROXY=https://goproxy.cn,direct

报错2: cannot find module providing package xxx: invalid github.com/ import path "xxx"
解决2:将本地存在的库,但是远程拉取失败的,自己建一个git仓库进行拉取。

报错3:如果遇到拉取不下来的域名地址,
解决3:使用go env -w GOPROXY=https://goproxy.cn,direct设置全局代理

报错4:编译时,提示:$GOPATH/go.mod exists but should not
解决4:设置了 GOPATH,把相应设置去掉即可。

17.string转为基本数据类型
"C:\Users\yys53\OneDrive - wtfyd\gocode\main.go"

package main
import (
	"fmt"
	"strconv"
)
func main(){
	//string --> bool
	var s1 string = "true"
	var b bool
	//parsbool 这个函数的返回值有两个(value bool,err error)
	//value就是我们得到的布尔类型的数据,err出现的错误
	//我们只关注得到布尔类型的数据,err可以用——直接忽略
	b, _ = strconv.ParseBool(s1)
	fmt.Printf("b的类型是:%T,b=%v \n",b,b)
	// string-->> int64
	var s2  string = "19"
	var num1 int64
	num1, _=strconv.ParseInt(s2, 10, 64)
	fmt.Printf("num1的类型是:%T,num1=%v \n",num1,num1)
	// string-->> float32/float64
	var s3  string = "3.14"
	var f1 float64
	f1, _=strconv.ParseFloat(s3, 64)
	fmt.Printf("f1的类型是:%T,f1=%v \n",f1,f1)

	//注意:string向基本数据类型转换的时候,一定确保string类型能够转成有效的数据类型,否则最后得到结果是按照对应类型的默认值输出
	var s4 string = "golong"
	var b1 bool
	b1, _ = strconv.ParseBool(s4)
	fmt.Printf("b1的类型是:%T,b1=%v \n",b1,b1)

	var s5  string = "golong"
	var num2 int64
	num2, _=strconv.ParseInt(s5, 10, 64)
	fmt.Printf("num2的类型是:%T,num2=%v \n",num2, num2)

}

PS C:\Users\yys53\OneDrive - wtfyd\gocode> go run .\main.go
b的类型是:bool,b=true
num1的类型是:int64,num1=19
f1的类型是:float64,f1=3.14
b1的类型是:bool,b1=false
num2的类型是:int64,num2=0

18.指针

1.概念

package main
import (
	"fmt"
)

func main(){
	var age int = 18
	// &符合+变量,就可以获取这个变量内存的地址
	fmt.Println(&age)
	//定义一个指针变量
	//var代表要声明一个变量
	//ptr 指针变量名字
	//ptr 对应的类型是:*int 是一个指针类型(可以理解为指向int类型的指针)
	//&age就是一个地址,是ptr变量的具体的值
	var ptr *int = &age
	fmt.Println(ptr)
	fmt.Println("ptr本身这个存储空间的地址为:", &ptr)
	//想获取ptr这个指针或着这个地址指向的那个数据:
	fmt.Printf("ptr指向的数值为:%v", *ptr)

}

PS C:\Users\yys53\OneDrive - wtfyd\gocode> go run .\zzmain.go
0xc000016088
0xc000016088
ptr本身这个存储空间的地址为: 0xc00000a030
ptr指向的数值为:18

 

总计:最重要就是两个符号:

  • &取内存地址
  • *根据地址取值

2.指针4个细节

①可以通过指针改变指向值

package main
import(
	"fmt"
)

func main(){
	var num int = 13
	fmt.Println(num)
	var ptr *int = &num
	*ptr = 20
	fmt.Println(num)
}

②指针变量接收一定是地址值

PS C:\Users\yys53\OneDrive - wtfyd\gocode> go run .\zzdetail.go
# command-line-arguments
.\zzdetail.go:9:17: cannot use num (variable of type int) as type *int in variable declaration

③指针变量的地址不可以不匹配

PS C:\Users\yys53\OneDrive - wtfyd\gocode> go run .\zzdetail.go
# command-line-arguments
.\zzdetail.go:9:21: cannot use &num (value of type *int) as type *float32 in variable declaration

④基本数据类型(又叫值类型),都有对应的指针类型,形式为*数据类型,比如int的对应的指针就是*int,float32对应的指针类型就是*float32。依次类推。
 

19.标识符

关键字

关键字即是被Go语言赋予了特殊含义的单词,也可以称为保留字。

Go语言中的关键字一共有 25 个:
 

break default  func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var


之所以刻意地将Go语言中的关键字保持的这么少,是为了简化在编译过程中的代码解析。和其它语言一样,关键字不能够作标识符使用。

标识符

标识符是指Go语言对各种变量、方法、函数等命名时使用的字符序列,标识符由若干个字母、下划线_、和数字组成,且第一个字符必须是字母。通俗的讲就是凡可以自己定义的名称都可以叫做标识符。

下划线_是一个特殊的标识符,称为空白标识符,它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用_作为变量对其它变量进行赋值或运算。

在使用标识符之前必须进行声明,声明一个标识符就是将这个标识符与常量、类型、变量、函数或者代码包绑定在一起。在同一个代码块内标识符的名称不能重复。

标识符的命名需要遵守以下规则:

  • 由 26 个英文字母、0~9、_组成;
  • 不能以数字开头,例如 var 1num int 是错误的;
  • Go语言中严格区分大小写;
  • 标识符不能包含空格;
  • 不能以系统保留关键字作为标识符,比如 break,if 等等。


命名标识符时还需要注意以下几点:

  • 标识符的命名要尽量采取简短且有意义;
  • 不能和标准库中的包名重复;
  • 为变量、函数、常量命名时采用驼峰命名法,例如 stuName、getVal;


当然Go语言中的变量、函数、常量名称的首字母也可以大写,如果首字母大写,则表示它可以被其它的包访问(类似于 Java 中的 public);如果首字母小写,则表示它只能在本包中使用 (类似于 Java 中 private)。

在Go语言中还存在着一些特殊的标识符,叫做预定义标识符,如下表所示:
 

append bool byte cap close complex complex64 complex128 uint16
copy false float32 float64 imag int int8 int16 uint32
int32 int64 iota len make new nil panic uint64
print println real recover string true uint uint8 uintptr


预定义标识符一共有 36 个,主要包含Go语言中的基础数据类型和内置函数,这些预定义标识符也不可以当做标识符来使用。

注意.可以用如下形式,但是不建议: var int int = 10 (int,float32,float64等不算是保留关键字,但是也尽量不要使用)
 

20.获取用户终端输入Scanln和Scanf
 

package main
import "fmt"

func main(){
	fmt.Println("方法1:scanln")
	//实现功能:键盘录入学生的年龄,姓名
	var age int
	fmt.Println("请输入学生年龄:")
	//传入age地址目的:在Scanln函数中,对地址中的值进行改变的时候,实际外面的age被影响了
	fmt.Scanln(&age) //录入数据的时候,类型一定要匹配,因为底层会自动判断类型的

	var name string
	fmt.Println("请输入学生姓名:")
	fmt.Scanln(&name)

	fmt.Printf("学生年龄为:%v, 姓名: %s", age ,name)
	fmt.Println("方法2:scanf")
	fmt.Println("请输入学生年龄,姓名,以空格隔开")
	
	fmt.Scanf("%v %s", &age, &name)
	fmt.Printf("学生年龄为:%v, 姓名: %s", age ,name)



}


PS C:\Users\yys53\OneDrive - wtfyd\gocode> go run .\scan.go
方法1:scanln
请输入学生年龄:
19
请输入学生姓名:
yys
学生年龄为:19, 姓名为yys方法2:scanf
请输入学生年龄,姓名,以空格隔开
26 杨永生
学生年龄为:26, 姓名为杨永生

21.switch
【3】注意事项:

(1) switch后是一个表达式(即:常量值、变量、一个有返回值的函数等都可以)
(2) case后面的表达式如果是常量值(字面量),则要求不能重复
(3) case后的各个值的数据类型,必须和switch的表达式数据类型一致
(4) case后面可以带多个值,使用逗号间隔。比如case值1,值2...
(5) case后面不需要带break
(6) default语句不是必须的,位置也是随意的。
(7) switch后也可以不带表达式,当做if分支来使用
(8) switch后也可以直接声明/定义一个变量,分号结束,不推荐
(9) switch穿透,利用fallthrough关键字,如果在case语句块后增加falthrough ,则会继续执行下一个case,也叫switch穿透。

 

 


air后台运行命令

使用 air(一个 Go 热重载工具)开发 Go 项目的主要好处是:


✅ 1. 自动热重载(Hot Reload)

  • 当你修改 .go 源码、.tmpl 模板、.env 等文件时,air 会自动重启你的应用。
  • 不需要手动停止、重新编译、运行。
  • 大幅提升开发效率,修改代码后几乎立刻看到效果。

✅ 2. 零配置上手简单

  • 只需在项目根目录创建 .air.toml 或使用默认配置即可运行:

    air
  • 自动寻找 main.go 并编译运行。


✅ 3. 自定义构建与运行命令

  • 可以自定义构建前置命令、运行参数,适配各种复杂项目需求。

例如配置文件 .air.toml 中:

[build]
cmd = "go build -o ./tmp/main ."
bin = "tmp/main"

✅ 4. 快速反馈适合前后端联调

  • 前端(HTML/JS/CSS)改了,Go 模板渲染立即生效。
  • 非常适合开发 SSR(服务端渲染)、API 服务、管理后台等。

✅ 5. 支持颜色日志输出、清屏、延迟构建等功能

  • 日志有颜色、清晰易读。
  • 可设置构建延迟,防止频繁重启。
[log]
time = true
color = true

✅ 6. 跨平台(Windows / macOS / Linux)

  • 可用于本地开发,也适合在远程开发容器中使用。

✅ 适合场景

  • Go Web 应用(如使用 net/httpginechofiber 等)
  • 模板驱动的博客 / CMS
  • API 后端开发
  • 命令行工具的迭代调试

📌 总结一句话:

Air 是 Go 开发者提升效率最实用的工具之一,让你专注写代码,省去不停手动重启的烦恼。

如果你需要,我也可以帮你配置一份 .air.toml 文件适配你的项目结构。是否需要?

0.安装

go install github.com/air-verse/air@latest

1. 用 nohup + 重定向 + 

# 在项目根目录运行:
nohup air > air.log 2>&1 </dev/null & disown
  • nohup:让进程不受终端退出影响
  • > air.log:把标准输出写入 air.log
  • 2>&1:把标准错误也重定向到同一个文件
  • &:放到后台执行
  • </dev/null:让进程的 stdin 直接连到空设备,不依赖任何终端。

  • disown:把这个后台作业从当前 shell 的 job table 中移除,就算 shell 退出也不再追踪它。

执行后会在当前目录生成 nohup.out(如果你不指定输出文件)或者你指定的 air.log,你可以:

tail -f air.log

随时查看最新日志。


2. 用 screen 或 tmux

如果你希望能随时「attach」回去看完整终端:

# 启动一个新的 screen 会话
screen -S air-dev

# 在 screen 里运行
air | tee air.log

# 按 Ctrl+A 然后 D 键,detach 回到 shell,air 依旧在后台跑

# 以后你可以用
screen -r air-dev
# 重新 attach 会话

tmux 的用法类似:

tmux new -s air-dev
air | tee air.log
# Ctrl+B D 退出会话
tmux attach -t air-dev

3. 写一个 systemd 服务(生产或者持久化场景)

在 /etc/systemd/system/air.service 写入:

[Unit]
Description=Go Air Live Reload
After=network.target

[Service]
Type=simple
WorkingDirectory=/path/to/your/project
ExecStart=/usr/local/bin/air
Restart=always
StandardOutput=file:/var/log/air.log
StandardError=file:/var/log/air.log

[Install]
WantedBy=multi-user.target

然后执行:

sudo systemctl daemon-reload
sudo systemctl enable air
sudo systemctl start air

日志会写到 /var/log/air.log,并且开机自动启动 + 崩溃自动重启。


小结

  • 开发时nohup air > air.log 2>&1 & 最简单。
  • 交互式查看:用 screen/tmux + tee,既有日志文件又能 attach。
  • 生产化:写 systemd 服务,持久、可开机自启。