Go 入门

1. 赋值和初始化

var s=""; //<=> s:="";

2. 程序入口:

//hello.go
package main
import "fmt"
func main(){
    fmt.Println("Hello, world!");
}

go run hello.go //<=> go build hello.go; ./hello

3. 遍历数组或者map

for index,value:=range lst{
    fmt.Println("The "+fmt.Sprint(index)+"th element is "+value);
}

4. 初始化map或数组

m:=make(map[string]int)//空map
m:=make(map[string]int){
    "Hello":1,
    "World":2,
}

5. 数组或者slice。数组和slice很像,但是slice没有固定长度

arr:=[...]int{1,2,3} //数组,长度根据赋值自动推导,此处为3
arr:=[3]int{1,2,3} // 数组,指定长度
slice:=[]int{1,2,3} //slice, 没有固定长度
b:=arr[1:2] //slice. slice 有两个属性,cap和len。访问的时候都不能越界。但是对其重新切片的时候,可以超过len,但是仍然不能超过cap
a:=b[:]赋值之后b和a共享同一个底层数组,但是当b发生动态扩展时,两者不再共享同一个底层数据,从一个引用发起的修改,对另外一个不可见

6.强制类型转换和类型断言

// 类型转换
u64:=239892930
i8:=int8(u64) // 类型转换,u64的值被截断

u64 := new(uint64)
i8 := (*int8)(u64) // 指针也可以转换,但是要求底层类型相同,此处编译错误

type long uint64
l8 := (*long)(u64) //ok

bytes := []byte{0x61, 0x62, 0x63}
str := string(bytes)
bs := []byte(str)   // str和[]byte, float int 也可以

// 类型断言,任何值都可以赋值给interface{}类型,当需要转成原类型的时候,可以做一次类型断言,类型必须完全相同,否则即使底层类型相同也不行
type long uint64

var i interface{}
i = uint64(99)
u := i.(uint64)   // ok

l := i.(long)     // runtime error

7. switch的独特语法x.(type)

func Sprint(x interface{}) string {
	switch x := x.(type) {
	case string:
		return x
	case int:
		return strconv.Itoa(x)
	default:
		return ""
	}
}

8. reflect.TypeOf 返回的是一个动态类型的接口值, 它总是返回具体的类型,即使对于接口,也会返回具体类型

tp := reflect.TypeOf(3)  // a reflect.Type
fmt.Println(tp.String()) // "int"
fmt.Println(tp)           // "int"

fmt.Printf("%T\n", 3)    // "int"

9. reflect.ValueOf 一个 reflect.Value 和 interface{} 都能保存任意的值. 所不同的是, 一个空的接口隐藏了值对应的表示方式和所有的公开的方法, 因此只有我们知道具体的动态类型才能使用类型断言来访问内部的值(就像上面那样), 对于内部值并没有特别可做的事情. 相比之下, 一个 Value 则有很多方法来检查其内容,无论它的具体类型是什么

v := reflect.ValueOf(3) // a reflect.Value
fmt.Println(v) // "3"
fmt.Printf("%v\n", v) // "3"
fmt.Println(v.String()) // NOTE: "<int Value>"

t := v.Type() // a reflect.Type
fmt.Println(t.String()) // "int"

i := v.Interface().(int)

10. reflect.ValueOf 赋值。所有通过reflect.ValueOf(x)返回的reflect.Value都是不可取地址的。但是对于d,它是c的解引用方式生成的,指向另一个变量,因此是可取地址的。我们可以通过调用reflect.ValueOf(&x).Elem(),来获取任意变量x对应的可取地址的Value

x := 2
d := reflect.ValueOf(&x).Elem() // d refers to the variable x
px := d.Addr().Interface().(*int) // px := &x
*px = 3 // x = 3
d.Set(reflect.ValueOf(int64(5))) // panic: int64 is not assignable to int

d.SetInt(3)
fmt.Println(x) // "3"

x := 2
b := reflect.ValueOf(x)
b.Set(reflect.ValueOf(3)) // panic: Set using unaddressable value

tp.Implements(reflect.TypeOf((*Eater)(nil)).Elem())

10. MakeFunc有点麻烦,但是能够处理参数、返回值个数和类型都是不确定情况

add := func(a, b int) int { return a + b }
f := reflect.MakeFunc(reflect.TypeOf(add), func(args []reflect.Value) (results []reflect.Value) {
	a := args[0].Interface().(int)
	b := args[1].Interface().(int)
	return []reflect.Value{reflect.ValueOf(a + b)}
})
vs := f.Call([]reflect.Value{
	reflect.ValueOf(7),
	reflect.ValueOf(9),
})
fmt.Println(len(vs))
fmt.Println(vs[0].Interface().(int))

10. interface{}类型没什么神奇的,就是把原来对象的地址和元数据封装成一个新的对象。如果获取某个地址的值,可以是用unsafe.Pointer进行强制类型转换。计算偏移可以先把unsafe.Pointer 转成uintptr,进行算术计算,算完之后再转成unsafe.Pointer

// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
	typ  *rtype
	word unsafe.Pointer
}
tptr := unsafe.Pointer((*emptyInterface)(unsafe.Pointer(&ifc_u64)).typ)
fmt.Printf("%x\n",*(*uint64)(unsafe.Pointer(uintptr(tptr)+16)))

10. 地址可取性 所有通过reflect.ValueOf(x)返回的reflect.Value都是不可取地址的。我们可以通过调用reflect.ValueOf(&x).Elem(),来获取任意变量x对应的可取地址的Value

7. 包的初始化

如果包中含有多个.go源文件,它们将按照发给编译器的顺序进行初始化,Go语言的构建工具首先会将.go文件根据文件名排序,然后依次调用编译器编译。对于在包级别声明的变量,如果有初始化表达式则用表达式初始化,还有一些没有初始化表达式的,例如某些表格数据初始化并不是一个简单的赋值过程。在这种情况下,我们可以用一个特殊的init初始化函数来简化初始化工作。每个文件都可以包含多个init初始化函数。

如果只想执行包中的初始化函数func init(){},可以将包名引入为_

import (
	_ "playground/zoo"
)

8. 方法

接收器可以是指针或者对象。如果是对象,则会产生一次拷贝,意味着在方法内对对象的所有修改在返回后都失效。如果是指针接收器,则不能用下面的语法。 nil也是合法的接收器

func (a*Animal) Speak(){}
(Animal{}).Speak() // compile error. 无法获取临时变量的地址

9. 扩展现有类型

type long uint64

func (l *long) Minus(v uint64) uint64 {
	fmt.Println(*l)
	return uint64(*l) - v
}

ll := long(5)
fmt.Println(ll.Minus(3))

6. 自动编号,enum

const (
	Sunday = 1<<iota + 1
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
)

6. 一个带有pipeline的goroutine和channel

package main

import ("fmt";"time")

func main(){
    num:=make(chan int)
    squ:=make(chan int)
    go func(){
        for x:=0;;x++{
            num<-x
            time.Sleep(1*time.Second)
            if x>5{
                close(num)
                close(squ)
            }
        }
    }()

    go func(){
        for{
            x:=<-num
            squ<-x*x
        }
    }()

    for{
        value:=<-squ
        fmt.Println(value)
    }
}

6.更优雅的迭代方式

package main

import "fmt"

func counter(out chan<-int){
    for x:=0;x<5;x++{
        out<-x
    }
    close(out)
}

func squarer(in <-chan int, out chan<-int){
    for x:=range in {
        out<-x*x
    }
    close(out)
}

func printer(in <-chan int){
    for x:=range in{
        fmt.Println(x)
    }
}
func main(){
    num:=make(chan int)
    squ:=make(chan int)
    go counter(num)
    go squarer(num,squ)
    printer(squ)
}

6. 当一个channel被关闭后,再向该channel发送数据将导致panic异常。当一个被关闭的channel中已经发送的数据都被成功接收后,后续的接收操作将不再阻塞,它们会立即返回一个零值。没有办法直接测试一个channel是否被关闭,但是接收操作有一个变体形式:它多接收一个结果,多接收的第二个结果是一个布尔值ok,ture表示成功从channels接收到值,false表示channels已经被关闭并且里面没有值可接收。这也是结束多个读取一个channel的多个goroutine的通常做法。

6. Go routine的泄漏:如果我们使用了无缓存的channel,那么两个慢的goroutines将会因为没有人接收而被永远卡住。这种情况,称为goroutines泄漏,这将是一个BUG。和垃圾变量不同,泄漏的goroutines并不会被自动回收,因此确保每个不再需要的goroutine能正常退出是重要的。

func mirroredQuery() string {
    responses := make(chan string, 3)
    go func() { responses <- request("asia.gopl.io") }()
    go func() { responses <- request("europe.gopl.io") }()
    go func() { responses <- request("americas.gopl.io") }()
    return <-responses // return the quickest response
}

6. 多路复用,每次循环,select只会等最先就绪的channel,如果同时有多个channel就绪,则随机选取一个。如果加上default,因为是永远立即就绪,导致select空循环。

func TestSelect2(t *testing.T) {
	for {
		select {
		case <-time.After(time.Second * 3):
			fmt.Println("3s...")
		case <-time.After(time.Second * 1):
			fmt.Println("1s...")
		case <-time.After(time.Second * 2):
			fmt.Println("2s...")
		}
	}
}

6. http

type MyServer struct{}

func (s *MyServer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	if "GET" == req.Method {
		w.Write([]byte("Get\n"))
		w.Write([]byte(req.URL.Path + "\n"))
		w.Write([]byte(req.URL.Query().Get("hello") + "\n"))
	} else {
		w.Write([]byte("Post"))
	}
}

func TestClient(t *testing.T) {
	resp, err := http.Get("http://www.baidu.com")
	if err == nil {
		fmt.Println(resp.Header)
		fmt.Println(resp.Body)
	}
	http.ListenAndServe("localhost:9000", &MyServer{})
}

func TestClient2(t *testing.T) {
	server := &MyServer{}
	mux := http.NewServeMux()
	mux.HandleFunc("/list", server.ServeHTTP)
	http.ListenAndServe("localhost:9001",mux)
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值