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.pswp.cn/bicheng/78608.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/78608.shtml
英文地址,請注明出處:http://en.pswp.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;甚至可以預測未來可能發生…