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通过解析request
c.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.25
,25%
扩容
-
-
赋值:将一个切片赋值给另一个切片可指定索引
- 第一个索引:指定切片的头部
- 第二个索引:指定切片长度的尾部
- 第三个索引:限制切片的容量
参考下面代码:
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字节,由于与切片关联的数据包含在底层数组里面,不属于切片本身,所以将切片复制给人以数组时对底层数组大小都不会有影响。
有疑问加站长微信联系(非本文作者)