This commit is contained in:
Frank 2023-03-01 11:23:26 +08:00
commit e6ee860fd9
46 changed files with 1628 additions and 0 deletions

29
.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
### Go template
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
.idea
# .vscode
app.env
.DS_Store
*/.DS_Store

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"go.autocompleteUnimportedPackages": true,
"go.testFlags": [
"-v",
"-cover"
]
}

73
README.MD Normal file
View File

@ -0,0 +1,73 @@
# Go学习
### 编写hello world程序
- package 必须是 main
- 方法名必须是 main()
- 文件名不一定是 main.go
### 和其他语言差异
- Go中 main 函数不支持任何返回值
- 通过os.Exit来返回状态
- main 函数不支持传入参数
- 通过 os.Args 获取命令行参数
### 编写测试程序
1. 源码文件以 `_test` 结尾: xxx_test.go
2. 测试方法名以 Test 开头: func TestXXX(t *testing.T) {...}
### 变量赋值
- 赋值可以自动进行类型推断
- 在一个赋值语句中可以对多个变量进行同时赋值
```go
// 单独赋值
var a int = 1
var b int = 1
// 同时赋值
var (
a int = 1
b int = 1
)
// 自动推断
a := 1
b := 1
```
### 基本数据类型
```go
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32, represents a Unicode code point
float32 float64
complex64 complex128
```
### 类型转化
- 与其他编程语言的差异
1. Go不允许隐式类型转换
2. 别名和原有类型也不能进行隐式类型转换
- 类型的预定义值
1. math.MaxInt64
2. math.MaxFloat64
3. math.MaxUint32
### 算术运算符
- Go语言没有前置的 ++, --,
### 线程和协程 Thread vs. Groutine
1. 创建时默认的 stack (栈) 的大小
- JDK5 以后 Java Thread stack 默认为 1M
- Groutine 的 stack 初始化大小为 2k
2. 和 KSE (Kernel Speace Entity) 的对应关系
- Java Thread 是 1:1
- Groutine 是 M:N

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module go_study
go 1.20
require github.com/go-playground/assert/v2 v2.2.0

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=

60
src/c10_func/func_test.go Normal file
View File

@ -0,0 +1,60 @@
package func_test
import (
"fmt"
"math/rand"
"testing"
"time"
)
// 多返回值函数
func returnMultiValues() (int, int) {
return rand.Intn(10), rand.Intn(20)
}
// 函数用法之一: 统计执行时间
func timeSpent(inner func(op int) int) func(op int) int {
return func(n int) int {
start := time.Now()
ret := inner(n)
fmt.Println("time spent: ", time.Since(start).Seconds())
return ret
}
}
func slowFun(op int) int {
time.Sleep(time.Second * 1)
return op
}
func TestFn(t *testing.T) {
a, b := returnMultiValues()
t.Log(a, b)
tsSF := timeSpent(slowFun)
t.Log(tsSF(10))
}
// 求和
func Sum(ops ...int) int {
ret := 0
for _, op := range ops {
ret += op
}
return ret
}
func TestVarParam(t *testing.T) {
t.Log(Sum(1, 3, 5, 7, 9, 2, 6))
}
func Clear() {
fmt.Println("Clear resources")
}
// defer 延迟执行 一般用于资源的释放和异常的捕捉
func TestDefer(t *testing.T) {
defer Clear()
fmt.Println("Start")
panic("program error") //抛出异常
}

View File

@ -0,0 +1,29 @@
package customer_tpye_test
import (
"fmt"
"testing"
"time"
)
// 自定义类型
type IntConv func(op int) int
func timeSpent(inner IntConv) IntConv {
return func(n int) int {
start := time.Now()
ret := inner(n)
fmt.Println("time spent: ", time.Since(start).Seconds())
return ret
}
}
func slowFun(op int) int {
time.Sleep(time.Second * 1)
return op
}
func TestFn(t *testing.T) {
tsSF := timeSpent(slowFun)
t.Log(tsSF(10))
}

View File

