当前位置: 首页 > news >正文

南昌网站开发培训班快速网站排名

南昌网站开发培训班,快速网站排名,如何做网络营销推广的事项,网站建设吉金手指排名12go语言的初学者,特别是java开发者新学习go语言,对于一些和java类似但是又有差异的概念很容易混淆,比如说go中的零值,nil 和 空结构体。本文就来详细探讨一下go中这些特殊概念的含义和实际场景中的应用: 零值 零值&…

go语言的初学者,特别是java开发者新学习go语言,对于一些和java类似但是又有差异的概念很容易混淆,比如说go中的零值,nil 和 空结构体。本文就来详细探讨一下go中这些特殊概念的含义和实际场景中的应用:

零值

零值(The Zero Value)可以看作为当你声明了一个变量,但没有显式的初始化的时候,系统为变量赋予的一个默认初始值。官方对零值的定义如下:

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, “” for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

据此我们可总结出:

  • 值类型 布尔类型为 false, 数值类型为 0,字符串为”“,数组和结构体(struct)会递归初始化其元素或字段,即其初始值取决于元素或字段。这里所谓的值类型其实就相当于java中的 primary 类型,只是需要注意的是string在java中是对象类型,而go中string则是值类型。

  • 引用类型 均为 nil,包括指针 pointer,函数 function,接口 interface,切片 slice,管道 channel,映射 map。

tip: 其实go里面没有真正的引用类型,可以粗略的理解为值类型的变量直接存储值,引用类型的变量存储的是一个地址,这个地址用于存储最终的值

值类型

因为有零值的存在,使得我们在使用变量时,大部分情况下可以不必进行初始化而直接使用,这样能够保持代码的简洁性,也能够尽量避免出现Java开发中常见的**NullPointerException,**以下是一些例子:

package mainimport "sync"type Value struct {mu sync.Mutex   //无需初始化,声明就能用val int
}func (v *Value)Incr(){defer v.mu.Unlock()v.mu.Lock()v.val++
}func main() {var i Valuei.Incr()
}

sync.Mutex本质上是一个结构体:

// A Mutex is a mutual exclusion lock.
// The zero value for a Mutex is an unlocked mutex.
//
// A Mutex must not be copied after first use.
type Mutex struct {state int32sema  uint32
}

那么如果是引用类型,零值为nil,是不是就不能直接用了呢?这个实际上也要分情况,按照类型我们一个个来看:

切片(Slices)

切片的零值是一个nil slice,除了不能按照序号索引查询以外,其它的操作都能做:

func testNilSlice() {var nilSlice []stringfmt.Println(nilSlice == nil) // truefmt.Println(nilSlice[0]) //index out of rangeemptySlice = append(nilSlice, "dd") // append操作会自动扩容fmt.Println(nilSlice[0]) //输出dd
}

nil slice与not nil slice的区别:

type Person {Friends []string
}var f1 []string //nil切片
json1, _ := json.Marshal(Person{Friends: f1})
fmt.Printf("%s\n", json1) //output:{"Friends": null}f2 := make([]string, 0) //non-nil空切片 ,等价于 f2 := []string{}
json2, _ := json.Marshal(Person{Friends: f2})
fmt.Printf("%s\n", json2) //output: {"Friends": []}

推荐在日常使用时,没有特殊需求都使用var nilSlice []string 这样的形式声明空切片:https://github.com/golang/go/wiki/CodeReviewComments#declaring-empty-slices

Map

对于nil的map,我们可以简单把它看成是一个只读的map,不能进行写操作,否则就会panic:

func testNilMap() {var m map[string]stringfmt.Println(m["key"]) //输出""m["key"]="value" //panic: assignment to entry in nil map
}

那么nil map有啥用呢,可以看看以下的例子:

func NewGet(url string, headers map[string]string) (*http.Request, error) {req, err := http.NewRequest(http.MethodGet, url, nil)if err != nil {return nil, err}for k, v := range headers {req.Header.Set(k, v)}return req, nil
}//调用该方法时如果没有header,可以传入一个空的map,例如:
NewGet("http://google.com", map[string]string{})
//也可以直接传入nil
NewGet("http://google.com", nil)

Channel

nil channel会阻塞对该channel的所有读、写。所以,可以将某个channel设置为nil,进行强制阻塞,对于select分支来说,就是强制禁用此分支

