go 基础
地址: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 packagesUse "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 GOVCSUse "go help <topic>" for more information about that topic.
要永久设置 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 模块将从阿里云源下载。
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
。
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 的版本覆盖它。
如果在覆盖期间发生错误,原始文件将从自动备份中恢复
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
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
init
├── lib1
│ └── lib1.go
├── lib2
│ └── lib2.go
└── main.go
main.go |
lib1/lib1.go |
lib2/lib2.go |
7.go语言导入包的几种方式
1.操作,注意前面的点
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.忽略此包
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
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
一般通过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]
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 = 首尔
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 张三}
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类型
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 [周星驰 张伯之]}
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结束
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...
挂国内镜像源
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
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。依次类推。
关键字
关键字即是被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 |
println | real | recover | string | true | uint | uint8 | uintptr |
预定义标识符一共有 36 个,主要包含Go语言中的基础数据类型和内置函数,这些预定义标识符也不可以当做标识符来使用。
注意.可以用如下形式,但是不建议: var int int = 10 (int,float32,float64等不算是保留关键字,但是也尽量不要使用)
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/http
,gin
,echo
,fiber
等) - 模板驱动的博客 / 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
服务,持久、可开机自启。
本文作者: 永生
本文链接: https://www.yys.zone/detail/?id=260
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
评论列表 (0 条评论)