@ -0,0 +1,48 @@
package encap_test
import (
"fmt"
"testing"
"unsafe"
)
type Employee struct {
Id string
Name string
Age int
}
// 指针形式 复用 推荐使用
func (e *Employee) String() string {
fmt.Printf("Address is %x \n", unsafe.Pointer(&e.Name))
return fmt.Sprintf("Id: %s, Name: %s, Age: %d", e.Id, e.Name, e.Age)
}
// 数据复制, 地址变了
// func (e Employee) String() string {
// fmt.Printf("Address is %x \n", unsafe.Pointer(&e.Name))
// return fmt.Sprintf("Id: %s, Name: %s, Age: %d", e.Id, e.Name, e.Age)
// }
func TestCreateEmployeeObj(t *testing.T) {
e := Employee{"1", "Frank", 20}
e1 := Employee{Name: "Mike", Age: 28}
e2 := new(Employee) //返回指针
e2.Id = "3"
e2.Name = "Jack"
e2.Age = 32
t.Log(e)
t.Log(e1)
t.Log(e1.Id) //空字符串
t.Log(e2)
t.Logf("e is %T", e)
t.Logf("e2 is %T", e2)
}
func TestStructOperations(t *testing.T) {
e := Employee{"1", "Frank", 20}
fmt.Printf("Address is %x \n", unsafe.Pointer(&e.Name))
t.Log(e.String())
}

View File

@ -0,0 +1,20 @@
package interface_test
import "testing"
type Programmer interface {
WriteWord() string
}
type GoProgrammer struct {
}
func (p *GoProgrammer) WriteWord() string {
return "hello world"
}
func TestClient(t *testing.T) {
var p Programmer
p = new(GoProgrammer)
t.Log(p.WriteWord())
}

View File

@ -0,0 +1,68 @@
package extension_test
import (
"fmt"
"testing"
)
// golang是可以实现继承的但是这种继承并不是严格意义上的继承
// golang并不支持继承特性因而也没有单继承,多继承,重写方法等复杂概念。
type Pet struct {
}
func (p *Pet) Speak() {
fmt.Println("...")
}
func (p *Pet) SpeakTo(host string) {
p.Speak()
fmt.Println(host)
}
// 有名继承
type Dog struct {
p *Pet
}
func (d *Dog) Speak() {
fmt.Println("汪汪汪!")
}
func TestDog(t *testing.T) {
dog := new(Dog)
dog.Speak()
// dog.SpeakTo("Zhang") Dog没有这个方法, 虽然Pet有但是此时Dog不是匿名继承 ,如果要要调用需要指定继承事指定的名称。
dog.p.Speak()
dog.p.SpeakTo("Li")
}
// 匿名继承
type People struct {
}
func (p *People) ShowA() {
fmt.Println("AAA")
}
func (p *People) ShowB() {
fmt.Println("BBB")
}
type Doctor struct {
People
}
func (p *Doctor) ShowA() {
fmt.Println("Doctor AAA")
}
func TestDoctor(t *testing.T) {
d := new(Doctor)
d.ShowA()
d.ShowB() // 因为Teacher没有ShowB()的方法此时又匿名继承了People所以会调到People实现的ShowB方法
d.People.ShowA()
d.People.ShowB()
}

View File

@ -0,0 +1,41 @@
package polymorphism
import (
"fmt"
"testing"
)
// 多态
type Code string
type Programmer interface {
WriteHelloWorld() Code
}
type GoProgrammer struct {
}
func (gp *GoProgrammer) WriteHelloWorld() Code {
return "fmt.Println(\"hello world\")"
}
type JavaProgrammer struct {
}
func (jp *JavaProgrammer) WriteHelloWorld() Code {
return "System.out.Println(\"hello world\")"
}
func writeFirstProgram(p Programmer) {
// %T 输出类型
fmt.Printf("%T %v\n", p, p.WriteHelloWorld())
}
func TestPolymorphism(t *testing.T) {
gp := new(GoProgrammer)
// gp := &GoProgrammer{}
jp := new(JavaProgrammer)
writeFirstProgram(gp)
writeFirstProgram(jp)
}

View File

@ -0,0 +1,40 @@
package empty_interface
import (
"fmt"
"testing"
)
// 空接口与断言
// 1.空接口可以表示任何类型
// 2.通过断言来将空接口转换为制定类型
// 空接口入参
func DoSomething(p interface{}) {
// if i, ok := p.(int); ok {
// fmt.Println("Integer", i)
// return
// }
// if s, ok := p.(string); ok {
// fmt.Println("String", s)
// return
// }
// fmt.Println("Unknown Type")
switch v := p.(type) {
case int:
fmt.Println("Integer", v)
case string:
fmt.Println("String", v)
default:
fmt.Println("Unknown Type")
}
}
// 测试空接口入参断言
func TestEmptyInterfaceAssertion(t *testing.T) {
DoSomething(10)
DoSomething("1231")
DoSomething(8.88)
}

View File

