指針
普通指針
- 通過指針也可以間接操作指向的存儲空間
格式:var p *int
var num int = 666
var value float32 = 3.14
var flag bool = false
var p1 *int
p1 = &num
fmt.Println(num)
*p1 = 678
fmt.Println(num)
var p2 *float32 = &value
fmt.Println(value)
*p2 = 6.66
fmt.Println(value)
p3 := &flag
fmt.Println(flag)
*p3 = true
fmt.Println(flag)
指向數(shù)組的指針
- 可以通過指針間接操作數(shù)組
格式:var p *[3]int
操作數(shù)組:(*p)[0] / p[0]
var arr [3]int = [3]int{1, 3, 5}
var p *[3]int
p = &arr
//p = &arr[0] //數(shù)據(jù)類型不同,不能賦值
(*p)[0] = 666
p[0] = 666
- 注意點:
- Go語言中 &數(shù)組名/&數(shù)組首元素 都是同意哦個地址,但是通過數(shù)組名不能直接獲取數(shù)組的地址(C語言中可以)
- 只有相同類型的數(shù)據(jù)才能賦值
- 指向數(shù)組的指針不支持+1 -1操作
指向切片的指針
- 切片的本質(zhì)是結(jié)構(gòu)體,結(jié)構(gòu)體的一個屬性是指針,這個指針指向了底層的一個數(shù)組
操作切片:(*p)[0]
sce := []int{1, 3, 5}
//打印sce,得到的是結(jié)構(gòu)體中指向底層數(shù)組的指針保存的值
//fmt.Printf("sce = %T\n",sce) // []int
//打印&sce,得到的是切片結(jié)構(gòu)自己的地址
//fmt.Printf("&sce = %T\n",&sce) // *[]int
var p *[]int
p = &sce
//結(jié)論: p == &sce / *p == sce
(*p)[0] = 666
sce[0] = 678
p[0] =888 // 報錯
指向字典的指針
操作字典: (*p)[key]
dict := map[string]string{"name":"yzf", "age":"18"}
var p *map[string]string
p = &dict
(*p)["name"] = "zx"
dict["name"] = "zs"
p["name"] = "ww" //報錯
指向結(jié)構(gòu)體的指針
- 和C語言一樣
操作結(jié)構(gòu)體: (*p).atter 和 p.atter
type Person struct {
name string
age int
}
var per Person = Person{"yzf", 18}
var p *Person
p = &per
(*p).name = "zs"
p.name = "zs"
指針作為函數(shù)的參數(shù)和返回值
- 地址傳遞
- 若指針指向的是一個局部變量,不建議返回(函數(shù)調(diào)用完后局部變量就釋放)
方法
方法的基本使用
- 方法就是一個特殊的函數(shù),函數(shù)獨立存在,方法和某種數(shù)據(jù)類型綁定在一起的
- 格式:
func (接收者名稱 接收者類型)函數(shù)名(形參列表)(返回值列表){
邏輯語句
}
- Go語言的函數(shù)可以和任何類型綁定,但一般用于和結(jié)構(gòu)體綁定
- 注意:
- 方法和函數(shù)的區(qū)別:函數(shù)可以==直接調(diào)用(包名.函數(shù)名)==,方法只能==通過綁定的數(shù)據(jù)類型對應(yīng)的變量來調(diào)用(變量.函數(shù)名)==
- 函數(shù)名和方法名可以重名
- 方法和函數(shù)都是函數(shù)類型
//1.定義一個結(jié)構(gòu)體
type Person struct {
name string
age int
}
//2.定義一個方法,和Person結(jié)構(gòu)體綁定
//如果指定了接收者的名稱,那么調(diào)用方法時會將調(diào)用者傳遞給接收者
func (per Person)say() {
fmt.Println("name is", per.name, "age is", per.age)
}
3.接收者存在值傳遞和地址傳遞的問題
//值傳遞
func (per Person)setName(name string) {
per.name = name
}
//地址傳遞
//只要接收者是指針類型,編譯器會自動將普通變量的地址取出來傳遞給接收者
func (per *Person)setAge(age int) {
(*per).age = age
}
func say() {
fmt.Println("hello")
}
p := Person{name:"yzf", age:18}
var ptr *Person = &p
//ptr.setName("zx")// 值傳遞
ptr.setAge(20)
p.say() //方法調(diào)用
say() //函數(shù)調(diào)用
接口
接口的基本使用
- 定義某種規(guī)范,只要使用者按照接口定義的標(biāo)準(zhǔn)來實現(xiàn),無論誰實現(xiàn)都可以使用
- 作用:用于定義函數(shù)的聲明(規(guī)定函數(shù)的形參,返回值,名稱)
- 格式
type 接口名稱 interface{
函數(shù)的聲明
}
- 注意點:
- 接口只有方法的聲明,沒有方法的實現(xiàn)
- 接口中聲明的方法,==只能通過和某種數(shù)據(jù)類型綁定的方法實現(xiàn)==,不能通過函數(shù)實現(xiàn)
- 只要某個數(shù)據(jù)類型實現(xiàn)了接口中聲明的==所有==方法,就可以說這個數(shù)據(jù)類型實現(xiàn)了這個接口
- 一個數(shù)據(jù)類型實現(xiàn)了某個接口,就可以使用這個接口類型的變量來保存這個類型的數(shù)據(jù),也可以使用接口類型變量調(diào)用接口中的方法
//1.定義一個接口
type USB interface {
//制定一個方法的標(biāo)準(zhǔn)
//名稱是start,傳入?yún)?shù)類型為string,沒返回值
start(name string)
//名稱是end,傳入?yún)?shù)類型為string,沒返回值
end(name string)
}
type Computer struct{
}
//實現(xiàn)接口中聲明的方法
func (Computer)start(name string) {
fmt.Println(name, "啟動了")
}
func (Computer)end(name string) {
fmt.Println(name, "關(guān)機了")
}
type Phone struct {
}
//實現(xiàn)接口中聲明的方法
func (Phone)start(name string) {
fmt.Println(name, "啟動了")
}
func (Phone)end(name string) {
fmt.Println(name, "關(guān)機了")
}
func main() {
cm := Computer{}
Option(cm, "DELL")
pp := Phone{}
Option(pp, "huawei")
}
//函數(shù)的作用,開關(guān)
func Option(in USB, name string) {
in.start(name)
in.end(name)
}
- ==重點掌握==:
1.定義一個接口
type USB interfa {
函數(shù)名(形參列表)(返回值列表)
函數(shù)名(形參列表)(返回值列表)
}
2.實現(xiàn)一個接口
只要某種數(shù)據(jù)類型綁定了所有接口中聲明的方法就是實現(xiàn)了這個接口
3.實現(xiàn)接口后的特性
(1)只要某種數(shù)據(jù)類型實現(xiàn)了接口,那么就可以使用接口變量保存這種數(shù)據(jù)類型
(2)只要某種數(shù)據(jù)類型實現(xiàn)了接口,那么就可以使用接口變量調(diào)用接口中聲明的方法
接口的注意點
- 接口中只能有方法的聲明,==不能有方法的實現(xiàn)和變量的聲明==
- 只有實現(xiàn)了接口中聲明的所有方法,才算實現(xiàn)了接口,才算實現(xiàn)了接口,才能使用接口變量保存
- 實現(xiàn)接口時,方法名,形參列表,返回值必須一模一樣
- 接口和結(jié)構(gòu)體一樣,可以嵌套(不能自己嵌套自己)
- 可以將超集接口變量賦值給子集接口變量,不可以講子集接口變量賦值給超集接口變量(無論實際數(shù)據(jù)類型是否實現(xiàn)了超集的所有方法)
//子集接口
type aer interface {
// aer //會報錯
start()
}
//超集接口
type ber interface {
aer //在ber中嵌套了aer
end()
}
type Phone struct {
name string
}
func (p Phone)start() {
fmt.Println(p.name, "啟動了")
}
func (p Phone)end() {
fmt.Println(p.name, "關(guān)閉了")
}
func main() {
var b ber = Phone{"華為"}
var a aer
a = b //可以賦值
a.start()
/*
var a aer = Phone{"華為"}
var b ber
b = a //會報錯
*/
}
- 接口中不能出現(xiàn)同名的方法聲明
空接口
- Go語言中的空接口,相當(dāng)于其他語言的Object類型
- Go語言中的空接口,可以充當(dāng)任何類型
- 格式: interfa{}
- 有了空接口,可以讓數(shù)組和字典保存不同類型的數(shù)據(jù)
- 注意:空接口類型的數(shù)據(jù)和普通的數(shù)據(jù)類型,在使用時有很大區(qū)別的
var value interface{}
value = 1 // 保存整型
value = 3.14 // 保存浮點類型
value = false // 保存布爾類型
value = 'T' // 保存字符
value = "lnj" // 保存字符串類型
value = [3]int{1, 3, 5} // 保存數(shù)組類型
value = []int{2, 4, 5} // 保存切片類型
value = map[string]string{"name": "lnj", "age": "18"} // 保存字典類型
value = Person{"lnj", 33} // 保存結(jié)構(gòu)體類型
// 1.定義一個數(shù)組
var arr [3]interface{}
// 2.往數(shù)組中存儲數(shù)據(jù)
arr[0] = 1
arr[1] = "lnj"
arr[2] = false
// 3.打印數(shù)據(jù)
fmt.Println(arr)
接口類型的轉(zhuǎn)換
- 如果結(jié)構(gòu)體實現(xiàn)了某個接口,那么就可以使用接口類型來保存結(jié)構(gòu)體變量
- 如果利用接口類型變量保存了實現(xiàn)了接口的結(jié)構(gòu)體,那么該變量只能訪問接口中的方法,不能訪問結(jié)構(gòu)體中特有的方法,以及結(jié)構(gòu)體中的屬性
- 如果利用結(jié)構(gòu)類型變量保存了實現(xiàn)了結(jié)構(gòu)的結(jié)構(gòu)體,想要訪問結(jié)構(gòu)體中特有的方法和屬性,那么必須進行類型轉(zhuǎn)換,將接口類型轉(zhuǎn)換為結(jié)構(gòu)體類型
type USB interface {
start()
}
type Computer struct {
name string
}
func (cm Computer)start() {
fmt.Println(cm.name, "啟動了")
}
func (cm Computer)say() {
fmt.Println(cm.name)
}
func main() {
var in USB = Computer{"huawei"}
}
in.start()
//in.say() //報錯
//fmt.Println(in.name) //報錯
//類型轉(zhuǎn)換
//方法一:使用類型斷言
//格式: cm, ok := 接口類型變量名.(結(jié)構(gòu)體類型)
cm, ok := in.(Computer)
fmt.Println(cm.name)
fmt.Println(ok)
//if cm, ok := in.(Computer); ok{
// cm.say()
// fmt.Println(cm.name)
//}
//方式二:使用類型斷言
//會將接口類型變量轉(zhuǎn)換為對應(yīng)的原始類型之后賦值給cm
//格式:cm := 接口變量名.(type)
switch cm := in.(type) {
case Computer:
fmt.Println(cm)
cm.say()
default:
fmt.Println("不是Computer")
}
cm := Computer{"惠普"}
var sce []interface{} = []interface{}{1, 3.14, false, "lnj", cm}
// 需求: 獲取切片中保存的每一個數(shù)據(jù)的原始類型
for key, value := range sce {
switch temp := value.(type) {
case int:
fmt.Println("第",key, "個元素是int類型")
case float64:
fmt.Println("第",key, "個元素是float64類型")
case bool:
fmt.Println("第",key, "個元素是bool類型")
case string:
fmt.Println("第",key, "個元素是string類型")
case Computer:
fmt.Println("第",key, "個元素是Computer類型")
temp.say()
}
//方法一除了可以將接口類型轉(zhuǎn)換為具體類型以外,還可以將抽象接口類型轉(zhuǎn)換為具體的接口類型
var in interface{}
in = Computer{"huawei"}
//in.start() //不可以利用抽象接口類型變量, 調(diào)用具體接口類型中聲明的方法
if value, ok := in.(USB); ok{
value.start()
}
//或者
if value, ok := in.(Computer); ok{
value.start()
}
別名
- 在C語言中可以通過typedef給某種類型起一個別名
格式: typedef 原類型名 新類型名;
- 在Go語言中可以通過type給某種類型起一個別名
格式一: type 新類型名 原類型名
//代表起了一個新的類型叫 新類型名(是兩個不同類型)
格式二: type 新類型名=原類型名
//代表給原類型名起了一個別名叫做 新類型名(是同一個類型)
//type newint int //報錯
type newint = int
num1 := 666
var num2 newint
num2 = num1
fmt.Println(num1,num2)