Go总结(2)

struct

go中struct结构默认字段都会有零值,故不能用nil来判断struct是否为空,可通过额外的字段来判断struct是否填充或为空


type Demo struct{
    ready bool

    name string
    //其他字段
}

在初始化的时候必须将ready设置为true

var d Demo
if !d.ready{
    //do stuff
}



Web工作方式

  • Go通过ListenAndServer来建立web服务器,底层是初始化一个server对象,然后调用net.Listen("tcp",addr)来监听端口。
  • 调用srv.server(net.Listener)函数来处理接收客户端请求。函数里面为一个for{},首先通过accept接受请求,接着创建一个Conn,最后单独开一个goroutine取执行:go c.server()
  • 用户的每一次请求都是一个新的goroutine去执行。
  • conn通过解析requestc.readRequest()获取相应的handler := c.server.Handler,它本质是一个路由器,通过它来匹配url跳到对应的handle函数。
  • 可通过`http.HandleFunc(“/”,sayhelloName)来注册请求的路由规则。

OS获取环境变量

os.getenv()获取环境变量获取不到最新设置的环境变量,最新设置的需要重新启动电脑获取

基本类型

这两天在搞反射,看到Go的基础数据类型那么多,int,int32,int64都有,而且运算过程中还需要转换,所以抽空看了些博客以及官方文档。

  • int跟uint

    • 有符号:int8,int16,int32,int64
    • 无符号:unit8,unit16,unit32,uint64
    • int和unit取决于操作系统,在32位系统就是32字节,在64位系统就是64字节
    • int跟int32不是相同的类型,虽然在特定的场景下它们大小相同,但是在运算过程中需要转换
    • byte是unit8的别名,rune是int32的别名
  • 浮点类型为float32和float64

    • 浮点类型在运算过程中可能存在精度丢失的情况
  • string

    • 字符串是不可变的,一旦创建,就不能改变字符串的内容
    • 可以使用内置函数len来发现s的长度,如果字符串为常量,则length是编译时常量。
    • 字符串的字节可以通过索引来获取,但是取元素的地址是非法的,即&s[i]是无效的。

反射

反射在计算机中是程序检查自己结构的一种能力,尤其是通过类型,它是元数据编程的一种方式,也是混乱的重要来源

每个语言的反射模型都不同(很多语言根本不支持它)

Type And interface

因为反射是建立在类型系统上,让我们先回顾一下Go中的类型。

Go是静态类型语言,每个变量都有一个静态类型,即在编译时就已知被固定上一种类型:int, float32, &MyType, []byte

type MyInt int

var i int
var j MyInt

变量i和j具有不同的静态类型,虽然他们具有相同的底层类型,但如果没有转换,则无法将他们分配给彼此。

interface可以存储任何具体的值,interface包括:

  • 变量包括(type,value)两部分,这也是为什么nil != nil的原因
  • type包括static type和concrete type,static type是编辑时就看到的类型,而concrete type是runtime看到的类型

反射就是建立在类型之上的,Golang的指定类型的变量的类型是静态的(也就是指定int、string这些变量,它的type是static type),在创建变量的时候就已经确定,反射主要于Golang的interface有关(它的类型是concrete type),只有interface类型才有反射之说。

API

以下是一些API

reflect:
    TypeOf(interface{}) Type : 返回接口中保存值的类型,i为nil值返回nil
    ValueOf(interface{}) Value : 返回一个初始化为i接口保管的具体值的Value,但i为nil时返回Value零值
    New(Type) Value:返回一个指向类型为Type的新申请的零值的指针。

Type:
    Kind():返回该接口的具体类型
    Name():返回类型名
    Elem():返回该类型的元素类型,如果Kind不是Array,Chan,Map,Slice,Ptr会panic

Value:
    Append(s Value,x ...Value) Value: s需为切片类型的Value值,x需为s的元素类型的Value值,将x复制给s并且返回s
    Type():返回v持有的类型的Type表示
    Elem() Value:返回v持有的接口或者指针保管值的Value封装,如果v的Kind不是interface或者Ptr将会panic
    Kind():同上一致
    CanSet():判断v持有的值是否能更改,只有当Value持有值为Ptr并且为共有类型时,它才可以被修改。
    Set(x Value):将v的持有值修改为x的持有值
    SetInt(x Int64)
    SetString(s string)
    ....

更多的可参考官方文档:https://go-zh.org/pkg/reflect/#Value.Convert

反射讲得比较好的一篇文章:https://juejin.im/post/5a75a4fb5188257a82110544

Go运行时

尽管Go编译器产生的是本地可执行代码,这些代码仍旧运行在Go的runtime(这部分的代码可以在runtime包中找到)当中,这个runtime类似虚拟机,它负责管理包括内存分配、垃圾回收、栈处理、goroutine、channel、slice、map和reflection等等。

Interface

Go中的interface并不是显示实现的,这就导致,一个方法接收的类型为IPerson,但是虽然我的Student对象已经实现了IPerson,但是还是不要在通过方法去new实现返回IPerson对象

type Student struct{
    name string
    age int
}

func newIPerson(name string,age int) IPerson{
    return Student{
        name : name,
        age : age,
    }
}

切片

  • make([]int,l,c)l为长度,c为容量,不传c则容量等于长度
  • 底层还是数组,通过len()获取长度,cap()获取容量
  • append之后返回的是一个新的切片
  • 扩容:

    • capacity小于1000时,两倍扩容
    • capacity大于1000时,增长因子为1.2525%扩容
  • 赋值:将一个切片赋值给另一个切片可指定索引

    • 第一个索引:指定切片的头部
    • 第二个索引:指定切片长度的尾部
    • 第三个索引:限制切片的容量

参考下面代码:

a := []int{1, 2, 3, 4, 5}
b := a[1:]
c := a[:4]
d := a[1:4]
e := a[2:3:4]
fmt.Println("a", len(a), cap(a))
fmt.Println("b", len(b), cap(b))
fmt.Println("c", len(c), cap(c))
fmt.Println("d", len(d), cap(d))
fmt.Println("e", len(e), cap(e))

//打印结果
a 5 5
b 4 4
c 4 5
d 3 4
e 1 2
  • for-range返回的是每个元素的副本,而不是引用
  • 切片在函数件传递还是以值传递的方式传递,由于切片的尺寸很小,在函数间复制和传递切片的成本也很低。在64位结构的机器上,一个切片需要24个字节,指针字段8字节,长度和容量分别需要8字节,由于与切片关联的数据包含在底层数组里面,不属于切片本身,所以将切片复制给人以数组时对底层数组大小都不会有影响。

《Go总结(2)》

    原文作者:dack
    原文地址: https://segmentfault.com/a/1190000017983621
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