GO语言基础必知必会

博主学习时总结的一些基础知识点。拿捏!

1. 应用程序入口

  • 必须是 main 包: package main
  • 必须是 main 方法: func main()
  • 文件名不一定是 main.go

2. 命令行参数接收

  • 使用 os 包下面的 os.Args 参数
  • 且下标从 1 开始

3.编写测试程序

  • 文件包名为 xxx_test
  • 文件名为 xxx_test.go
  • 方法名为首字符大写 Test 开头,且形参为 t *testing.T

4.变量命名和赋值

  一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。大写字母和小写字母是不同的

var a int = 1
var b int = 2

// 也可以依赖自动类型判断

var a = 1
var b = 2

// 也可以批量赋值
var (
    a int  = 1
    b int  = 2
    c = 1
    d = 2
)

// 还可以更简单的下
 a:=1
 b:=2
 
 // 还可以这样批量赋值【元组赋值】
 
 a:=1
 b:=2
 // 交换两个的值
 a,b = b,a
 

5.常量定义

const (
    one = 1
    two = 2
    three = 3
)
// 等效于
const(
    one = ital + 1
    two
    three

)
const(
		Readable = 1 << iota //0001
		Writeable //0010
		Executable // 0100
)

6.数据类型

Go语言将数据类型分为四类:

6.1 基础类型
  • 数字
  • 字符串
  • 布尔型
6.2 复合类型
  • 数组
  • 结构体
6.3 引用类型
  • 指针
  • 切片
  • 字典
  • 函数
  • 通道
6.4 接口类型
bool

string

int  int8 int16  int32 int64

unit unit8 unit16 unit32 unit64 unitptr

byte //等同于unit8

rune // 等同 int32 unitcode编码指针

float32 float64

complex64 complex128
  • go 语言不支持隐式的类型转换
  • 别名和原有类型也不能进行隐式的转换
  • 字符串的默认值是空字符串不是nil
  • 指针不支持运算(比如说+1)

string 是只读的 byte slice ,len 函数可以计算它所包含的 byte 数, string 的 byte 数组可以存放任何数据

7.运算符

  • 没有前置++ 和前置–
  • 数组的比较(==)必须是相同类型且相同元素个数,否则会报错

8.条件和循环

// 普通循环
 n:= 0
 for n < 5 {
     n++
     fmt.PrintLn(n)
 }
 //普通循环三次
 for n:=0; n < 3; n++ {
     
 }
 
 // 无限循环
 n := 0
 for {
     ...
 }
 
 
 // 条件语句
 
 if n==5 {
     
 }
 
 //多判断,函数支持多个返回值
 if  result,err := someThing(); err == nil {
     
 }
 
 //switch 使用,支持case 后面接多个值,且不用带 break
    i:=32
	switch i {
	case 1:
		t.Log(1)
	case 2,32:
		t.Log(32)
	default:
		t.Log(3)
	}
 
 

9. 函数


// 支持多返回值
func test(a string, b *T) (result string, err error) {
    return "ok", nil
}

a, err := test("a", &b);
if (err != nil) {
    return err;
}

// 匿名函数
func squares() func() int {
    var x int
    return func() int {
        x++
        return x * x
    }
}

//可变参数
func sum(vals ...int) int {
    total := 0
    for _, val := range vals {
        total += val
    }
    return total
}


// 延迟函数,执行顺序和声明相反
defer resp.Body.Close()


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
}

ts := timeSpent(slowFun)
ts(10)

10 接口

// 定义接口
type Programmer interface {
    Say() string
}
// 定义类
type GoProgrammer struct {

}
// 定义方法,方法定义和接口相同就是实现了这个接口
func (g *GoProgrammer) Say() string {
    return "hello"
}

func main() {
    // 定义接口变量
    var p Programmer
    // 实现接口
	w = new(GoProgrammer)
	// 调用接口方法
	w.Say()
}




11.重写

type Pet struct {
}

type Dog struct {
	Pet
}

func (p *Pet) Say() {
	fmt.Println("pet is saying")
}

func (p *Pet) Eat() {
	fmt.Println("pet is eating")
}