func addIntegers(c chan int) {sum := 0t := time.NewTimer(time.Second)for {select {case input := <-c:sum = sum + inputcase <-t.C:c = nilfmt.Println(sum)  // 输出10}}
}func main() {c := make(chan int, 1)go addIntegers(c)for i := 0; i <= 10; i++ {c <- itime.Sleep(time.Duration(200) * time.Millisecond)}
}

指针(Pointers)

指针如果为nil,则对指针进行解引用的话,会引发我们在java中非常熟悉的空指针错误

type Person struct {Name stringSex  stringAge  int
}var p *Person
fmt.Println(p.Name)  // panic: runtime error: invalid memory address or nil pointer dereference

神奇的nil

nil 是 Golang 中预先声明的标识符,其主要用来表示引用类型的零值(指针,接口,函数,映射,切片和通道),表示它们未初始化的值。

// [src/builtin/builtin.go](https://golang.org/src/builtin/builtin.go#L98)
//
// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

nil在go语言里面不是一个关键字或者保留字,所以你可以用nil作为变量名(作死):

var nil = errors.New("my god")

nil没有默认的类型,所以不能给一个未声明类型的变量赋值,也不能和自己比较:

a := nil
// cannot declare variable as untyped nil: a
fmt.Println(nil == nil)
// invalid operation: nil == nil (operator == not defined on nil)
fmt.Printf("%T", nil)
// use of untyped nil

比较nil时一定要注意nil实际上是有类型的,不同类型的nil是不相等的,比如下面的例子:

var p *int
var i interface{}fmt.Println(p)      // <nil>
fmt.Println(i)      // <nil>fmt.Println(p == i) // false

再看一个在实际编码里面很容易犯的错误:

type BusinessError struct {errorerrorCode int64
}func doBusiness() *BusinessError {return nil
}func wrapDoBusiness() error {err := doBusiness()return err
}func testError() {err := wrapDoBusiness() //这里面拿到的本质上是一个<T:*BusinessError,V:nil>的nilfmt.Println(err == nil)
}

建议:如果任何地方有判断interface是否为 nil 值的逻辑,一定不要写任何有关于将interface赋值为具体实现类型(可能为nil)的代码,如果是 nil 值就直接赋给interface,而不要过具体类型的转换

type BusinessError struct {errorerrorCode int64
}func doBusiness() *BusinessError {return nil
}func wrapDoBusiness() error {err := doBusiness() if err == nil {return nil  //如果返回值为nil,直接返回nil,不要做类型转换} else {return err}
}func testError() {err := wrapDoBusiness()fmt.Println(err == nil)
}

空结构体

golang 正常的 struct 就是普通的一个内存块,必定是占用一小块内存的,并且结构体的大小是要经过边界,长度的对齐的,但是“空结构体”是不占内存的,size 为 0;

var q struct{}
fmt.Println(unsafe.Sizeof(q)) // 0

空结构体 struct{ } 为什么会存在的核心理由就是为了节省内存。当你需要一个结构体,但是却丝毫不关系里面的内容,那么就可以考虑空结构体。以下是几个经典的用法:

map & struct{}

map 和 struct {} 一般的结合姿势是这样的:

// 创建 map
m := make(map[int]struct{})
// 赋值
m[1] = struct{}{}
// 判断 key 键存不存在
_, ok := m[1]

一般 map 和 struct {} 的结合使用场景是:只关心 key,不关注值。比如查询 key 是否存在就可以用这个数据结构,通过 ok 的值来判断这个键是否存在,map 的查询复杂度是 O(1) 的,查询很快。这种方式在部分场景下可以起到类似Java中Set的作用

chan & struct{}

channel 和 struct{} 结合是一个最经典的场景,struct{} 通常作为一个信号来传输,并不关注其中内容。chan 本质的数据结构是一个管理结构加上一个 ringbuffer ,如果 struct{} 作为元素的话,ringbuffer 就是 0 分配的。

chan 和 struct{} 结合基本只有一种用法,就是信号传递,空结构体本身携带不了值,所以也只有这一种用法啦,一般来说,配合 no buffer 的 channel 使用。

waitc := make(chan struct{})// ...
goroutine 1:// 发送信号: 投递元素waitc <- struct{}// 发送信号: 关闭close(waitc)goroutine 2:select {// 收到信号,做出对应的动作case <-waitc:}
http://www.yayakq.cn/news/577189/

相关文章:

  • 中国十大网站建设推百拉
  • 医药企业网站设计制作福建龙岩发现1例阳性
  • 西安网站建设xazxcy黄骅市有火车站吗
  • 想给公司做个网站怎么做集团高端网站建设公司
  • 青岛响应式网站建设企业邮箱大全号码大全
  • 获取网站访客qq做国外零售的话是在什么网站开店
  • 梧州做网站企业网站有哪些内容
  • 如何做网站竞品分析网站建设项目评审意见
  • 广州木马网站建设公司怎么样公司网站建设文案
  • 茂名h5网站建设做ui的图从哪个网站找
  • 石排做网站建设招标网
  • 网站备案 备注站内推广的方法
  • 沧州市建设服务中心网站优化手机性能的软件
  • 中山营销网站建设联系方式wordpress汉化插件库
  • 怎么才可以做网站wordpress中文语言包下载地址
  • 企业网站建设中在方案设计上wordpress 查询当月
  • 10个网站wordpress 模块化主题
  • 中国建设银行的招投标网站中国工商网查询入口
  • 四川星星建设集团有限公司网站京东网站的公司全名
  • 珠海十大网站建设公司哪家好自己怎么创建微信小程序
  • 网站开发答辩难点想做个网站怎么做
  • 如何用源代码做网站惠州手机模板建站
  • 网站做产品的审核工作内容营销网站制作方案
  • 建设企业网站官网企业深圳品牌策划
  • 比价网站 源码网站链接可以自己做吗
  • 投资集团网站建设广告推广合同范本
  • 南县网站定制沈阳京科医院
  • 人才网站建设的目标wordpress跳转到老域名
  • 东莞网站建设外包免费关键词搜索工具
  • 网络营销的培训课程宁波谷歌seo