惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
B
Blog
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
腾讯CDC
D
Docker
G
Google Developers Blog
D
DataBreaches.Net
雷峰网
雷峰网
Blog — PlanetScale
Blog — PlanetScale
S
SegmentFault 最新的问题
The Cloudflare Blog
有赞技术团队
有赞技术团队
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Stack Overflow Blog
Stack Overflow Blog
大猫的无限游戏
大猫的无限游戏
量子位
美团技术团队
aimingoo的专栏
aimingoo的专栏
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Engineering at Meta
Engineering at Meta
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - Sam Xiao

在线工具 工作备注笔记 JT808,JT1078 —— AAC编码 —— 部标机语音对讲Java实现 博文阅读密码验证 - 博客园 minio 安装 若依-扩展为多租户版本 npm 错误,ERESOLVE unable to resolve dependency tree 解决方案 openssl 生成多域名 多IP 的数字证书 SSL/TSL 总结 MQTT 实践总结 Windows服务启动exe无界面终极解决方案 大数据技术 nohup Command [ Arg … ] [ & ] centos7 docker安装 pgsql安装与主从配置搭建 rocketmq 搭建配置 定义制造业操作(定义 MES/MOM 系统) dubbo 的三种引用 把nacos安装成windows服务
go 从入门到了解
Sam Xiao · 2021-10-15 · via 博客园 - Sam Xiao

一,GO的安装与配置

官网:https://golang.org/dl/

镜像:https://golang.google.cn/dl/

1,GOPATH

GOPATH在windows上的默认值:%USERPROFILE%/go

Go1.14及之后的版本中启用了Go Module模式之后,不一定非要将代码写到GOPATH目录下,所以也就不需要我们再自己配置GOPATH了

Go1.14之前的版本GOPATH 即为工作目录:本示例设置为 E:\workgo

workgo                  // my-go为GOPATH目录
  -- bin
     -- myApp1        // 编译生成
     -- myApp2        // 编译生成
     -- myApp3        // 编译生成
  -- pkg                  // 依赖包,编译后的归档文件
  -- src
     -- MyApp1        // 项目1
        -- models
        -- controllers
        -- others
        -- main.go 
     -- MyApp2        // 项目2
        -- models
        -- controllers
        -- others
        -- main.go 

2,Proxy

2.1,设置代码

默认GoPROXY配置是:GOPROXY=https://proxy.golang.org,direct,
https://proxy.golang.org访问不是很可靠,常用的两个代理镜像:
代理一:go env -w GOPROXY=https://goproxy.io,direct
代理二:go env -w GOPROXY=https://goproxy.cn,direct

2.2,设置私仓库

私有库设置,不走代理
go env -w GOPRIVATE=*.gitlab.com,*.gitee.com

2.3,验证包的有效性

go mod vendor,验证包的有效性时,Get https://sum.golang.org/lookup/xxxxxx: dial tcp 216.58.200.49:443: i/o timeout
是因为 sum.golang.org 网站不能访问,可以关闭验证包的有效性
go env -w GOSUMDB=off
也可以使用国内的网站验证包的有效性
go env -w GOSUMDB="sum.golang.google.cn"

3,go环境的常用命令:

go run src/main.go

go build src/main.go

./main.exe

go version

go env 

3,项目目录结构

4,开发工具:

4.1,GoLand (关键在于怎么长期使用)

GoLand 下载地址:https://www.jetbrains.com/go/download/other.html

4.2,VS Code

Vs Code 下载地址:https://code.visualstudio.com/Download

Ctrl + Shift + P  安装 go:install  相关命令

5,包管理

1.5版本之前,依赖包放在GOPATH下
1.5版本及之后,采用了vendor机制,主要解决包的版本管理
1.9版本推出了包管理工具,dep
1.11版本推出了包管理工具,modules 简称 mod

初始化一个项目的mod, go mod init <项目名>
go.mod定义:

module用来定义包名
require用来定义依赖包及版本
indirect表示间接引用
在goland中可以用alt+enter来下载依赖包

同一个项目的包引用:
"xcj.com/gose01/src/mypackage"
不同项目的包引用:
a,在go.mod里定义:
require "xcj.com/gose02" v0.0.0
replace "xcj.com/gose02" => "../gose02"
b,在使用的文件里定义:
"xcj.com/gose02/src/testpackage"

指定安装某个版本:go get package@version
例:go get github.com/astaxie/beego@v1.11.1

 跨平台编译:

winows下编译linux可执行文件:
SET CGO_ENABLED=0 // 禁用CGO
SET GOOS=linux // 目标平台是linux
SET GOARCH=amd64 // 目标处理器架构是amd64

windows下编译苹果可执行文件:
SET CGO_ENABLED=0
SET GOOS=darwin
SET GOARCH=amd64

变量声明:

var name string
var age int
var isOk bool

var (
a string
b int
c bool = true
d float32
) // 通过用小括号包含的方式写到多行
var a, b, c int // 变量a、b、c的类型都是int 若不赋值,用零值来初始化
var x, y, z = true, 10.24, "randy" // 推导出变量x、y、z的类型分别为bool、float64、string
var f, err = os.Open(name) // 调用os.Open函数返回一个打开的文件和一个错误信息
短变量:
i, j := 0, 1 // 简短声明一组变量,用于函数内部
a,b: = 200,"hello" // 简短声明一组变量,用于函数内部

匿名变量:

定义:
func Foo(str string) (int, string) {
return 10, str
}
调用:
x, _ := mypackage.Foo("xcj")
_, y := mypackage.Foo("yy")
fmt.Println("x=", x)
fmt.Println("y=", y)

// iota是go语言的常量计数器,只能在常量的表达式中使用
// iota的行记数,只在当前const内生效
// iota在const关键字出现时将被重置为0 ,主要是用于定义枚举
// const同时声明多个常量时,如果省略了值则表示和上面一行的值相同
const (
n1 = iota
n2 = iota
n3 = 0
n4
)
const n5 = iota //0

fmt.Println(n1,n2,n3,n4,n5)     // 输出结果  0 1 0 0 0

格式化输出:

var people interface{} = struct {
name string
age int
}{"sam", 20}
fmt.Printf("%v\n",people) // %v 相应值的默认格式 {sam 20}
fmt.Printf("%+v\n",people) // %+v 打印结构体时,会添加字段名 {name:sam age:20}
fmt.Printf("%#v\n",people) // %#v 相应值的Go语法表示 struct { name string; age int }{name:"sam", age:20}
fmt.Printf("%T\n",people) // %T 相应值的类型的Go语法表示 struct { name string; age int }
fmt.Printf("%v%%\n",100) // %% 字面上的百分号,并非值的占位符 100%

go的数据类型:

除了基本的整型、浮点型、布尔型、字符串外,还有数组、切片、结构体、函数、map、通道(channel)等。

nil是预定义的标识符,代表指针、通道、函数、接口、映射或切片的零值,是预定义好的一个变量

// 浮点数不是一种精确的表达方式
x := 0.1
y := 0.2

res := x + y
fmt.Println(res) // 0.30000000000000004
fmt.Println(res == 0.3) // false

数组与切片:

切片可以理解成一种特殊的数组,切片可以改变存储大小,数组是值拷贝而切片是引用 

// 切片添加值
//var slice []int = []int{3, 6}
//fmt.Println(slice)
//slice = append(slice, 1, 2, 3, 5, 8)
//fmt.Println(slice)

fmt.Println("数组***********************************")
var arr1 [3]int = [3]int{1, 2, 3}
var arr2 [3]int = arr1
fmt.Println(arr1, arr2)
arr2[0] = 10002
fmt.Println(arr1, arr2)

fmt.Println("切片***********************************")
var slice1 []int = []int{1, 2, 3}
var slice2 []int = slice1
fmt.Println(slice1, slice2)
slice2[0] = 10002
fmt.Println(slice1, slice2)

数组的大小是类型的一部分,数组大小不可改变
var a [4]int=[4]int{1,2,3,4}
var a =[4]int{1,2,3,4}
a :=[4]int{1,2,3,4}

interface{} 类型数组,可以包含任意类型

让编译器根据初始值的个数自行推断数组的长度
var numArray = [...]int{1, 2}

使用指定索引值的方式来初始化数组
a := [...]int{1: 1, 3: 5}
fmt.Println(a) // [0 1 0 5]

多组数组
a := [...][2]string{
{"北京", "上海"},
{"广州", "深圳"},
{"成都", "重庆"},
}

切片的本质就是对底层数组的封装,它包含了三个信息:底层数组的指针(地址)、切片的长度(len)和切片的容量(cap)。
var a[9]int = [9]int{0,1,2,3,4,5,6,7,8}
fmt.Println(a)
var b[]int = a[1:2] // 使用数组构建切片
fmt.Println(b)
fmt.Println(len(b))
fmt.Println(cap(b))