func (d *Dog) Say() {
	fmt.Println("dog is saying")
}

func TestExtension(t *testing.T) {
	dog := new(Dog)
	dog.Eat() //pet is eating
	dog.Say() // dog is saying
}

12.错误机制

 errors.New("test")
 
func TestRecover(t *testing.T) {
	defer func() {
		if err := recover() ; err != nil {
			fmt.Println(err)
		}
		fmt.Print("over")
	}()
	panic(errors.New("problem"))
}

13.协程

// 如果main协程退出,那么所有协程都会退出
func TestMyGo(t *testing.T) {
	for i:=0 ; i < 10 ; i++ {
		go func(i int) {
			fmt.Println(i)
		}(i)
		time.Sleep(100 * time.Microsecond)
	}
}

// 使用锁机制避免资源竞争
	var mut sync.Mutex
	counts := 0;
	for i:=0 ; i < 5000 ; i++ {
		go func() {
			defer func() {
				mut.Unlock() //解锁
			}()
			mut.Lock() // 锁住
			counts++
		}()
	}
	time.Sleep(time.Second)
	println(counts)
}

14.通道

  • 通道是协程通信使用的媒介
  • 不带缓冲区的通道会有阻塞作用
	// 创建一个 channel (带缓冲区和不带缓冲区的)
	ch1 := make(chan string)
	ch2 := make(chan string, 2)
	// 往通道里面写东西
	go func() {
		ch1 <- "go"
		ch2 <- "php"
	}()
	// 读通道
	println(<-ch1)
	for ret := range ch2 
		// 关闭channel
	close(ch1)
	close(ch2)


15.多路选择

// 多渠道的选择
select {
    case ret := <-retCh1:
     t.Log(ret)
     case ret := <-retCh2:
     t.Log(ret)
     default:
     t.Error("No one returned")
}

// 超时控制
select {
    case ret := <-retCh:
    t.Log(ret)
    case <-time.After(time.Second):
    t.Log("time out")
}

16.使用通道进行异步消费

func TestChannelClose(t *testing.T) {
	ch := make(chan int)
	var wg sync.WaitGroup
	wg.Add(1)
	dataProducer(ch, &wg)
	wg.Add(1)
	dataReceiver(ch, &wg)
	wg.Wait()
}

// 生产者
func dataProducer(ch chan int , wg *sync.WaitGroup) {
	go func() {
		for i:=0; i < 20; i++ {
			ch <-i
		}
		close(ch) // 如果不关闭通道,获取的时候一直返回了的是 零值, true 。只有关闭的时候才会返回 nil, false
		wg.Done()
	}()
}

// 消费者
func dataReceiver(ch chan int , wg *sync.WaitGroup) {
	go func() {
		for{
			if data, ok := <-ch; ok {
				fmt.Println(data)
			}else {
				break
			}
		}
		wg.Done()
	}()
}

17.关闭通道

  • 关闭一个未初始化(nil) 的 channel 会产生 panic
  • 重复关闭同一个 channel 会产生 panic
    向一个已关闭的 channel 中发送消息会产生 panic
  • 从已关闭的 channel 读取消息不会产生 panic,且能读出 channel 中还未被读取的消息,若消息均已读出,则会读到类型的零值。
  • 从一个已关闭的 channel 中读取消息永远不会阻塞,并且会返回一个为 false 的 ok-idiom,可以用它来判断 channel 是否关闭
  • 关闭 channel 会产生一个广播机制,所有向 channel 读取消息的 goroutine 都会收到消息
func TestCloseChannel(t *testing.T)  {
	ch := make(chan int)
	for i:=0 ; i < 5 ; i++ { // 创建五个协程
		go func(i int, ch chan int) {
			for {
				if isCancel(ch) { // 能消费到就会退出
					break
				}
				time.Sleep(time.Microsecond * 5)
			}
			fmt.Println(i, "cancel")
		}(i, ch)
	}
	cancle1(ch)
	time.Sleep(time.Second)
}

func cancle1(ch chan int) {
	// 关闭有通道广播的作用,所有消费者都能收到通知,即 <-ch, 拿到对应的类型的零值
	close(ch)
}