@ -0,0 +1,45 @@
package error_test
import (
"errors"
"fmt"
"testing"
)
/*
Go的错误机制
1.没有异常机制
2.error类型实现了error接口
3.可以通过 errors.New 来快速创建错误实例
*/
var ErrorLessThanTwo = errors.New("n should be not less than 2")
var ErrorLargerThenHundred = errors.New("n should be not larger than 100")
func GetFibonacci(n int) ([]int, error) {
if n < 2 {
return nil, ErrorLessThanTwo
}
if n > 100 {
return nil, ErrorLargerThenHundred
}
fibList := []int{1, 1}
for i := 2; i < n; i++ {
fibList = append(fibList, fibList[i-2]+fibList[i-1])
}
return fibList, nil
}
func TestGetFibonacci(t *testing.T) {
if ret, err := GetFibonacci(-10); err != nil {
if err == ErrorLessThanTwo {
fmt.Println("It is less.")
}
t.Error(err)
} else {
t.Log(ret)
}
}

View File

@ -0,0 +1,34 @@
package panic_recover
import (
"errors"
"fmt"
"testing"
)
// Let it Crash 往往是恢复不确定性错误的最好方法
// panic 用于不可以恢复的错误
// panic 退出前会执行 defer 指定的内容
// os.Exit 退出时不会调用 defer 指定的函数
// os.Exit 退出时不输出当前调用栈信息
// 常见的错误恢复 recover()
// 当心 recover 成为"恶魔" 形成僵尸服务进程 导致health check失效
func TestPanicVxExit(t *testing.T) {
// defer func () {
// fmt.Println("Finally")
// }()
defer func() {
// recover() 错误恢复 小心使用
if err := recover(); err != nil {
fmt.Println("recovered from ", err)
}
}()
fmt.Println("Start")
panic(errors.New("Something wrong!"))
// os.Exit(-1)
// fmt.Println("end")
}

View File

@ -0,0 +1,18 @@
package client
import (
"go_study/src/c15_package_test/series"
"testing"
)
// 可复用模块 包
// 基本复用单元 以首字母大写来表明可被包外代码访问
// 代码的 package 可以和所在的目录不一致
// 同一目录里的 Go 代码的 package 要保持一致
// 测试复用方法
func TestPackage(t *testing.T) {
t.Log(series.GetFibonacciSeries(5))
// t.Log(series.getSlice()) // 报错 无法访问
}

View File

@ -0,0 +1,14 @@
package series
// GetFibonacciSeries 实现 斐波那契数列
func GetFibonacciSeries(n int) []int {
fibList := []int{1, 1}
for i := 2; i < n; i++ {
fibList = append(fibList, fibList[i-2]+fibList[i-1])
}
return fibList
}
func getSlice() []int {
return []int{1, 2, 3, 4, 5}
}

View File

@ -0,0 +1,18 @@
package groutine_test
import (
"fmt"
"testing"
"time"
)
// 测试协程机制
func TestGroutine(t *testing.T) {
for i := 0; i < 10; i++ {
// 协程
go func(i int) {
fmt.Println(i)
}(i)
}
time.Sleep(time.Millisecond * 50)
}

View File

@ -0,0 +1,89 @@
package share_mem
import (
"fmt"
"sync"
"testing"
"time"
)
// 共享内存并发机制
func TestCounter(t *testing.T) {
counter := 0
for i := 0; i < 5000; i++ {
go func() {
// 不安全的写法
counter++
}()
}
time.Sleep(time.Second * 1)
t.Logf("counter= %d", counter) // 得到的结果会小于 5000
}
// Mutex的使用
func TestCounterThread(t *testing.T) {
var mut sync.Mutex
counter := 0
for i := 0; i < 5000; i++ {
go func() {
defer func() {
mut.Unlock()
}()
mut.Lock()
counter++
}()
}
time.Sleep(time.Second * 1) //去掉 time.Sleep(), counter的结果不符合预期, 小于 5000
t.Logf("counter= %d", counter)
}
// 利用WaitGroup来确定所有任务是否结束
func TestCounterWaitGroup(t *testing.T) {
var mut sync.Mutex
var wg sync.WaitGroup
counter := 0
for i := 0; i < 5000; i++ {
wg.Add(1)
go func() {
defer func() {
mut.Unlock()
}()
mut.Lock()
counter++
wg.Done()
}()
}
wg.Wait() //挂起 等待完成
t.Logf("counter= %d", counter)
}
var rwMutex *sync.RWMutex
var wg *sync.WaitGroup
// 读写锁
func TestRWMutex(t *testing.T) {
rwMutex = new(sync.RWMutex)
wg = new(sync.WaitGroup)
wg.Add(2)
go readData(1)
go readData(2)
wg.Wait()
fmt.Println("func finish")
}
func readData(i int) {
defer wg.Done()
fmt.Println(i, "start locking!")
// 给读操作 上锁
rwMutex.RLock()
// 读数据
fmt.Println(i, "Reading data")
// 休眠1s
time.Sleep(1 * time.Second)
// 读解锁
rwMutex.RUnlock()
// 打印提示信息
fmt.Println(i, "Read over")
}

