环境变量
GOROOT:
- GOROOT 指向的是 Go 的安装路径。当你导入 Go 内部库时,Go 会在 GOROOT 路径下寻找相关的库文件。
GOARCH:
- GOARCH 表示目标机器的处理器架构,例如 386、amd64 或 arm。这意味着你可以编译适用于不同处理器架构的 Go 程序。
GOOS:
- GOOS 表示目标机器的操作系统,例如 darwin、freebsd、linux 或 windows。这允许你编译适用于不同操作系统的 Go 程序。
GOPATH:
GOPATH 是一个路径,用来存放第三方库和项目的工作空间。
bin目录存放编译后生成的可执行文件。pkg目录存放编译后的包/库的归档文件。src目录存放项目和库的源文件。
在 Go 中,并没有项目的概念,而是有 包 这样的概念。在 GOPATH 目录中,第一个路径是默认的包目录,意味着使用 go get 下载的包都会存放在第一个路径的 src 目录下。而在使用 go install 编译时,生成的可执行文件会存放在相应 GOPATH 的 bin 目录下。
常见命令
go mod init // 初始化go.mod
go mod tidy // 更新依赖文件
go mod download // 下载依赖文件
go mod vendor // 将依赖转移至本地的vendor文件
go mod edit // 手动修改依赖文件
go mod graph // 打印依赖图
go mod verify // 校验依赖
go mod why // 解释为什么需要依赖设置代理
go env -w GO111MODULE=on
go env -w GOPROXY=https://goproxy.cn,direct设置32位
go env -w GOARCH=386 // 设置32位
go env -w GOARCH=amd64 // 设置64位
在Go语言中,整数类型(int 和 uint 系列)非常丰富,能够根据不同的需求选择适当的整数类型。相比于C/C++中的int类型,Go提供了更多细化的类型,以适应不同位数和符号要求的整数值。下面是对Go语言中各int类型的总结:
整数类型
整数类型:
int8:占用1字节,取值范围为-128到127。int16:占用2字节,取值范围为-32768到32767。int32:占用4字节,取值范围为-2147483648到2147483647。int64:占用8字节,取值范围为-9223372036854775808到9223372036854775807。int:在64位系统上通常为64位,在32位系统上为32位。Go会根据平台自动确定其字节大小。
无符号整数类型:
uint8:占用1字节,取值范围为0到255。uint16:占用2字节,取值范围为0到65535。uint32:占用4字节,取值范围为0到4294967295。uint64:占用8字节,取值范围为0到18446744073709551615。uint:与int类似,大小与平台有关,64位系统上为64位,32位系统上为32位。
在给出的Go语言代码中,我们可以看到如何使用Go语言的整数类型以及类型转换。
整数类型范围
代码使用math包中的常量,输出了Go语言中各整数类型的最小值和最大值:
fmt.Println("int8:", math.MinInt8, "~", math.MaxInt8)
fmt.Println("int16:", math.MinInt16, "~", math.MaxInt16)
fmt.Println("int32:", math.MinInt32, "~", math.MaxInt32)
fmt.Println("int64:", math.MinInt64, "~", math.MaxInt64)推导类型字节大小
通过n := 1234567890,Go语言会根据值推导出变量n的类型,输出该类型的信息:
fmt.Printf("n := 1234567890 的默认类型为:%T\n", n)
fmt.Println("int类型的字节数为:", unsafe.Sizeof(n))在这个例子中,n的类型会根据它的值推导为int(在64位系统上是int64),并通过unsafe.Sizeof函数输出字节大小。
类型转换
Go语言支持类型转换,但对于不同类型之间的转换可能会发生截断或溢出。代码示范了将int32类型的变量a转换为其他整数类型:
var a int32 = 987654321
b := int8(a) // 转换为int8,发生数值截断
c := int16(a) // 转换为int16,发生数值截断
d := int64(a) // 转换为int64在这种转换中,如果值超出了目标类型的取值范围,就会发生截断,导致数据丢失。
操作注册表
package main
import (
"golang.org/x/sys/windows/registry"
)
func main() {
key, exists, _ := registry.CreateKey(registry.CURRENT_USER, `SOFTWARE\Hello Go\`, registry.ALL_ACCESS)
defer key.Close()
// 判断是否已经存在了
if exists {
println(`键已存在`)
} else {
println(`新建注册表键`)
}
// 写入:32位整形值
key.SetDWordValue(`32位整形值`, uint32(123456))
// 写入:64位整形值
key.SetQWordValue(`64位整形值`, uint64(123456))
// 写入:字符串
key.SetStringValue(`字符串`, `hello`)
// 写入:字符串数组
key.SetStringsValue(`字符串数组`, []string{`hello`, `world`})
// 写入:二进制
key.SetBinaryValue(`二进制`, []byte{0x11, 0x22})
// 读取:字符串
s, _, _ := key.GetStringValue(`字符串`)
println(s)
// 读取:一个项下的所有子项
keys, _ := key.ReadSubKeyNames(0)
for _, key_subkey := range keys {
// 输出所有子项的名字
println(key_subkey)
}
// 创建:子项
subkey, _, _ := registry.CreateKey(key, `子项`, registry.ALL_ACCESS)
defer subkey.Close()
// 删除:子项
// 该键有子项,所以会删除失败
// 没有子项,删除成功
registry.DeleteKey(key, `子项`)
}网络请求
HTTP 协议定义了多种请求方法,用于向服务器发送请求。在日常开发中,以下是常用的五种请求方法及其作用:
| 方法 | 描述 |
|---|---|
| GET | 请求指定的页面信息,并返回实体主体。 |
| POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
| PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
| DELETE | 请求服务器删除指定的页面。 |
| PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新。 |
GET 请求
resp, err := http.Get("http://localhost:8080/get")
if err != nil {
log.Printf("get请求失败 error: %+v", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("读取Body失败 error: %+v", err)
return
}
log.Println(string(body))http.Get发送 GET 请求,获取指定 URL 的响应。
POST 请求
resp, err := http.Post("http://localhost:8080/post", "text/json;charset=utf-8", strings.NewReader("{\"name\":\"jack\"}"))
if err != nil {
log.Printf("post请求失败 error: %+v", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("读取Body失败 error: %+v", err)
return
}
log.Println(string(body))http.Post发送 POST 请求,strings.NewReader用于构造请求体。
POSTForm 请求
resp, err := http.PostForm("http://localhost:8080/postForm", url.Values{"name": {"jack"}})
if err != nil {
log.Printf("postForm请求失败 error: %+v", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("读取Body失败 error: %+v", err)
return
}
log.Println(string(body))http.PostForm用于发送表单数据请求,数据以url.Values形式传递。
HEAD 请求
resp, err := http.Head("http://localhost:8080/head")
if err != nil {
log.Printf("Head请求失败 error: %+v", err)
return
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("读取Body失败 error: %+v", err)
return
}
log.Println(string(body))http.Head请求资源的头信息,不返回响应体。
复杂请求(POST 请求 + 请求头)
type User struct {
Id int `json:"id"`
Name string `json:"name"`
}
user := User{Id: 1, Name: "bob"}
jsonBytes, err := json.Marshal(user)
client := &http.Client{}
request, err := http.NewRequest("POST", "http://localhost:8080/complex?env=dev", bytes.NewReader(jsonBytes))
request.Header.Add("Cookie", "123")
request.Header.Add("Content-Type", "application/json;charset=utf-8")
request.Header.Add("Token", "456")
client.Timeout = 1 * time.Minute
resp, err := client.Do(request)
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Printf("读取Body失败 error: %+v", err)
return
}
log.Println(string(body))http.NewRequest用于创建复杂的 HTTP 请求,包括自定义请求方法、请求头等。
完整的 HTTP 请求(GET、POST、PUT、PATCH、DELETE)
func TestHttpGet(t *testing.T) { ... }
func TestHttpGetHeader(t *testing.T) { ... }
func TestHttpPost(t *testing.T) { ... }
func TestHttpPostForm(t *testing.T) { ... }
func TestHttpPostJSON(t *testing.T) { ... }
func TestHttpPut(t *testing.T) { ... }
func TestHttpPatch(t *testing.T) { ... }
func TestHttpDelete(t *testing.T) { ... }在这些测试方法中,使用了 Go 的 http 包来发送各种 HTTP 请求方法,并处理响应内容。
线程并发编程
Go 语言的并发性是其最具吸引力的特点之一。与其他语言(如 Java)不同,Go 使用轻量级的 Goroutine 来实现并发,这使得 Go 在处理大量并发时非常高效。Goroutine 是一种非常轻量的线程,它的内存开销小,程序可以创建成千上万的 Goroutine 而不会占用太多资源。
创建 Goroutine
创建 Goroutine 使用 go 关键字,它会启动一个并发执行的函数。在下面的示例中,count 函数通过 Goroutine 被并发执行:
package main
import (
"fmt"
"time"
)
func main() {
go count(5, "?")
go count(5, "?")
}
func count(n int, animal string) {
for i := 0; i < n; i++ {
fmt.Println(i+1, animal)
time.Sleep(time.Millisecond * 500)
}
}程序提前退出
- 在上面的代码中,虽然我们创建了两个 Goroutine,但是程序会在 Goroutine 执行之前就退出。因为主函数没有等待 Goroutine 执行完毕。
解决方法
使用 time.Sleep 延迟主线程结束(不推荐,简便,但不优雅)
time.Sleep(time.Second * 1)使用 fmt.Scan 等待用户输入(仍不太优雅)
var stop int
fmt.Scan(&stop)使用 sync.WaitGroup(推荐)
sync.WaitGroup 用于追踪 Goroutine 的执行状态。我们可以通过 Add、Done 和 Wait 方法控制 Goroutine 的执行和等待。
var wg sync.WaitGroup
wg.Add(2) // 添加两个任务
go func() {
count(5, "?")
wg.Done() // 每个 Goroutine 完成时,计数器减1
}()
go func() {
count(5, "?")
wg.Done()
}()
wg.Wait() // 等待所有 Goroutine 完成实现多并发
上面的代码实现了多个 Goroutine 并发执行任务,每个 Goroutine 都会执行 count 函数并在完成后通过 wg.Done() 通知主线程:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup // 计数器
wg.Add(2) // 添加2个线程
go func() {
count(5, "?")
wg.Done() // 运行完毕就在计数器里减1
}()
go func() {
count(5, "?")
wg.Done()
}()
wg.Wait()
}
func count(n int, animal string) {
for i := 0; i < n; i++ {
fmt.Println(n, animal)
time.Sleep(time.Millisecond * 500)
}
}- 这里每个 Goroutine 运行时,独立执行并发的任务,没有直接的通信。
实现 Goroutine 间通信
Go 的 channel 允许 Goroutine 之间传递数据,它是 Go 并发编程的核心之一。通过 channel,多个 Goroutine 可以共享数据或者进行同步。
在下面的代码中,使用了两个 channel 来实现 Goroutine 之间的通信:
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string) // 创建信道 获取信息
c2 := make(chan string)
go func() {
c1 <- "?" // 向 c1 发送数据
time.Sleep(time.Millisecond * 500)
}()
go func() {
c2 <- "?" // 向 c2 发送数据
time.Sleep(time.Millisecond * 500)
}()
for {
select {
case msg := <-c1: // 接收来自 c1 的数据
fmt.Println(msg)
case msg := <-c2: // 接收来自 c2 的数据
fmt.Println(msg)
}
}
}select语句用于从多个 channel 中选择一个可以接收到数据的 channel,并执行相应的操作。- 当数据从任意一个
channel接收到时,程序会打印相应的信息。
程序打包
打包命令
go build main.go // 直接打包,大体积
go build // 也可以不带任何指定使用
go build -ldflags "-w -s -H=windowsgui" // 隐藏DOS窗口,小体积打包(推荐使用)rsrc工具
如果需要为程序添加 UAC 权限或图标,可以使用 rsrc 工具来编译资源文件。可以通过以下两种方式下载 rsrc 工具:
git clone https://github.com/akavel/rsrc.git
go get github.com/akavel/rsrc`添加 UAC 与图标
新建 manifest 文件
新建名为nac.manifest的文件,填写以下内容:<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="requireAdministrator"/> </requestedPrivileges> </security> </trustInfo> </assembly>生成并打包资源文件
使用rsrc工具来编译资源文件:rsrc -manifest nac.manifest -o nac.syso // 编译资源文件 rsrc -manifest nac.manifest -o nac.syso -ico 00.ico // 添加自定义图标编译可执行文件
使用go build命令来生成最终的可执行文件:go build // 编译 Go 源代码并生成可执行文件参数打包
常规编译方式:go build -ldflags="-w -s" -o main.exe无界面编译方式(隐藏DOS窗口):
go build -ldflags="-w -s -H windowsgui" -o main.exe
通过以上步骤,可以生成一个带有 UAC 权限和自定义图标的小体积 Go 程序。
评论 (0)