a := make([]int, 2, 10)
fmt.Println(a) //[0 0]
var b []int = make([]int, 3)

var citySlice []string
citySlice = append(citySlice, "北京")

流程控制:

// if 用法
if score := 65; score >= 90 {
fmt.Println("A")
} else if score > 75 {
fmt.Println("B")
} else {
fmt.Println("C")
}

// for
// for循环可以通过break、goto、return、panic语句强制退出循环。
i := 0
for i < 10 {
fmt.Println(i)
i++
}

// 循环数组
//names:=[4]string{"张三","李四","王五"}
//for i,v:=range names{
// fmt.Println(i,v)
//}

// 循环切片
//names:=[]string{"张三","李四","王五"}
//for i,v:=range names{
// fmt.Println(i,v)
//}

// 循环字符串
//for i,v:=range "hello你好"{
// fmt.Printf("%d %q\n",i,v)
//}


// map返回键和值
//ages:=map[string]int{
// "张三":20,
// "李四":30,
//}
//for i,v:=range ages{
// fmt.Println(i,v)
//}

fallthrough 语法可以执行满足条件的case的下一个case,无条件执行下一个case(穿透)是为了兼容C语言中的case设计的。
s := "a"
switch {
case s == "a":
fmt.Println("a")
fallthrough // 无条件执行下一个case(穿透)
case s == "b":
// 上一个条件满足之后会执行这条语句 fallthrough
fmt.Println("b")
case s == "c":
fmt.Println("c")
default:
fmt.Println("...")
}

goto跳转:

for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 2 {
// 设置退出标签
goto breakTag
}
fmt.Printf("%v-%v\n", i, j)
}
}
return
// 标签
breakTag:
fmt.Println("结束for循环")

函数作为参数传递:

x11 :=calc(calc(10,20), calc(20,30))
print(x11)

func calc(x, y int) (int) {
sum := x + y
return sum
}

func intSum(x ...int) int {
fmt.Println(x) //x是一个切片
sum := 0
for _, v := range x {
sum = sum + v
}
return sum
}

我们可以使用type关键字来定义一个函数类型
type calculation func(int, int) int
func add(x, y int) int {
return x + y
}

func sub(x, y int) int {
return x - y
}


函数作为参数:
func add(x, y int) int {
return x + y
}
func calc(x, y int, op func(int, int) int) int {
return op(x, y)
}

ret2 := calc(10, 20, add)
fmt.Println(ret2) //30

闭包指的是一个函数和与其相关的引用环境组合而成的实体

func adder() func(int) int {
var x int
return func(y int) int {
x += y
return x
}
}
func main() {
var f = adder() // f = func(y int) int{...}
fmt.Println(f(10)) //10
fmt.Println(f(20)) //30
fmt.Println(f(30)) //60

f1 := adder()
fmt.Println(f1(40)) //40
fmt.Println(f1(50)) //90
}


defer延迟处理,异步处理,最后被defer的语句,最先被执行。
fmt.Println("start")
defer fmt.Println(1)
defer fmt.Println(2)
defer fmt.Println(3)
fmt.Println("end")


start
end
3
2
1


Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)
Go语言中使用&字符放在变量前面对变量进行“取地址”操作。
int、float、bool、string、array、struct
*int、*float、*bool、*string、*array、*struct

//指针取值
a := 10
b := &a // 取变量a的地址,将指针保存到b中
fmt.Printf("type of b:%T\n", b) // type of b:*int
c := *b // 指针取值(根据指针去内存取值)
fmt.Printf("type of c:%T\n", c) //type of c:int
fmt.Printf("value of c:%v\n", c) //value of c:10


func modify1(x int) {
x = 100
}

func modify2(x *int) {
*x = 100
}

func main() {
a := 10
modify1(a)
fmt.Println(a) // 10
modify2(&a)
fmt.Println(a) // 100
}

// 结构体名字or结构体中的字段名字首字母大写表示公开的、可以被其他包导入,小写表示私有、仅在定义当前结构体的包中可访问
// 使用type和struct关键字来定义结构体
// 方法与函数的区别是, 函数不属于任何类型, 方法属于特定的类型。
//Person 结构体
type Person struct {
name string
age int8
}

//NewPerson 构造函数
func NewPerson(name string, age int8) *Person {
return &Person{
name: name,
age: age,
}
}

//Dream Person做梦的方法
func (p Person) Dream() {
fmt.Printf("%s的梦想是学好GO!\n", p.name)
}

func main() {
p1 := NewPerson("路人A", 25)
p1.Dream()
}