View File

@ -0,0 +1,41 @@
package csp_test
import (
"fmt"
"testing"
"time"
)
func service() string {
time.Sleep(time.Millisecond * 50)
return "Done"
}
func otherTask() {
fmt.Println("执行一些其他的任务")
time.Sleep(time.Millisecond * 100)
fmt.Println("任务完成")
}
func TestService(t *testing.T) {
fmt.Println(service())
otherTask()
}
func AsyncService() chan string {
retChan := make(chan string, 1) // buffered channel (非阻塞、异步模式) 不会阻塞service
// retChan := make(chan string) // unbuffered channel (阻塞、同步模式) 会阻塞service
go func() {
ret := service()
fmt.Println("返回service结果")
retChan <- ret
fmt.Println("service已退出")
}()
return retChan
}
func TestAsyncService(t *testing.T) {
retCh := AsyncService()
otherTask()
fmt.Println(<-retCh)
}

View File

@ -0,0 +1,34 @@
package select_test
import (
"fmt"
"testing"
"time"
)
func service() string {
time.Sleep(time.Millisecond * 100)
return "Done"
}
func AsyncService() chan string {
retChan := make(chan string, 1) // buffered channel (非阻塞、异步模式) 不会阻塞service
// retChan := make(chan string) // unbuffered channel (阻塞、同步模式) 会阻塞service
go func() {
ret := service()
fmt.Println("返回service结果")
retChan <- ret
fmt.Println("service已退出")
}()
return retChan
}
// 多路选择 select 实现超时机制
func TestSelect(t *testing.T) {
select {
case ret := <-AsyncService():
t.Log(ret)
case <-time.After(time.Millisecond * 100):
t.Error("超时")
}
}

View File

@ -0,0 +1,19 @@
package main
import (
"fmt"
"os"
)
// Go中 main 函数不支持任何返回值
// main 函数不支持传入参数
func main() {
// 通过 os.Args 获取命令行参数
fmt.Println(os.Args)
fmt.Println("Hello Golang!")
os.Exit(0) // 通过os.Exit来返回状态
}
// package 必须是 main
// 方法名必须是 main()
// 文件名不一定是 main.go

View File

@ -0,0 +1,43 @@
package cancel_test
import (
"fmt"
"testing"
"time"
)
func isCancelled(cancelChan chan struct{}) bool {
select {
case <-cancelChan:
return true
default:
return false
}
}
func cancel1(cancelChan chan struct{}) {
cancelChan <- struct{}{}
}
func cancel2(cancelChan chan struct{}) {
close(cancelChan)
}
// 使用close来取消任务
func TestCancel(t *testing.T) {
cancelChan := make(chan struct{}, 0)
for i := 0; i < 5; i++ {
go func(i int, cancelChan chan struct{}) {
for {
if isCancelled(cancelChan) {
break
}
time.Sleep(time.Millisecond * 5)
}
fmt.Println(i, "Done")
}(i, cancelChan)
}
// cancel1(cancelChan)
cancel2(cancelChan)
time.Sleep(time.Second * 1)
}

View File

@ -0,0 +1,35 @@
package cancel_by_context_test
import (
"context"
"fmt"
"testing"
"time"
)
func isCancelled(ctx context.Context) bool {
select {
case <-ctx.Done():
return true
default:
return false
}
}
// 使用context来取消任务
func TestCancel(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
for i := 0; i < 5; i++ {
go func(i int, ctx context.Context) {
for {
if isCancelled(ctx) {
break
}
time.Sleep(time.Millisecond * 5)
}
fmt.Println(i, "Done")
}(i, ctx)
}
cancel()
time.Sleep(time.Second * 1)
}

View File

