Golang笔记
目录
Hello World!
package main
import "fmt"
func main() {
fmt.Println("Hello World!")
}
变量定义
package main
import "fmt"
func varInit() {
var a, b int = 3, 4
var s = "Hello"
c, d := true, 3.14
var (
e = "你"
f = 6
)
fmt.Println(a, b, s, c, d, e, f)
}
func main() {
varInit()
}
内建类型
-
bool
-
string
-
有无符号的int, int8, int16, int32, int64
-
uintptr
-
byte = uint8
-
rune= uint32
-
float32, float64, complex64, complex128
-
强制类型转换
常量与枚举
package main
import "fmt"
func consts() {
const filename = "a.txt"
const (
a = 1
b = 2
)
fmt.Println(filename, a, b)
}
func enums() {
const (
windows = iota
linux
mac
)
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
fmt.Println(windows, linux, mac)
fmt.Println(b, kb, mb, gb, tb, pb)
}
func main() {
consts()
enums()
}
条件语句
package main
import (
"fmt"
"io/ioutil"
)
func main() {
const filename = "a.txt"
if content, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(err)
} else {
fmt.Println(content)
}
}
循环语句
package main
import (
"fmt"
"strconv"
)
func convertToBin(n int) string {
result := ""
for ; n > 0; n /= 2 {
result = strconv.Itoa(n%2) + result
}
return result
}
func main() {
fmt.Println(convertToBin(5))
}
函数
package main
import (
"fmt"
)
func eval(a, b int, op string) (int, error) {
switch op {
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
case "/":
return a / b, nil
default:
return 0, fmt.Errorf("unsupported operation: %s", op)
}
}
func main() {
fmt.Println(eval(1, 2, "*"))
}
指针
package main
import (
"fmt"
)
func swap(a, b *int) {
*a, *b = *b, *a
}
func main() {
a, b := 2, 3
// go里的参数传递都是值传递,对于比较大的数据使用指针可以避免资源浪费
swap(&a, &b)
fmt.Println(a, b)
}
ntln(a, b)
}
数组
package main
import "fmt"
func main() {
// 数组是值类型
var (
arr1 [6]int
arr2 = [3]int{1, 2, 3}
arr3 = [...]int{4, 5, 6}
)
fmt.Println(arr1, arr2, arr3)
// range关键字
for i := range arr2 {
fmt.Println(i)
}
for i, v := range arr2 {
fmt.Println(i, v)
}
}
切片
package main
import "fmt"
func modifyArr(s []int) {
s[0] = 99
}
func printSlice(s []int) {
fmt.Printf("len=%d, cap=%d", len(s), cap(s))
fmt.Println("")
}
func main() {
var sli []int // nil
printSlice(sli)
sli = append(sli, 0)
printSlice(sli)
sli1 := make([]int, 8)
fmt.Println(sli1)
arr := [...]int{0, 1, 2, 3, 4}
// s是对arr的一个view,使用切片可以不传指针而方便地修改数组
s := arr[1:3]
fmt.Println(s)
modifyArr(s)
fmt.Println(s) // [99 2]
fmt.Println(arr) // [0 99 2 3 4]
s1 := s[1:3]
fmt.Println(s1) // [2 3]
// slice: ptr(指向切片起始位置), len,cap(从起始位置到数组末尾的长度)
// s[i]不可超越len,s[:n]不可超越cap
s2 := append(s1, 100)
fmt.Println(s2) // [2, 3, 100]
fmt.Println(arr) // [0 99 2 3 100]
originSSlice := []int{0, 1, 2, 3, 4}
destSlice := make([]int, 16)
copy(destSlice, originSSlice)
fmt.Println(destSlice) // [0 1 2 3 4 0 0 0 0 0 0 0 0 0 0 0]
deleteRes := append(originSSlice[:2], originSSlice[3:]...)
fmt.Println(deleteRes) // [0 1 3 4]
fmt.Println(originSSlice) // [0 1 3 4 4]
}
Map
package main
import "fmt"
func main() {
m := map[string]int{
"Windows": 0,
"Mac": 1,
"Linux": 100,
}
m1 := make(map[string]string)
var m2 map[string]int
fmt.Println(m1, m2 == nil) // map[] true
// python3.5之后dict是有序的,但map是无序的
for k, v := range m {
fmt.Println(k, v)
}
fmt.Println(m["android"]) // 会取到对应类型的零值
val, ok := m["nothing"]
fmt.Println(val, ok) // 0 false
delete(m, "Windows")
fmt.Println(m)
}
寻找最长不含重估字符的字串
package main
func solute(s string) {
m := make(map[rune]int)
start := 0
maxLen := 0
for i, v := range []rune(s) {
if old, ok := m[v]; ok && old >= start {
start = old + 1
}
if i-start+1 > maxLen {
maxLen = i - start + 1
}
m[v] = i
}
print(maxLen)
}
func main() {
solute("pwwkew")
}
结构体
package main
import "fmt"
type TreeNode struct {
value int
left, right *TreeNode
}
func CreateNode(value int) *TreeNode {
// 返回局部变量的地址并不会使外界调用时的代码出现问题
return &TreeNode{value: value}
}
func (node TreeNode) PrintNode() {
fmt.Println(node.value)
}
// 需要改变内容、结构过大时应使用指针接收者;保持接收者类型一致性
// 接受者类型并不会直接影响调用
func (node *TreeNode) SetNode(value int) {
// 使用指针接收者以方便修改数据
if node == nil {
// nil指针也可以调用方法
fmt.Println("Ignored")
return
}
node.value = value
}
func (node *TreeNode) Traverse() {
if node == nil {
// 边界条件
return
}
node.left.Traverse()
node.PrintNode()
node.right.Traverse()
}
func main() {
root := TreeNode{value: 3}
root.left = &TreeNode{4, nil, nil}
root.right = &TreeNode{5, nil, nil}
root.right.left = new(TreeNode)
root.left.right = CreateNode(6)
root.Traverse()
}
封装
包
-
每个目录为一个包(不要求包名和目录名一致)
-
main包包含可执行入口
-
为结构定义的方法必须放在同一个包内(的相同或者不同文件)
针对包的权限:标识符首字母大写代表public、首字母小写代表private
扩充已有(系统或自定义)类型
- 使用组合
package trn
import "fmt"
type TreeNode struct {
Value int
Left, Right *TreeNode
}
func CreateNode(value int) *TreeNode {
return &TreeNode{Value: value}
}
func (node *TreeNode) PrintNode() {
fmt.Println(node.Value)
}
func (node *TreeNode) SetNode(value int) {
if node == nil {
fmt.Println("Ignored")
return
}
node.Value = value
}
func (node *TreeNode) Traverse() {
if node == nil {
return
}
node.Left.Traverse()
node.PrintNode()
node.Right.Traverse()
}
package main
import "go_code/trn"
type myTreeNode struct {
node *trn.TreeNode
}
func (myNode *myTreeNode) TraverseL() {
if myNode == nil || myNode.node == nil {
return
}
(&myTreeNode{myNode.node.Left}).TraverseL()
(&myTreeNode{myNode.node.Right}).TraverseL()
myNode.node.PrintNode()
}
func main() {
root := trn.TreeNode{Value: 3}
root.Left = &trn.TreeNode{4, nil, nil}
root.Right = &trn.TreeNode{5, nil, nil}
root.Right.Left = new(trn.TreeNode)
root.Left.Right = &trn.TreeNode{Value: 6}
test := myTreeNode{&root}
test.TraverseL()
}
- 定义别名
package queue
type Q []int
func (q *Q) Push(value int) {
*q = append(*q, value)
}
func (q *Q) Pop() int {
tail := (*q)[len(*q)-1]
*q = (*q)[:len(*q)-1]
return tail
}
func (q *Q) IsEmpty() bool {
return len(*q) == 0
}
package main
import (
"fmt"
"go_code/queue"
)
func main() {
q := queue.Q{1}
q.Push(2)
q.Push(3)
fmt.Println(q)
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q)
fmt.Println(q.IsEmpty())
fmt.Println(q.Pop())
fmt.Println(q)
fmt.Println(q.IsEmpty())
}
- 内嵌
package main
import "go_code/trn"
type myTreeNode struct {
*trn.TreeNode // 内嵌
}
func (myNode *myTreeNode) Traverse() {
if myNode == nil || myNode.TreeNode == nil {
return
}
// 减少代码量myNode.TreeNode.Left -> myNode.Left
(&myTreeNode{myNode.Left}).Traverse()
(&myTreeNode{myNode.Right}).Traverse()
myNode.PrintNode()
}
func main() {
// myTreeNode自动获取内嵌结构体的属性和方法
root := myTreeNode{&trn.TreeNode{Value: 3}}
root.Left = &trn.TreeNode{4, nil, nil}
root.Right = &trn.TreeNode{5, nil, nil}
root.Right.Left = new(trn.TreeNode)
root.Left.Right = &trn.TreeNode{Value: 6}
// 相较于传统面向对象的重载,内嵌仍可调用被shadowed的方法,
// 但不是通过子类对象赋值基类
root.Traverse()
root.TreeNode.Traverse()
}
依赖管理
-
GOPATH:摆烂管理
-
GO VENDER:打补丁
-
GO MOD:直接用
GOPATH
go env -w GOPATH=
go env -w GO111MODULE=off
# 临时
export GOPATH=
export GO111MODULE=off
mkdir src
go get -u xxx.xxx.org/xxx
GO VENDER
每个项目下自建vender目录管理自己的库
GO MOD
import 路径 = go mod 下的module name + 包相对于go mod的相对目录
go env -w GO111MODULE=on
# 切换本地代理
go env -w GOPROXY=https://goproxy.cn,direct
go get -u xxx.xxx.org/xxx@vn.nn
# 清理无用版本
go mod tidy
旧项目迁移GO MOD
go env -w GO111MODULE=on
# 切换本地代理
go env -w GOPROXY=https://goproxy.cn,direct
go mod init modname
# 检查编译是否可以通过,编译全部子目录文件可以考虑使用go install ./...
go build ./...
go中没有类似python中的__name__ == '__main__'
的用法,所以如果要写不同的main入口只能将其放入不同目录
接口
- Go中接口的实现是隐式的
// retriever/main.go
package main
import (
"fmt"
"learn1/retriever/test"
"learn1/retriever/web"
"time"
)
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("http://www.baidu.com")
}
func main() {
var r Retriever
r = web.Retriever{TimeOut: time.Minute}
fmt.Println(download(r))
r = test.Retriever{Contents: "测试"}
fmt.Println(download(r))
}
// retriever/test/retriever.go
package test
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
// retriever/web/retriever.go
package web
import (
"net/http"
"net/http/httputil"
"time"
)
type Retriever struct {
TimeOut time.Duration
}
func (r Retriever) Get(url string) string {
response, err := http.Get(url)
if err != nil {
panic(err)
}
bytes, err := httputil.DumpResponse(response, true)
response.Body.Close()
if err != nil {
panic(err)
}
return string(bytes)
}
- 接口变量包含了实现者类型信息和实现者值信息
// retriever/main.go
package main
import (
"fmt"
"learn1/retriever/test"
"learn1/retriever/web"
"time"
)
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("http://www.baidu.com")
}
func main() {
var r Retriever
r = web.Retriever{TimeOut: time.Minute}
//fmt.Println(download(r))
r = test.Retriever{Contents: "测试"}
//fmt.Println(download(r))
// type assertion
if testRetriever, ok := r.(test.Retriever); ok {
fmt.Println(testRetriever.Contents)
} else {
fmt.Println("web.Retriever")
}
}
- 接口变量自带指针
- 接口变量同样采用值传递,几乎不需要使用接口的指针
- 指针接收者实现只能以指针方式使用,值接收者则无此限制
- 任何类型:interface{}
- 接口的组合
type Retriever interface {
Get(url string) string
}
type Poster interface {
Post(url string) string
}
type WebTool interface {
Retriever
Poster
Delete(target map[string]string) bool
}
- 常用系统接口(很像Python中的某些魔法方法)
- Stringer
- Reader
- Writer
函数式编程
闭包
错误处理与资源管理
defer
- 类似栈,在函数返回前执行,多个defer语句先定义的后执行
- defer语句中的变量定义时确定值
panic
- 停止当前函数执行
- 一直向上范围,执行每一层的defer
- 如果没有遇见recover,程序退出
recover
- 仅在defer调用中使用
- 获取panic的值
- 可重新panic
测试
表格驱动测试
// learn_t/triangle.go
package main
import "math"
func triangle(a, b int) int {
return int(math.Sqrt(float64(a*a + b*b)))
}
//learn_t/triangle_test.go
package main
import "testing"
func TestTriangle(t *testing.T) {
tests := []struct{ a, b, c int }{
{3, 4, 5},
{5, 12, 13},
{6, 8, 10},
{30000, 40000, 50001},
}
for _, te := range tests {
if actual := triangle(te.a, te.b); actual != te.c {
t.Errorf("triangle(%d, %d) got %d expected %d", te.a, te.b, actual, te.c)
}
}
}
// terminal
// go test .
代码覆盖率
#代码覆盖率
go test -coverprofile=c.out
#输出html
go tool cover -html=c.out
// 性能测试
// learn_t/triangle_test.go
package learn1
import "testing"
func BenchmarkTriangle(b *testing.B) {
x, y, z := 3, 4, 5
for i := 0; i < b.N; i++ {
if actual := triangle(x, y); actual != z {
b.Errorf("triangle(%d, %d) got %d expected %d", x, y, actual, z)
}
}
}
// terminal
// go test -bench .
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战