Go中的方法能給用戶自定義的類型添加新的行為。它和函數的區別在于方法有一個接收者,給一個函數添加一個接收者,那么它就變成了方法。接收者可以是值接收者,也可以是指針接收者。
在調用方法的時候,值類型既可以調用值接收者的方法,也可以調用指針接收者的方法;指針類型既可以調用指針接收者的方法,也可以調用值接收者的方法。
也就是說,不管方法的接收者是什么類型,該類型的值和指針都可以調用,不必嚴格符合接收者的類型。
~~~go
package main
import "fmt"
type Person struct {
age int
}
func (p Person) Elegance() int {
return p.age
}
func (p *Person) GetAge() {
p.age += 1
}
func main() {
// p1 是值類型
p := Person{age: 18}
// 值類型 調用接收者也是值類型的方法
fmt.Println(p.howOld())
// 值類型 調用接收者是指針類型的方法
p.GetAge()
fmt.Println(p.GetAge())
// ----------------------
// p2 是指針類型
p2 := &Person{age: 100}
// 指針類型 調用接收者是值類型的方法
fmt.Println(p2.GetAge())
// 指針類型 調用接收者也是指針類型的方法
p2.GetAge()
fmt.Println(p2.GetAge())
}
~~~
運行
~~~go
18
19
100
101
~~~
| 函數和方法 | 值接收者 | 指針接收者 |
| --- | --- | --- |
| 值類型調用者 | 方法會使用調用者的一個副本,類似于“傳值” | 使用值的引用來調用方法,上例中,p1.GetAge() 實際上是 (&p1).GetAge(). |
| 指針類型調用者 | 指針被解引用為值,上例中,p2.GetAge()實際上是 (\*p1).GetAge() | 實際上也是“傳值”,方法里的操作會影響到調用者,類似于指針傳參,拷貝了一份指針 |
如果實現了接收者是值類型的方法,會隱含地也實現了接收者是指針類型的方法。
如果方法的接收者是值類型,無論調用者是對象還是對象指針,修改的都是對象的副本,不影響調用者;如果方法的接收者是指針類型,則調用者修改的是指針指向的對象本身。
通常我們使用指針作為方法的接收者的理由:
* 使用指針方法能夠修改接收者指向的值。
* 可以避免在每次調用方法時復制該值,在值的類型為大型結構體時,這樣做會更加高效。
因而呢,我們是使用值接收者還是指針接收者,不是由該方法是否修改了調用者(也就是接收者)來決定,而是應該基于該類型的本質。
如果類型具備“原始的本質”,也就是說它的成員都是由 Go 語言里內置的原始類型,如字符串,整型值等,那就定義值接收者類型的方法。像內置的引用類型,如 slice,map,interface,channel,這些類型比較特殊,聲明他們的時候,實際上是創建了一個 header, 對于他們也是直接定義值接收者類型的方法。這樣,調用函數時,是直接 copy 了這些類型的 header,而 header 本身就是為復制設計的。
如果類型具備非原始的本質,不能被安全地復制,這種類型總是應該被共享,那就定義指針接收者的方法。比如 go 源碼里的文件結構體(struct File)就不應該被復制,應該只有一份實體。
接口值的零值是指動態類型和動態值都為 nil。當僅且當這兩部分的值都為 nil 的情況下,這個接口值就才會被認為 接口值 == nil。
- Golang基礎
- Go中new與make的區別
- Golang中除了加Mutex鎖以外還有哪些方式安全讀寫共享變量
- 無緩沖Chan的發送和接收是否同步
- Golang并發機制以及它所使用的CSP并發模型.
- Golang中常用的并發模型
- Go中對nil的Slice和空Slice的處理是一致的嗎
- 協程和線程和進程的區別
- Golang的內存模型中為什么小對象多了會造成GC壓力
- Go中數據競爭問題怎么解決
- 什么是channel,為什么它可以做到線程安全
- Golang垃圾回收算法
- GC的觸發條件
- Go的GPM如何調度
- 并發編程概念是什么
- Go語言的棧空間管理是怎么樣的
- Goroutine和Channel的作用分別是什么
- 怎么查看Goroutine的數量
- Go中的鎖有哪些
- 怎么限制Goroutine的數量
- Channel是同步的還是異步的
- Goroutine和線程的區別
- Go的Struct能不能比較
- Go的defer原理是什么
- Go的select可以用于什么
- Context包的用途是什么
- Go主協程如何等其余協程完再操作
- Go的Slice如何擴容
- Go中的map如何實現順序讀取
- Go中CAS是怎么回事
- Go中的逃逸分析是什么
- Go值接收者和指針接收者的區別
- Go的對象在內存中是怎樣分配的
- 棧的內存是怎么分配的
- 堆內存管理怎么分配的
- 在Go函數中為什么會發生內存泄露
- G0的作用
- Go中的鎖如何實現
- Go中的channel的實現
- 棧的內存是怎么分配的2
- 堆內存管理怎么分配的2
- Go中的map的實現
- Go中的http包的實現原理
- Goroutine發生了泄漏如何檢測
- Go函數返回局部變量的指針是否安全
- Go中兩個Nil可能不相等嗎
- Goroutine和KernelThread之間是什么關系
- 為何GPM調度要有P
- 如何在goroutine執行一半就退出協程
- Mysql基礎
- Mysql索引用的是什么算法
- Mysql事務的基本要素
- Mysql的存儲引擎
- Mysql事務隔離級別
- Mysql高可用方案有哪些
- Mysql中utf8和utf8mb4區別
- Mysql中樂觀鎖和悲觀鎖區別
- Mysql索引主要是哪些
- Mysql聯合索引最左匹配原則
- 聚簇索引和非聚簇索引區別
- 如何查詢一個字段是否命中了索引
- Mysql中查詢數據什么情況下不會命中索引
- Mysql中的MVCC是什么
- Mvcc和Redolog和Undolog以及Binlog有什么不同
- Mysql讀寫分離以及主從同步
- InnoDB的關鍵特性
- Mysql如何保證一致性和持久性
- 為什么選擇B+樹作為索引結構
- InnoDB的行鎖模式
- 哈希(hash)比樹(tree)更快,索引結構為什么要設計成樹型
- 為什么索引的key長度不能太長
- Mysql的數據如何恢復到任意時間點
- Mysql為什么加了索引可以加快查詢
- Explain命令有什么用
- Redis基礎
- Redis的數據結構及使用場景
- Redis持久化的幾種方式
- Redis的LRU具體實現
- 單線程的Redis為什么快
- Redis的數據過期策略
- 如何解決Redis緩存雪崩問題
- 如何解決Redis緩存穿透問題
- Redis并發競爭key如何解決
- Redis的主從模式和哨兵模式和集群模式區別
- Redis有序集合zset底層怎么實現的
- 跳表的查詢過程是怎么樣的,查詢和插入的時間復雜度
- 網絡協議基礎
- TCP和UDP有什么區別
- TCP中三次握手和四次揮手
- TCP的LISTEN狀態是什么
- 常見的HTTP狀態碼有哪些
- 301和302有什么區別
- 504和500有什么區別
- HTTPS和HTTP有什么區別
- Quic有什么優點相比Http2
- Grpc的優缺點
- Get和Post區別
- Unicode和ASCII以及Utf8的區別
- Cookie與Session異同
- Client如何實現長連接
- Http1和Http2和Grpc之間的區別是什么
- Tcp中的拆包和粘包是怎么回事
- TFO的原理是什么
- TIME_WAIT的作用
- 網絡的性能指標有哪些