@ -0,0 +1,56 @@
package channel_close_test
import (
"fmt"
"sync"
"testing"
)
// 生产者
func dataProducer(ch chan int, wg *sync.WaitGroup) {
go func() {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch) // 关闭channel
wg.Done()
}()
}
// 消费者
// func dataReceiver(ch chan int, wg *sync.WaitGroup) {
// go func() {
// for i := 0; i < 10; i++ {
// data := <-ch
// fmt.Println(data)
// }
// wg.Done()
// }()
// }
func dataReceiver(ch chan int, wg *sync.WaitGroup) {
go func() {
for {
if data, ok := <-ch; ok {
fmt.Println(data)
} else {
// 通道关闭 去获取值会立即返回通道定义类型的默认值
fmt.Println(data)
break
}
}
wg.Done()
}()
}
func TestCloseChannel(t *testing.T) {
var wg sync.WaitGroup
ch := make(chan int)
wg.Add(1)
dataProducer(ch, &wg)
wg.Add(1)
dataReceiver(ch, &wg)
wg.Add(1)
dataReceiver(ch, &wg)
wg.Wait()
}

View File

@ -0,0 +1,37 @@
package once_test
import (
"fmt"
"sync"
"testing"
"unsafe"
)
// 利用sync.Once实现 单例模式
type Singleton struct {
}
var singletonInstance *Singleton
var once sync.Once
func GetSingletonObj() *Singleton {
once.Do(func() {
fmt.Println("创建对象")
singletonInstance = new(Singleton)
})
return singletonInstance
}
func TestGetSingletonObj(t *testing.T) {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
obj := GetSingletonObj()
t.Logf("%x", unsafe.Pointer(obj))
wg.Done()
}()
}
wg.Wait()
}

View File

@ -0,0 +1,33 @@
package util_anyone_reply
import (
"fmt"
"runtime"
"testing"
"time"
)
func runTask(id int) string {
time.Sleep(time.Millisecond * 10)
return fmt.Sprintf("结果来自: %d", id)
}
func FirstResponse() string {
numOfRunner := 10
// 使用 buffered channel (非阻塞、异步模式) 来防止协程泄露
ch := make(chan string, numOfRunner)
for i := 0; i < numOfRunner; i++ {
go func(i int) {
ret := runTask(i)
ch <- ret
}(i)
}
return <-ch
}
func TestFirstResponse(t *testing.T) {
t.Log("Before 当前系统协程数: ", runtime.NumGoroutine())
t.Log(FirstResponse())
time.Sleep(time.Second * 1)
t.Log("After 当前协程数: ", runtime.NumGoroutine())
}

View File

@ -0,0 +1,36 @@
package util_all_done
import (
"fmt"
"runtime"
"testing"
"time"
)
func runTask(id int) string {
time.Sleep(time.Millisecond * 10)
return fmt.Sprintf("结果来自: %d", id)
}
func AllResponse() string {
numOfRunner := 10
ch := make(chan string, numOfRunner)
for i := 0; i < numOfRunner; i++ {
go func(i int) {
ret := runTask(i)
ch <- ret
}(i)
}
finalRet := ""
for j := 0; j < numOfRunner; j++ {
finalRet += <-ch + "\n"
}
return finalRet
}
func TestAllResponse(t *testing.T) {
t.Log("Before 当前系统协程数: ", runtime.NumGoroutine())
t.Log(AllResponse())
time.Sleep(time.Second * 1)
t.Log("After 当前协程数: ", runtime.NumGoroutine())
}

View File

@ -0,0 +1,42 @@
package obj_pool
import (
"errors"
"time"
)
type MyObj struct {
}
type ObjPool struct {
buffChan chan *MyObj
}
// 利用channel实现的对象池
func NewObjPool(num int) *ObjPool {
objPool := ObjPool{}
objPool.buffChan = make(chan *MyObj, num)
for i := 0; i < num; i++ {
objPool.buffChan <- &MyObj{}
}
return &objPool
}
func (p *ObjPool) GetObj(timeout time.Duration) (*MyObj, error) {
select {
case ret := <-p.buffChan:
return ret, nil
case <-time.After(timeout): // 超时控制
return nil, errors.New("超时")
}
}
func (p *ObjPool) PutObj(obj *MyObj) error {
select {
case p.buffChan <- obj:
return nil
default:
return errors.New("超出对象池最大容量")
}
}

View File

@ -0,0 +1,27 @@
package obj_pool
import (
"fmt"
"testing"
"time"
)
func TestObjPool(t *testing.T) {
num := 10
pool := NewObjPool(num)
//if err := pool.PutObj(&MyObj{}); err != nil { // 尝试防止超出池大小的对象
// t.Error(err)
//}
for i := 0; i < num; i++ {
if obj, err := pool.GetObj(time.Second * 1); err != nil {
t.Error(err)
} else {
fmt.Printf("%T\n", obj)
if err := pool.PutObj(obj); err != nil {
t.Error(err)
}
}
}
t.Log("Done.")
}

