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

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

  • 前言
  • 指针
  • 结构体
    • 声明
    • 初始化
    • 使用
    • 组合引用
    • 结构体和指针
    • 结构体的标签
  • 方法
    • 例子
    • 结合结构体
    • 总结

前言

  • 我是一名多年Java开发人员,因为工作需要现在要学习go语言,Go语言之路是一个系列,记录着我从0开始接触Go,到后面能正常完成工作上的业务开发的过程,如果你也是个小白或者转Go语言的,希望我这篇文章对你有所帮助。
  • 有关go其他基础的内容的文章大家可以查看我的主页,接下来主要就是把这个系列更完,更完之后我会在每篇文章中挂上连接,方便大家跳转和复习。

go中的指针,通常在结构体中用的特别多,而方法又是结构体一部分,所以我把这三个知识点放在一起来说,这样大家可以连贯起来方便理解和吸收。

指针

指针你只需要记住两个操作符,一个是取地址符&,另一个是解引用符 *。对一个变量进行取地址,会返回对应类型的指针,下面我简单举个例子:
我们先看取地址符号:&

package mainimport "fmt"func main() {a := 1fmt.Printf("%T\n", a)b := &afmt.Println(b)fmt.Printf("%T\n", b)
}console打印:
int
*int
0xc00008c0a8

我们对变量a用&符号取地址得到变量b,打印出来b的值就是a的地址,打印出b的类型,就是一个指针,这时候再回顾一下上面这句话:对一个变量进行取地址,会返回对应类型的指针。指针b存储的是变量a的地址

我们再看看解引用符:*
解引用符第一个用处,就跟它的命名一样,解除引用,就是解除指针的引用而获得具体的值,下面我们看个例子:

import "fmt"func main() {a := 123b := &aresult := *bfmt.Println(result)fmt.Printf("%T", result)
}console打印:
123
int

通过这个代码可以看到,我们通过&取得了指向a地址的指针b,但是通过解引符号*,用*b就可以解除指针引用直接获得这个地址对应的值,也就是a的值,打印result的数据类型也是int类型。

解引用符第一个用处:声明一个变量的类型为指针类型。
这里我们指定一个变量a它的类型为int类型的指针

var a *int

打印一下a看看呢

<nil>

因为我们没有给a赋值或者初始化,所以打印出来的为nil,要么使用取地址符将其他变量的地址赋值给该指针,要不就使用内置函数:new
说到:new。go中的new和Java中的new有区别的是,go中的new是专门为指针服务的,它的用处就是新建或者说初始化一个指针
看看代码

func main() {var a *intfmt.Println(a)a = new(int)fmt.Println(a)
}

我们用new去初始化了a,看看输出呢:

<nil>
0xc00000a120

为啥输出来是一个地址呢,因为该函数会为该指针分配内存,并且指针指向对应类型的零值
用上面的知识点,接引符*来验证下是不是零值呢:

func main() {var a *intfmt.Println(a)a = new(int)fmt.Println(a)fmt.Println(*a)
}
console打印:
<nil>
0xc00000a120
0

这么一看还真是对的,我们点进new函数,看看源码怎么写的:

func new(Type) *Type

通过代码分析,我们定义的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  stringage   intphone int
}

这是一个简单的声明,跟函数一样,如果遇到相同的数据类型,也可以写一起,所以上面的age和phone可以这样写:

type UserInfo struct {name       stringage, phone int
}

初始化

注意,上面只是声明,在Java中,是以new关键词创建一个类,比如这里:new UserInfo(),但是在go中没有那么复杂,直接调用传参,看下面例子:

type UserInfo struct {name       stringage, 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
42Process finished with the exit code 0

赋值或修改值:

func main() {var user = UserInfo{name:  "John",age:   42,phone: 1000,}user.name = "一颗知足的心"user.age = 18fmt.Println(user.name)fmt.Println(user.age)
}打印:
一颗知足的心
18Process 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 stringage  int
}type Student struct {p      Personschool string
}

看看使用:

student := Student{p:      Person{name: "jack", age: 18},school: "lili school",
}
fmt.Println(student.p.name)

结构体和指针

结构体的指针和值类型的指针使用上有个小的区别,就是结构体指针在使用的时候不用解引,请看下面例子:

type UserInfo struct {name       stringage, 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 []intfunc (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 IntSliceintSlice = []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       stringage, 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打印:
20Process finished with the exit code 0

看到变化没,我们只是把receiver UserInfo改为receiver *UserInfo,变成指针接受者,就可以改变接收者本身的属性。
这是为什么呢:因为值接收者可以简单的看成一个形参,而修改一个形参的值,并不会对方法外的值造成任何影响而用指针接收者,Go 会将其解释为(&receiver).age = age。所以方法的接收者为指针时,不管调用者是不是指针,都可以修改内部的值

总结

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/78608.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[创业之路-390]:人力资源 - 社会性生命系统的解构与重构:人的角色嬗变与组织进化论

前言&#xff1a; 人、财、物、信息、机制、流程、制度、方法共同组合了一个持续的消耗资源、持续的价值创造、持续面临生存与发展、遗传与变异的社会性生命系统。 "人"是所有社会性生命系统最最基础性的要素&#xff0c;它弥漫在系统中多维立体空间的不同节点上&am…

JS执行器在UI自动化测试中的应用

前言 在进行UI自动化过程会遇到滚动条下拉、隐藏元素定位、只读属性元素的编辑、富文本处理等&#xff0c;此时可以使用JS执行器简化我们的一些处理操作。 具体应用 JS执行器的使用步骤&#xff1a; 1.先写个JS脚本&#xff0c;如果需要获取操作后的值&#xff0c;JS脚本前面…

解析Suna:全球首款开源通用AI智能体

导语&#xff1a; 嘿&#xff0c;哥们儿&#xff0c;最近 AI Agent 这块儿挺火的&#xff0c;有个叫 Suna 的开源项目冒出来挺快&#xff01;听说只用了 3 周就开发出来了&#xff0c;但功能上感觉已经能跟那个商业版的 Manus掰掰手腕了。它能帮你搞定浏览器自动化、管文件、爬…

模板方法模式:定义算法骨架的设计模式

模板方法模式&#xff1a;定义算法骨架的设计模式 一、模式核心&#xff1a;模板方法定义算法骨架&#xff0c;具体步骤延迟到子类实现 在软件开发中&#xff0c;经常会遇到这样的情况&#xff1a;某个算法的步骤是固定的&#xff0c;但具体步骤的实现可能因不同情况而有所不…

浅谈Java 内存管理:栈与堆,垃圾回收

在Java编程世界里&#xff0c;内存管理是一项极为关键的技能&#xff0c;它就像程序运行背后的“隐形守护者”&#xff0c;默默影响着程序的性能与稳定性。今天&#xff0c;咱们就来简单学习一下Java内存管理中的两大核心要点&#xff1a;栈与堆的内存分配机制&#xff0c;以及…

【WebGL小知识】WebGL平台上不同Json的比较

今天来总结一下WebGL平台上不同Json插件的差别&#xff0c;话不多说直接开始。 JsonUtility JsonUtility是Unity自带的Json解析&#xff0c;无需另外安装插件。 优点&#xff1a; Unity自带&#xff0c;兼容性好&#xff0c;WebGL平台可以使用轻量级&#xff0c;性能较好。 …

4.22tx视频后台开发一面

总时长大概在一个小时&#xff0c;主要提问C、操作系统、计网以及数据库等方面&#xff0c;最后两个算法编程题。 一上来先介绍项目 Linux下的mybash命令处理器和内存池 mybash可以再总结归纳一下&#xff0c;一上来有点紧张没有条理 内存池是用边界标识法写的&#xff0c;…

从StandardMaterial和PBRMaterial到PBRMetallicRoughnessMaterial:Babylon.js材质转换完全指南

在现代3D图形开发中&#xff0c;基于物理的渲染(PBR)已成为行业标准。本文将深入探讨如何在Babylon.js中将传统StandardMaterial和PBRMaterial转换为PBRMetallicRoughnessMaterial&#xff0c;并保持视觉一致性。 为什么需要转换&#xff1f; PBRMetallicRoughnessMaterial作…

UEditor文档在Servlet项目上的应用

UEditor 是一款功能强大的富文本编辑器&#xff0c;在项目中应用广泛。 Ueditor使用 引入 UEditor 下载 UEditor&#xff1a;从 UEditor 官方网站&#xff08;ueditor 官网&#xff09;下载适合项目需求的版本。解压文件&#xff1a;将下载的压缩包解压到项目的静态资源目录…

ThinkPHP快速使用手册

目录 介绍 安装&#xff08;windows环境&#xff09; 安装Composer 安装ThinkPHP 目录结构 配置文件 第一个接口&#xff08;Controller层&#xff09; Hello World 自定义Controller 请求参数 获取查询参数&#xff08;Get请求&#xff09; 获取指定请求参数 获取…

面向 C# 初学者的完整教程

&#x1f9f1; 一、项目结构说明 你的项目大致结构如下&#xff1a; TaskManager/ ├── backend/ │ ├── TaskManager.Core/ // 实体类和接口 │ ├── TaskManager.Infrastructure/ // 数据库、服务实现 │ └── TaskManager.API/ // We…

Axios 的 GET 和 POST 请求:前端开发中的 HTTP 通信

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

【前端】如何检查内存泄漏

在实际的场景中&#xff0c;如果观察到内存持续出现峰值&#xff0c;并且内存消耗一直没有减少&#xff0c;那可能存在内存泄漏。 使用 Chrome DevTools 来识别内存图和一些内存泄漏&#xff0c;我们需要关注以下两个方面&#xff1a; ● 使用性能分析器可视化内存消耗&#xf…

JavaScript的JSON处理Map的弊端

直接使用 Map 会遇到的问题及解决方案 直接使用 Map 会导致数据丢失&#xff0c;因为 JSON.stringify 无法序列化 Map。以下是详细分析及解决方法&#xff1a; 问题复现 // 示例代码 const myMap new Map(); myMap.set(user1, { name: Alice }); myMap.set(user2, { name: B…

【数据结构】第五弹——Stack 和 Queue

文章目录 一. 栈(Stack)1.1 概念1.2 栈的使用1.3 栈的模拟实现1.3.1 顺序表结构1.3.2 进栈 压栈1.3.3 删除栈顶元素1.3.4 获取栈顶元素1.3.5 自定义异常 1.4 栈的应用场景1.改变元素序列2. 将递归转化为循环3. 四道习题 1.5 概念分区 二. 队列(Queue)2.1 概念2.2 队列的使用2.3…

第七届能源系统与电气电力国际学术会议(ICESEP 2025)

重要信息 时间&#xff1a;2025年6月20-22日 地点&#xff1a;中国-武汉 官网&#xff1a;www.icesep.net 主题 能源系统 节能技术、能源存储技术、可再生能源、热能与动力工程 、能源工程、可再生能源技术和系统、风力发…

深入解析C++ STL Stack:后进先出的数据结构

一、引言 在计算机科学中&#xff0c;栈&#xff08;Stack&#xff09;作为一种遵循后进先出&#xff08;LIFO&#xff09;​原则的数据结构&#xff0c;是算法设计和程序开发的基础构件。C STL中的stack容器适配器以简洁的接口封装了底层容器的操作&#xff0c;为开发者提供了…

Golang | 自行实现并发安全的Map

核心思路&#xff0c;读写map之前加锁&#xff01;哈希思路&#xff0c;大map化分为很多个小map

Mac 「brew」快速安装MySQL

安装MySQL 在 macOS 上安装 MySQL 环境可以通过Homebrew快速实现&#xff0c;以下是步骤指南&#xff1a; 方法 1&#xff1a;使用 Homebrew 安装 MySQL 1. 安装 Homebrew 如果尚未安装 Homebrew&#xff0c;可以通过以下命令安装&#xff1a; /bin/bash -c "$(curl -…

【数字孪生世界的搭建之旅:从0到1理解飞渡平台】

数字孪生世界的搭建之旅&#xff1a;从0到1理解飞渡平台 前言&#xff1a;数字分身的魔法 想象一下&#xff0c;如果你能在现实世界之外&#xff0c;创造一个物理世界的"分身"&#xff0c;这个分身能完美复制现实中的一切变化&#xff0c;甚至可以预测未来可能发生…