马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
媒介
- 我是一名多年Java开辟职员,由于工作需要现在要学习go语言,Go语言之路是一个系列,记录着我从0开始打仗Go,到反面能正常完成工作上的业务开辟的过程,如果你也是个小白或者转Go语言的,盼望我这篇文章对你有所资助。
- 有关go其他底子的内容的文章大家可以检察我的主页,接下来主要就是把这个系列更完,更完之后我会在每篇文章中挂上毗连,方便大家跳转和复习。
go中的指针,通常在结构体中用的特别多,而方法又是结构体一部分,以是我把这三个知识点放在一起来说,如许大家可以连贯起来方便明确和吸取。
指针
指针你只需要记着两个操纵符,一个是取地点符&,另一个是解引用符 *。对一个变量举行取地点,会返回对应范例的指针,下面我简朴举个例子:
我们先看取地点符号:&
- package main
- import "fmt"
- func main() {
- a := 1
- fmt.Printf("%T\n", a)
- b := &a
- fmt.Println(b)
- fmt.Printf("%T\n", b)
- }
- console打印:
- int
- *int
- 0xc00008c0a8
复制代码 我们对变量a用&符号取地点得到变量b,打印出来b的值就是a的地点,打印出b的范例,就是一个指针,这时间再回顾一下上面这句话:对一个变量举行取地点,会返回对应范例的指针。指针b存储的是变量a的地点
我们再看看解引用符:*
解引用符第一个用处,就跟它的命名一样,排除引用,就是排除指针的引用而得到详细的值,下面我们看个例子:
- import "fmt"
- func main() {
- a := 123
- b := &a
- result := *b
- fmt.Println(result)
- fmt.Printf("%T", result)
- }
- console打印:
- 123
- int
复制代码 通过这个代码可以看到,我们通过&取得了指向a地点的指针b,但是通过解引符号*,用*b就可以排除指针引用直接得到这个地点对应的值,也就是a的值,打印result的数据范例也是int范例。
解引用符第一个用处:声明一个变量的范例为指针范例。
这里我们指定一个变量a它的范例为int范例的指针
打印一下a看看呢
由于我们没有给a赋值或者初始化,以是打印出来的为nil,要么利用取地点符将其他变量的地点赋值给该指针,要不就利用内置函数:new
说到:new。go中的new和Java中的new有区别的是,go中的new是专门为指针服务的,它的用处就是新建或者说初始化一个指针
看看代码
- func main() { var a *int
- fmt.Println(a) a = new(int) fmt.Println(a)}
复制代码 我们用new去初始化了a,看看输出呢:
为啥输出来是一个地点呢,由于该函数会为该指针分配内存,而且指针指向对应范例的零值
用上面的知识点,接引符*来验证下是不是零值呢:
- func main() { var a *int
- fmt.Println(a) a = new(int) fmt.Println(a) fmt.Println(*a)}console打印:<nil>
- 0xc00000a1200
复制代码 这么一看还真是对的,我们点进new函数,看看源码怎么写的:
通过代码分析,我们界说的a为int,这里new中传入的是int,那么返回的就是int,恰恰和a范例划一,是不是就是初始化了啊,是不是很简朴啊。
而上面的示例代码,我们一样平常利用短赋值,简朴一点:
- func main() {
- a:=new(int)
- fmt.Println(a)
- }
复制代码 ps:在go中指针是不能运算的,而且这里我们还要区分一下new和make,前者是为指针服务器的,后者是为详细数据范例的值服务的,不要搞混了。
结构体
go中的结构体,你可以明确为Java中的实体类,但是他们又有细微的差异,但是不是很多,下面我就逐一道来。
既然是结构体,那么界说它的关键词就是:struct。我们先通过一个例子简朴看下。
界说一个UserInfo的结构体,里面分别有name、age、phone三个字段:
声明
- type UserInfo struct {
- name string
- age int
- phone int
- }
复制代码 这是一个简朴的声明,跟函数一样,如果碰到类似的数据范例,也可以写一起,以是上面的age和phone可以如许写:
- type UserInfo struct {
- name string
- age, phone int
- }
复制代码 初始化
注意,上面只是声明,在Java中,是以new关键词创建一个类,好比这里:new UserInfo(),但是在go中没有那么复杂,直接调用传参,看下面例子:
- type UserInfo struct {
- name string
- age, phone int
- }
- func main() { //这里需要注意点,为了方便阅读,或者机动传参,这里只管用这种格式字段名称:字段值, //也可以省略字段名称,但是就要传全部参数而且可读性很差,不保举。 var user = UserInfo{ name: "John", age: 42, phone: 1000, } fmt.Println(user)}console打印:{John 42 1000}
复制代码 注意:这里的结构体命名和里面字段的命名,都依照首字母巨细写的规则,大概有同砚忘了,这里提一下,go中首字母大写的方法就是public,小写的就是private,牢记。
利用
我们访问和结构体和修改结构体中的值也很简朴,直接用.就行:
获取值:
- func main() {
- var user = UserInfo{
- name: "John",
- age: 42,
- phone: 1000,
- }
- fmt.Println(user.name)
- fmt.Println(user.age)
- }
- 打印:
- John
- 42
- Process finished with the exit code 0
复制代码 赋值或修改值:
- func main() {
- var user = UserInfo{
- name: "John",
- age: 42,
- phone: 1000,
- }
- user.name = "一颗知足的心"
- user.age = 18
- fmt.Println(user.name)
- fmt.Println(user.age)
- }
- 打印:
- 一颗知足的心
- 18
- Process finished with the exit code 0
复制代码 如果实例化过程比力复杂,可以编写一个函数来实例化结构体,就像下面如许,你也可以把它明确为一个构造函数,但是go中函数不能重载,以是你想像Java那样通过参数差别用多个一样的函数名是不可的。
- func main() {
- user := NewUser("一颗知足的心", 18, 9527)
- fmt.Println(user)
- }
- func NewUser(name string, age int, phone int) *UserInfo {
- return &UserInfo{name: name, age: age, phone: phone}
- }
复制代码 组合引用
和Java一样,直接在内部字段声明就行,请看下面例子:
- type Person struct {
- name string
- age int
- }
- type Student struct {
- p Person
- school string
- }
复制代码 看看利用:
- student := Student{
- p: Person{name: "jack", age: 18},
- school: "lili school",
- }
- fmt.Println(student.p.name)
复制代码 结构体和指针
结构体的指针和值范例的指针利用上有个小的区别,就是结构体指针在利用的时间不消解引,请看下面例子:
- type UserInfo struct {
- name string
- age, phone int
- }
- func main() { user := &UserInfo{ name: "一颗满意的心", age: 18, phone: 9527, } fmt.Println(user.name)}
复制代码 可以看到我们直接用user.name就可以调用,和普通的结构体调用一样,由于这是go的语法糖,编译器会主动编译成(*user).name
结构体的标签
这里简朴提一点,相识一下就行,标签就是在结构体界说字段的时间,在反面打上标签
- type UserInfo struct {
- Name string `json:"name"`
- Age int `yaml:"age"`
- }
复制代码 结构体标签最广泛的应用就是在各种序列化格式中的别名界说,标签的利用需要联合反射才气完整发挥出其功能。
方法
方法与函数的区别在于,方法拥有接收者,而函数没有,且只有自界说范例可以大概拥有方法。先来看一个例子。
例子
- type IntSlice []int
- func (i IntSlice) Get(index int) int {
- return i[index]
- }
- func (i IntSlice) Set(index, val int) {
- i[index] = val
- }
- func (i IntSlice) Len() int {
- return len(i)
- }
复制代码 先声明白一个范例IntSlice,其底层范例为[]int,再声明白三个方法Get,Set和Len,方法的长相与函数并无太大的区别,只是多了一小段(i IntSlice) 。i就是接收者,IntSlice就是接收者的范例,接收者就类似于其他语言中的this或self,只不外在 Go 中需要显示的指明。
- func main() {
- var intSlice IntSlice
- intSlice = []int{1, 2, 3, 4, 5}
- fmt.Println(intSlice.Get(0))
- intSlice.Set(0, 2)
- fmt.Println(intSlice)
- fmt.Println(intSlice.Len())
- }
复制代码 联合结构体
根据上面的例子,我们把方法和结构体联合一下。这里增补一点,接收者也分两种范例,值接收者和指针接收者
我们先看值继承者:
- type UserInfo struct {
- name string
- age, phone int
- }
- func main() { user := &UserInfo{ name: "一颗满意的心", age: 18, phone: 9527, } user.updateAge(20) fmt.Println(user.age)}func (receiver UserInfo) updateAge(age int) { receiver.age = age}console打印:18Process finished with the exit code 0
复制代码 我们可以看到,固然我们在代码中,将age改为了20,但是末了user结构体中照旧18,也就是说值接收者的方法,并不能改变接收者本身的属性
那要改变接收者本身的属性,就到了指针接收者,我们照旧直接看代码:
- func main() {
- user := &UserInfo{
- name: "一颗知足的心",
- age: 18,
- phone: 9527,
- }
- user.updateAge(20)
- fmt.Println(user.age)
- }
- func (receiver *UserInfo) updateAge(age int) {
- receiver.age = age
- }
- console打印:
- 20
- Process finished with the exit code 0
复制代码 看到厘革没,我们只是把receiver UserInfo改为receiver *UserInfo,变成指针继承者,就可以改变接收者本身的属性。
这是为什么呢:由于值接收者可以简朴的当作一个形参,而修改一个形参的值,并不会对方法外的值造成任何影响而用指针接收者,Go 会将其解释为(&receiver).age = age。以是方法的接收者为指针时,不管调用者是不是指针,都可以修改内部的值
总结
函数的参数转达过程中,是值拷贝的,如果转达的是一个整型,那就拷贝这个整型,如果是一个切片,那就拷贝这个切片,但如果是一个指针,就只需要拷贝这个指针,显然转达一个指针比起转达一个切片所消耗的资源更小,接收者也不破例,值接收者和指针接收者也是同样的原理。在大多数情况下,都保举利用指针接收者,不外两者并不应该混淆利用,要么都用,要么就都不消
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
|