View File

@ -0,0 +1,22 @@
### sync.Pool 对象获取
- 尝试从私有对象获取
- 私有对象不存在, 尝试从当前Processor的共享池获取
- 如果当前Processor共享池也是空的,那么就尝试去其他Processor的共享池获取
- 如果所有子池都是空的, 最后就用用户指定的New函数产生一个新的对象返回
### sync.Pool 对象的生命周期
- GC会清除sync.pool缓存的对象
- 对象的缓存有效期为下一次GC之前
### sync.Pool对象的放回
- 如果私有对象不存在则保存为私有对象
- 如果私有对象存在, 放入当前Processor子池的共享池中
### sync.Pool总结
- 适合于通过复用, 降低复杂对象的创建和GC代价
- 协程安全, 会有锁的开销
- 生命周期受GC影响, 不适合于做连接池等, 需自己管理生命周期的资源的池化

View File

@ -0,0 +1,51 @@
package obj_cache
import (
"runtime"
"sync"
"testing"
)
func TestSyncPool(t *testing.T) {
pool := &sync.Pool{
New: func() interface{} {
t.Log("Create a new object.")
return 100
},
}
v := pool.Get().(int)
t.Log(v)
pool.Put(3)
v1 := pool.Get().(int)
t.Log("put操作后: ", v1)
runtime.GC() // GC 会清除sync.pool中缓存的对象
v2, _ := pool.Get().(int)
t.Log("GC操作后: ", v2)
}
// 协程测试
func TestSyncPoolInMultiGroutine(t *testing.T) {
pool := &sync.Pool{
New: func() interface{} {
t.Log("create object")
return 10
},
}
pool.Put(100)
pool.Put(100)
pool.Put(100)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
t.Log("从pool获取对象: ", pool.Get())
wg.Done()
}(i)
}
wg.Wait()
}

View File

@ -0,0 +1,5 @@
package unit_test
func Square(i int) int {
return i * i
}

View File

@ -0,0 +1,46 @@
package unit_test
import (
"fmt"
"testing"
"github.com/go-playground/assert/v2"
)
// 表格测试法
func TestSquare(t *testing.T) {
// 输入值
inputs := [...]int{1, 2, 3}
// 期待值
expected := [...]int{1, 4, 9}
for i := 0; i < len(inputs); i++ {
ret := Square(inputs[i])
if ret != expected[i] {
t.Errorf("input is %d, the expected is %d, the actual %d", inputs[i], expected[i], ret)
}
}
}
func TestErrorInCode(t *testing.T) {
fmt.Println("Start")
t.Error("Error") // 程序继续执行
fmt.Println("End")
}
func TestFatalInCode(t *testing.T) {
fmt.Println("Start")
t.Fatal("Error") // 程序会中断 不会输出End
fmt.Println("End")
}
// 使用断言Assert测试
func TestSquareWithAssert(t *testing.T) {
// 输入值
inputs := [...]int{1, 2, 3}
// 期待值
expected := [...]int{1, 4, 9}
for i := 0; i < len(inputs); i++ {
ret := Square(inputs[i])
assert.Equal(t, expected[i], ret)
}
}

View File

@ -0,0 +1,36 @@
package constant_test
import "testing"
// 声明常量
const (
// 连续常量写法 借助 iota
Monday = iota + 1
Tuesday
Wednesday
)
const (
// 位运算
Readable = 1 << iota
Writeable
Executable
)
const (
Cat = 1
Dog = 2
Pig = 3
)
func TestConstant(t *testing.T) {
t.Log(Monday, Tuesday)
}
func TestConstant1(t *testing.T) {
a := 1
t.Log(a&Readable, a&Writeable, a&Executable)
t.Log(a&Readable == Readable, a&Writeable == Writeable, a&Executable == Executable)
}

View File

@ -0,0 +1,47 @@
package fib
import (
"testing"
)
func TestFibList(t *testing.T) {
// var a int = 1
// var b int = 1
// var (
// a int = 1
// b int = 1
// )
a := 1
b := 1
t.Log(a)
for i := 0; i < 5; i++ {
t.Log(b)
tmp := a
a = b
b = tmp + a
}
}
func TestExchange(t *testing.T) {
// 其他编程语言常见写法 借助中间变量tmp
a := 1
b := 2
tmp := a
a = b
b = tmp
t.Log(a, b)
// go 值替换
a, b = b, a
t.Log(a, b)
}
func TestDemo(t *testing.T) {
a := "123"
b := "321"
t.Log(&a, &b)
a, b = b, a
t.Log(&a, &b, a, b)
}