func cancle2(ch chan int) {
	// 只塞了一个值,那么只会有一个消费者拿到数据
	ch <- 1
}

func isCancel(ch chan int) bool{
	select {
	 case ret := <-ch:
	 	fmt.Println(ret)
		 return true
	default:
		return false
	}
}

18.Context

  • 根 Context : 通过 context.Background() 创建
  • 子 Context:context.WithCancel(parentContext) 创建, ctx, cancel := context.WithCancel(context.Background())
  • 当前 Context 被取消时,基于他的子 context 都会被取消
  • 接收取消通知 <-ctx.Done()
func TestMyContext(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.Microsecond * 5)
			}
			fmt.Println(i, "Cancelled")
		}(i, ctx)
	}
	cancel() // 关闭上下文
	time.Sleep(time.Second)
}

func isCancelled(ctx context.Context) bool {
	select {
	case <-ctx.Done(): // 上下文结束
		return true
	default:
		return false
	}
}

19.sync once 使用

func TestSyncOnce(t *testing.T) {
	for i:=0; i<3; i++ {
		go doOnce() //只会有一个协程执行成功
	}
}

func doOnce() {
	once.Do(func() {
		println("我只能执行一次")
	})
}

20.sync pool 使用

  • 尝试从私有对象获取(只有一个)
  • 私有对象不存在,尝试从当前的 processor的共享池获取(会有锁的开销)
  • 如果当前processor共享池也是空的,那么就尝试去其他 processor的共享池获取
  • 如果所有子池都是空的,最后就用用户指定的 new 函数产生一个新的对象返回
  • GC会清除 sync.pool 缓存的对象
  • 对象的缓存有效期为下一次GC之前
  • 适合通过复用,降低复杂对象的创建和GC代价
  • 协程安全,会有锁的开销
  • 生命周期受 GC 影响,不适合于做连接池等,需自己管理生命周期的资源的池化
func TestSyncPoolTest(t *testing.T) {
	pool := &sync.Pool{
		New: func() interface{}{
		return 0
	}}
	v := pool.Get().(int)
	fmt.Println(v)// 0 默认调用自定义的 new 方法
	pool.Put(3) // 优先放在私有变量中
	w := pool.Get().(int) // 拿池里面的东西
	fmt.Println(w) //3
}

21.反射

  • reflect.TypeOf 返回类型(reflect.Type)
  • reflect.ValueOF 返回值(reflect.Value)
  • 可以从 reflect.Value 获得类型
  • 通过 kind 来判断类型
func TestReflect(t *testing.T) {
	var s int32
	fmt.Println(getType(s))// int

}

func getType(v interface{}) string {
	t := reflect.TypeOf(v) // 返回值是 reflect.Type
	switch t.Kind() { // 获得类型
	case reflect.Int32, reflect.Int64:
		return "int"
	case reflect.Float32, reflect.Float64:
		return "float"
	default:
		return "Unknown"
	}
}

22.内置 json 解析

type Man struct {
	Name string `json:"name"`
	Age  int64 `json:"age"`
	Sons [2]Son
	
}

type Son struct {
	SonName string `json:"son_name"`
	SonAge int64 `json:"son_age"`
}

func main() {
    man := Man{}
	man.Age = 32
	man.Name = "china"

	son := Son{}
	son.SonName = "son"
	son.SonAge = 2
	man.Sons[0] = son

	rs, _ := json.Marshal(man)
	fmt.Println(string(rs)) // {"name":"china","age":32,"address":"xxx","Sons":[{"son_name":"son","son_age":2},{"son_name":"","son_age":0}]}
}

23.构建HTTP服务

func TestMyHttp(t *testing.T) {
	http.HandleFunc("/", sayHelloName) // 路由
	err := http.ListenAndServe(":9000", nil) // 监听服务
	if err != nil {
		log.Fatal("err", err)
	}
}

func sayHelloName(w http.ResponseWriter, r *http.Request) {
	_ = r.ParseForm() //先解析才可以获取格式化好的参数, 否则下面打印出来是空map
	fmt.Println(r.Form)
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MClink

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值