Go语言之路————指针、结构体、方法

[复制链接]
发表于 2025-9-22 05:24:59 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
媒介



  • 我是一名多年Java开辟职员,由于工作需要现在要学习go语言,Go语言之路是一个系列,记录着我从0开始打仗Go,到反面能正常完成工作上的业务开辟的过程,如果你也是个小白或者转Go语言的,盼望我这篇文章对你有所资助。
  • 有关go其他底子的内容的文章大家可以检察我的主页,接下来主要就是把这个系列更完,更完之后我会在每篇文章中挂上毗连,方便大家跳转和复习。
go中的指针,通常在结构体中用的特别多,而方法又是结构体一部分,以是我把这三个知识点放在一起来说,如许大家可以连贯起来方便明确和吸取。
指针

指针你只需要记着两个操纵符,一个是取地点符&,另一个是解引用符 *。对一个变量举行取地点,会返回对应范例的指针,下面我简朴举个例子:
我们先看取地点符号:&
  1. package main
  2. import "fmt"
  3. func main() {
  4.         a := 1
  5.         fmt.Printf("%T\n", a)
  6.         b := &a
  7.         fmt.Println(b)
  8.         fmt.Printf("%T\n", b)
  9. }
  10. console打印:
  11. int
  12. *int
  13. 0xc00008c0a8
复制代码
我们对变量a用&符号取地点得到变量b,打印出来b的值就是a的地点,打印出b的范例,就是一个指针,这时间再回顾一下上面这句话:对一个变量举行取地点,会返回对应范例的指针。指针b存储的是变量a的地点
我们再看看解引用符:*
解引用符第一个用处,就跟它的命名一样,排除引用,就是排除指针的引用而得到详细的值,下面我们看个例子:
  1. import "fmt"
  2. func main() {
  3.         a := 123
  4.         b := &a
  5.         result := *b
  6.         fmt.Println(result)
  7.         fmt.Printf("%T", result)
  8. }
  9. console打印:
  10. 123
  11. int
复制代码
通过这个代码可以看到,我们通过&取得了指向a地点的指针b,但是通过解引符号*,用*b就可以排除指针引用直接得到这个地点对应的值,也就是a的值,打印result的数据范例也是int范例。
解引用符第一个用处:声明一个变量的范例为指针范例。
这里我们指定一个变量a它的范例为int范例的指针
  1. var a *int
复制代码
打印一下a看看呢
  1. <nil>
复制代码
由于我们没有给a赋值或者初始化,以是打印出来的为nil,要么利用取地点符将其他变量的地点赋值给该指针,要不就利用内置函数:new
说到:new。go中的new和Java中的new有区别的是,go中的new是专门为指针服务的,它的用处就是新建或者说初始化一个指针
看看代码
  1. func main() {        var a *int
  2.         fmt.Println(a)        a = new(int)        fmt.Println(a)}
复制代码
我们用new去初始化了a,看看输出呢:
  1. <nil>
  2. 0xc00000a120
复制代码
为啥输出来是一个地点呢,由于该函数会为该指针分配内存,而且指针指向对应范例的零值
用上面的知识点,接引符*来验证下是不是零值呢:
  1. func main() {        var a *int
  2.         fmt.Println(a)        a = new(int)        fmt.Println(a)        fmt.Println(*a)}console打印:<nil>
  3. 0xc00000a1200
复制代码
这么一看还真是对的,我们点进new函数,看看源码怎么写的:
  1. func new(Type) *Type
复制代码
通过代码分析,我们界说的a为int,这里new中传入的是int,那么返回的就是int,恰恰和a范例划一,是不是就是初始化了啊,是不是很简朴啊。
而上面的示例代码,我们一样平常利用短赋值,简朴一点:
  1. func main() {
  2.         a:=new(int)
  3.         fmt.Println(a)
  4. }
复制代码
ps:在go中指针是不能运算的,而且这里我们还要区分一下new和make,前者是为指针服务器的,后者是为详细数据范例的值服务的,不要搞混了。
结构体

go中的结构体,你可以明确为Java中的实体类,但是他们又有细微的差异,但是不是很多,下面我就逐一道来。
既然是结构体,那么界说它的关键词就是:struct。我们先通过一个例子简朴看下。
界说一个UserInfo的结构体,里面分别有name、age、phone三个字段:
声明

  1. type UserInfo struct {
  2.         name  string
  3.         age   int
  4.         phone int
  5. }
复制代码
这是一个简朴的声明,跟函数一样,如果碰到类似的数据范例,也可以写一起,以是上面的age和phone可以如许写:
  1. type UserInfo struct {
  2.         name       string
  3.         age, phone int
  4. }
复制代码
初始化