View File

@ -0,0 +1,9 @@
package constant_test
import (
"testing"
)
func TestFirst(t *testing.T) {
t.Log("我的第一个go测试程序!")
}

View File

@ -0,0 +1,39 @@
package tpye_test
import (
"math"
"testing"
)
// 自定义类型
type MyInt int64
// 类型转换
func TestImplicit(t *testing.T) {
var a int32 = 1
// 显示类型转换
var b = int64(a)
var c = MyInt(b)
t.Log(a, b, c)
}
// 指针
func TestPoint(t *testing.T) {
a := 1
aPtr := &a
t.Log(a, aPtr)
t.Logf("%T %T", a, aPtr)
}
func TestString(t *testing.T) {
// 不赋值默认空字符串
var s string
t.Log("*" + s + "*")
t.Log(len(s))
t.Log(s == "")
}
func TestMax(t *testing.T) {
t.Log(math.MaxFloat64)
t.Log(math.MaxInt64)
}

View File

@ -0,0 +1,30 @@
package operator_test
import "testing"
// 数组比较
func TestCompareArray(t *testing.T) {
a := [...]int{1, 2, 3, 4}
b := [...]int{1, 2, 3, 5}
d := [...]int{1, 2, 3, 4}
t.Log(a == b)
//c := [...]int{1, 2, 3, 4, 5}
//t.Log(a == c) //报错 长度不同不能比较
t.Log(a == d)
}
const (
// 位运算
Readable = 1 << iota
Writeable
Executable
)
func TestBitClear(t *testing.T) {
a := 7
a = a &^ Readable
a = a &^ Executable
t.Log(a&Readable, a&Writeable, a&Executable)
t.Log(a&Readable == Readable, a&Writeable == Writeable, a&Executable == Executable)
}

View File

@ -0,0 +1,40 @@
package condition_test
import "testing"
// if
func TestIfMultiSec(t *testing.T) {
if a := 1 == 1.0; a {
t.Log("1 == 1")
} else {
t.Log("!=")
}
}
// switch
func TestSwitchMultiCase(t *testing.T) {
for i := 0; i < 5; i++ {
switch i {
case 0, 2:
t.Log("偶数Even")
case 1, 3:
t.Log("基数Odd")
default:
t.Log("it is not 0-3")
}
}
}
// switch2
func TestSwitchCaseCondition(t *testing.T) {
for i := 0; i < 5; i++ {
switch {
case i%2 == 0:
t.Log("偶数Even")
case i%2 == 1:
t.Log("基数Odd")
default:
t.Log("it is default case")
}
}
}

View File

@ -0,0 +1,39 @@
package array_test
import "testing"
// 数组声明
func TestArrayInit(t *testing.T) {
var arr [3]int
arr1 := [4]int{1, 2, 3, 4}
arr3 := [...]int{1, 3, 4, 5}
arr1[1] = 5
t.Log(arr[1], arr[2])
t.Log(arr1, arr3)
}
// 数组遍历
func TestArrayTravel(t *testing.T) {
arr3 := [...]int{1, 3, 4, 5}
for i := 0; i < len(arr3); i++ {
t.Log(arr3[i])
}
for idx, v := range arr3 {
t.Log(idx, v)
}
for _, v := range arr3 {
t.Log(v)
}
}
// 数组截断
func TestArraySection(t *testing.T) {
arr3 := [...]int{1, 2, 3, 4, 5}
// 截取 arr[开始索引(包含), 结束索引(不包含)]
arr3_sec := arr3[1:4]
t.Log(arr3_sec) //2.3.4
t.Log(arr3[1:]) //2,3,4,5
t.Log(arr3[:3]) //1,2,3
}

12
src/c6_loop/loop_test.go Normal file
View File

@ -0,0 +1,12 @@
package loop_test
import "testing"
// for循环
func TestWhileLoop(t *testing.T) {
n := 0
for n < 5 {
t.Log(n)
n++
}
}

View File

@ -0,0 +1,51 @@
package slice_test
import "testing"
// 切片定义
func TestSlienceInit(t *testing.T) {
var s0 []int
t.Log(len(s0), cap(s0))
s0 = append(s0, 1)
t.Log(len(s0), cap(s0))
s1 := []int{1, 2, 3, 4}
t.Log(len(s1), cap(s1))
s2 := make([]int, 3, 5)
t.Log(len(s2), cap(s2))
t.Log(s2[0], s2[1], s2[2])
s2 = append(s2, 4)
t.Log(len(s2), cap(s2))
t.Log(s2[0], s2[1], s2[2], s2[3])
}
// 切片增长演示
func TestSliceGrowing(t *testing.T) {
s := []int{}
for i := 0; i < 10; i++ {
s = append(s, i)
t.Log(len(s), cap(s))
}
}
// 切片的共享内存 演示
func TestSliceShareMemory(t *testing.T) {
letter := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "L"}
letter2 := letter[3:6]
t.Log(letter2, len(letter2), cap(letter2))
l3 := letter[5:8]
t.Log(l3, len(l3), cap(l3))
l3[0] = "U"
t.Log(letter2)
t.Log(letter)
}
// 切片不能比较
func TestSliceCompare(t *testing.T) {
// a := []int{1, 2, 3, 4}
// b := []int{1, 2, 3, 4}
// if a == b {
// t.Log("equals")
// }
}

View File

@ -0,0 +1,50 @@
package map_test
import (
"testing"
)
// map的声明
func TestMapInit(t *testing.T) {
m1 := map[string]int{"a": 1, "b": 2, "c": 3}
t.Log(m1["b"])
t.Logf("len m1=%d", len(m1))
m2 := map[string]int{}
m2["d"] = 4
t.Logf("len m2=%d", len(m2))
m3 := make(map[string]int, 10)
t.Logf("len m3=%d", len(m3))
}
// map判断键key是否存在
func TestAccessNotExistingKey(t *testing.T) {
m1 := map[int]int{}
// 不存在会默认为0 而不是nil
t.Log(m1[1])
m1[2] = 0
t.Log(m1[2])
if v, ok := m1[3]; ok {
t.Logf("Key 3 value is %d", v)
} else {
t.Log("Key 3 is not exist")
}
// 赋值后
m1[3] = 0
if v, ok := m1[3]; ok {
t.Logf("Key 3 value is %d", v)
} else {
t.Log("Key 3 is not exist")
}
}
// 遍历Map
func TestTravelMap(t *testing.T) {
m1 := map[string]int{"a": 1, "b": 2, "c": 3}
for k, v := range m1 {
t.Logf("Key: %s, Value: %d", k, v)
}
}

View File

@ -0,0 +1,48 @@
package map_ext
import (
"fmt"
"testing"
)
// 值为方法的map
func TestMapWithFunValue(t *testing.T) {
m := map[int]func(op int) int{}
m[1] = func(op int) int { return op }
m[2] = func(op int) int { return op * op }
m[3] = func(op int) int { return op + op + op }
t.Log(m[1](2), m[2](2), m[3](2))
}
// Map实现Set操作
func TestMapForSet(t *testing.T) {
mySet := map[int]bool{}
// 添加元素
mySet[1] = true
mySet[2] = false
t.Logf("set len=%d", len(mySet))
// 判断元素是否存在
n := 3
if mySet[n] {
t.Logf("%d is exist", n)
} else {
t.Logf("%d is not exist", n)
}
//删除元素
delete(mySet, 1)
ValueIsExit(mySet, 1)
}
func ValueIsExit(s map[int]bool, v int) bool {
if s[v] {
fmt.Printf("%d is exist", v)
return true
} else {
fmt.Printf("%d is not exist", v)
return false
}
}

View File

@ -0,0 +1,31 @@
package string_test
import (
"strconv"
"strings"
"testing"
)
func TestStringFn(t *testing.T) {
s := "A,B,C"
// 切割
parts := strings.Split(s, ",")
for _, v := range parts {
t.Log(v)
}
// 连接
t.Log(strings.Join(parts, "-"))
}
func TestStrConv(t *testing.T) {
// int to string
s := strconv.Itoa(10)
t.Log(s)
// string to int
if i, err := strconv.Atoi("10"); err == nil {
t.Log(10 + i)
}
}

View File

@ -0,0 +1,29 @@
package string_test
import "testing"
func TestString(t *testing.T) {
var s string
t.Log(s) //初始化值为空字符串 ""
s = "hello"
t.Log(len(s))
s = "\xE4\xB8\xA5" // 可以存储任何二进制数据
// s = "\xE4\xBA\xB5\xFF"
t.Log(s)
s = "中"
t.Log(len(s)) // 是byte数
c := []rune(s)
t.Log(len(c))
t.Logf("中 unicode %x", c[0])
t.Logf("中 UTF8 %x", s)
}
func TestStringToRune(t *testing.T) {
s := "中华人民共和国"
for _, c := range s {
t.Logf("%[1]c %[1]d, %[1]x", c)
}
}