注意,上面只是声明,在Java中,是以new关键词创建一个类,好比这里:new UserInfo(),但是在go中没有那么复杂,直接调用传参,看下面例子:
  1. type UserInfo struct {
  2.         name       string
  3.         age, phone int
  4. }
  5. func main() {        //这里需要注意点,为了方便阅读,或者机动传参,这里只管用这种格式字段名称:字段值,        //也可以省略字段名称,但是就要传全部参数而且可读性很差,不保举。        var user = UserInfo{                name:  "John",                age:   42,                phone: 1000,        }        fmt.Println(user)}console打印:{John 42 1000}
复制代码
注意:这里的结构体命名和里面字段的命名,都依照首字母巨细写的规则,大概有同砚忘了,这里提一下,go中首字母大写的方法就是public,小写的就是private,牢记。
利用

我们访问和结构体和修改结构体中的值也很简朴,直接用.就行:
获取值:
  1. func main() {
  2.         var user = UserInfo{
  3.                 name:  "John",
  4.                 age:   42,
  5.                 phone: 1000,
  6.         }
  7.         fmt.Println(user.name)
  8.         fmt.Println(user.age)
  9. }
  10. 打印:
  11. John
  12. 42
  13. Process finished with the exit code 0
复制代码
赋值或修改值:
  1. func main() {
  2.         var user = UserInfo{
  3.                 name:  "John",
  4.                 age:   42,
  5.                 phone: 1000,
  6.         }
  7.         user.name = "一颗知足的心"
  8.         user.age = 18
  9.         fmt.Println(user.name)
  10.         fmt.Println(user.age)
  11. }
  12. 打印:
  13. 一颗知足的心
  14. 18
  15. Process finished with the exit code 0
复制代码
如果实例化过程比力复杂,可以编写一个函数来实例化结构体,就像下面如许,你也可以把它明确为一个构造函数,但是go中函数不能重载,以是你想像Java那样通过参数差别用多个一样的函数名是不可的。
  1. func main() {
  2.         user := NewUser("一颗知足的心", 18, 9527)
  3.         fmt.Println(user)
  4. }
  5. func NewUser(name string, age int, phone int) *UserInfo {
  6.         return &UserInfo{name: name, age: age, phone: phone}
  7. }
复制代码
组合引用

和Java一样,直接在内部字段声明就行,请看下面例子:
  1. type Person struct {
  2.    name string
  3.    age  int
  4. }
  5. type Student struct {
  6.    p      Person
  7.    school string
  8. }
复制代码
看看利用:
  1. student := Student{
  2.    p:      Person{name: "jack", age: 18},
  3.    school: "lili school",
  4. }
  5. fmt.Println(student.p.name)
复制代码
结构体和指针

结构体的指针和值范例的指针利用上有个小的区别,就是结构体指针在利用的时间不消解引,请看下面例子:
  1. type UserInfo struct {
  2.         name       string
  3.         age, phone int
  4. }
  5. func main() {        user := &UserInfo{                name:  "一颗满意的心",                age:   18,                phone: 9527,        }        fmt.Println(user.name)}
复制代码
可以看到我们直接用user.name就可以调用,和普通的结构体调用一样,由于这是go的语法糖,编译器会主动编译成(*user).name
结构体的标签

这里简朴提一点,相识一下就行,标签就是在结构体界说字段的时间,在反面打上标签
  1. type UserInfo struct {
  2.         Name string `json:"name"`
  3.         Age  int    `yaml:"age"`
  4. }
复制代码
结构体标签最广泛的应用就是在各种序列化格式中的别名界说,标签的利用需要联合反射才气完整发挥出其功能
方法

方法与函数的区别在于,方法拥有接收者,而函数没有,且只有自界说范例可以大概拥有方法。先来看一个例子。
例子

  1. type IntSlice []int
  2. func (i IntSlice) Get(index int) int {
  3.   return i[index]
  4. }
  5. func (i IntSlice) Set(index, val int) {
  6.   i[index] = val
  7. }
  8. func (i IntSlice) Len() int {
  9.   return len(i)
  10. }
复制代码
先声明白一个范例IntSlice,其底层范例为[]int,再声明白三个方法Get,Set和Len,方法的长相与函数并无太大的区别,只是多了一小段(i IntSlice) 。i就是接收者,IntSlice就是接收者的范例,接收者就类似于其他语言中的this或self,只不外在 Go 中需要显示的指明。
  1. func main() {
  2.    var intSlice IntSlice
  3.    intSlice = []int{1, 2, 3, 4, 5}
  4.    fmt.Println(intSlice.Get(0))
  5.    intSlice.Set(0, 2)
  6.    fmt.Println(intSlice)
  7.    fmt.Println(intSlice.Len())
  8. }
复制代码
联合结构体

根据上面的例子,我们把方法和结构体联合一下。这里增补一点,接收者也分两种范例,值接收者和指针接收者
我们先看值继承者:
  1. type UserInfo struct {
  2.         name       string
  3.         age, phone int
  4. }
  5. 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,也就是说值接收者的方法,并不能改变接收者本身的属性
那要改变接收者本身的属性,就到了指针接收者,我们照旧直接看代码:
  1. func main() {
  2.         user := &UserInfo{
  3.                 name:  "一颗知足的心",
  4.                 age:   18,
  5.                 phone: 9527,
  6.         }
  7.         user.updateAge(20)
  8.         fmt.Println(user.age)
  9. }
  10. func (receiver *UserInfo) updateAge(age int) {
  11.         receiver.age = age
  12. }
  13. console打印:
  14. 20
  15. Process finished with the exit code 0
复制代码
看到厘革没,我们只是把receiver UserInfo改为receiver *UserInfo,变成指针继承者,就可以改变接收者本身的属性。
这是为什么呢:由于值接收者可以简朴的当作一个形参,而修改一个形参的值,并不会对方法外的值造成任何影响而用指针接收者,Go 会将其解释为(&receiver).age = age。以是方法的接收者为指针时,不管调用者是不是指针,都可以修改内部的值
总结

函数的参数转达过程中,是值拷贝的,如果转达的是一个整型,那就拷贝这个整型,如果是一个切片,那就拷贝这个切片,但如果是一个指针,就只需要拷贝这个指针,显然转达一个指针比起转达一个切片所消耗的资源更小,接收者也不破例,值接收者和指针接收者也是同样的原理。在大多数情况下,都保举利用指针接收者,不外两者并不应该混淆利用,要么都用,要么就都不消

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
继续阅读请点击广告
回复

使用道具 举报

×
登录参与点评抽奖,加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表