一: 字符串
概述:
Go 語言將字符串作為 種原生的基本數據類型,字 符串的初始化可以使用字符串字面量。
(1)字符串是常量,可以通過類 數組 索引訪問其字節單元,但是不能修改某個字節的值 (2)宇符串轉換為切片[]byte( 要慎用,尤其是當數據量較大時(每轉換一次都需復制內容) a := ” hello, world !” b : = []byte (a) (3)字符串尾部不包含 NULL 字符 (4)字符串類型底層實現是一個二元的數據結構,一個是指針指向字節數組的起點,另一個是長度 (5)基於字符串創建的切片和原字符串指向相同的底層字符數組 一樣不能修改 對字符 串的切片操作返回的子串仍然是由in ,而非 slice a := ” hello, world ! b :=a[0:4 ] c :=a [1:2] d :=a [:4) (6 )字符串和切片的轉換字符串可以轉換為字節數組,也可以轉換為 Unicode 的字數組。 (7 )字符串的運算。
示例:
package main import ( "bytes" "fmt" "strconv" "strings" "time" ) func main() { fmt.Println("字符串測試") fmt.Println("字符串轉化") //獲取程序運行的操作系統平台下 int 類型所占的位數,如:strconv.IntSize。 //strconv.IntSize fmt.Println("將字符串轉換為 int 型。") var trastr01 string = "100" traint01, err_tra := strconv.Atoi(trastr01) if err_tra != nil { fmt.Println(err_tra) } else { fmt.Println(traint01) } fmt.Println("將字符串轉換為 float64 型") var trastr02 string = "100.55" trafloat01, err_float := strconv.ParseFloat(trastr02, 10) if err_float != nil { fmt.Println(err_float) } else { fmt.Println(trafloat01) } trastr03 := strconv.Itoa(99) fmt.Println("int 轉字符安 " + trastr03) var str01 string = "hello,world" str02 := "你好,世界" fmt.Println(str01) fmt.Println(str02) // //字符串比較 com01 := strings.Compare(str01, str02) if com01 == 0 { fmt.Println("相等") } else { fmt.Println("不相等 " + string(com01)) } fmt.Println(com01) //查找 包含 var isCon bool = strings.Contains(str01, "hello") fmt.Println(isCon) //true //查找位置 var theIndex int = strings.Index(str01, ",") fmt.Println(theIndex) //5 fmt.Println(strings.Index(str01, "haha")) //不存在返回-1 lastIndex := strings.LastIndex(str01, "o") fmt.Println("在字符串中最后出現位置的索引 " + strconv.Itoa(lastIndex)) //7 //-1 表示字符串 s 不包含字符串 //統計給定子串sep的出現次數, sep為空時, 返回1 + 字符串的長度 fmt.Println(strings.Count("cheeseeee", "ee")) // 3 fmt.Println(strings.Count("five", "")) // 5 // 重復s字符串count次, 最后返回新生成的重復的字符串 fmt.Println("hello " + strings.Repeat("world ", 10)) fmt.Println("替換") // 在s字符串中, 把old字符串替換為new字符串,n表示替換的次數,小於0表示全部替換 var str03 string = "/Users//Documents/GOPatch/src/MyGO/config/TestString/" str04 := strings.Replace(str03, "/", "**", -1) str05 := strings.Replace(str03, "/", "**", 4) fmt.Println(str04) //**Users****Documents**GOPatch**src**MyGO**config**TestString** fmt.Println(str05) //**Users****Documents**GOPatch/src/MyGO/config/TestString/ fmt.Println("刪除字符串的開頭和尾部") fmt.Println("刪除兩頭的/ = " + strings.Trim(str03, "/")) //Users//Documents/GOPatch/src/MyGO/config/TestString fmt.Println("刪除左邊的/ = " + strings.TrimLeft(str03, "/")) //Users//Documents/GOPatch/src/MyGO/config/TestString/ //還有 TrimRight str06 := strings.TrimSpace(" hello hao hao hao ") fmt.Println("刪除開頭末尾的空格 =" + str06) //'hello hao hao hao' fmt.Println("大小寫") str07 := "hello hao hao hao" fmt.Println(strings.Title(str07)) //Hello Hao Hao Hao fmt.Println(strings.ToLower(" Hello Hao Hao Hao")) // hello hao hao hao fmt.Println(strings.ToUpper(str07)) //HELLO HAO HAO HAO //前綴 后綴 fmt.Println(strings.HasPrefix("Gopher", "Go")) // true fmt.Println(strings.HasSuffix("Amigo", "go")) // true fmt.Println("字符串分割") fieldsStr := " hello it's a nice day today " //根據空白符分割,不限定中間間隔幾個空白符 fieldsSlece := strings.Fields(fieldsStr) fmt.Println(fieldsSlece) //[hello it's a nice day today] for i, v := range fieldsSlece { fmt.Printf("下標 %d 對應值 = %s \n", i, v) } for i := 0; i < len(fieldsSlece); i++ { fmt.Println(fieldsSlece[i]) } //根據特定字符分割 slice01 := strings.Split("q,w,e,r,t,y,", ",") fmt.Println(slice01) //[q w e r t y ] fmt.Println(cap(slice01)) //7 最后多個空"" for i, v := range slice01 { fmt.Printf("下標 %d 對應值 = %s \n", i, v) } //拼接 //Join 用於將元素類型為 string 的 slice, 使用分割符號來拼接組成一個字符串: var str08 string = strings.Join(fieldsSlece, ",") fmt.Println("Join拼接結果=" + str08) //hello,it's,a,nice,day,today fmt.Println("------------對比字符串拼接效率----------------") var buffer bytes.Buffer start := time.Now() for i := 0; i < 100000; i++ { buffer.WriteString("test is here\n") } buffer.String() // 拼接結果 end := time.Now() fmt.Println("Buffer time is ", end.Sub(start).Seconds()) start = time.Now() str := "" for i := 0; i < 100000; i++ { str += "test is here\n" } end = time.Now() fmt.Println("+= time is ", end.Sub(start).Seconds()) start = time.Now() var sl []string for i := 0; i < 100000; i++ { sl = append(sl, "test is here\n") } strings.Join(sl, "") end = time.Now() fmt.Println("Join time is", end.Sub(start).Seconds()) /* Buffer time is 0.00388283 += time is 11.730007558 Join time is 0.016644653 */ }
復合類型數據結構
基本復合數據類型有:指針、數組、切片、字典( map )、通道、結構和接口
* pointerType //指針類型使用*后面跟其指向的類型名 [n] elementType ///數紐類型使用[]后面跟數紐元素類型來表示, n表示該數組的長度 [] elementType //切片類型使用[] 后面跟切片元素類型來表示 map [keyType] valueType //map類型使用map[鍵類型]值類型來表示 chan valueType //通道使用chan后面跟通道元素類型表示 interface{ //接口類型 interface{} 將各個方法括起來 method1(inputParams)(returnParams) method2(inputParams) (returnParams) )
二:指針
概述: 指針聲明類型為*T,多指針為**T 通過變量名前加&來獲取變量的地址
1)在賦值語句中,*T出現在”=“ 左邊表示聲明,*T出現在”=“右邊表示取指針指向的值(varName)
var a=11 p := &a //*p和a的值都是11
2)結構體指針訪問結構體字段然仍使用”.“ 點操作符
import "fmt" type User struct { name string age int } func main(){ andes := User{ name: "andes", age: 18, } p := &andes fmt.Println(p.name) //p.name 通過“.”操作符訪問成員變量 }
指針的使用
package main import "fmt" func main() { // 聲明變量 var a int = 20 // 聲明指針 var ip *int // 指針變量的存儲 ip = &a fmt.Printf("a變量地址是:%x\n", &a) //指針變量存儲的地址 fmt.Printf("ip變量存儲的指針地址:%x\n", ip) //使用指針訪問值 fmt.Printf("*ip變量的值:%d\n", *ip) }
3)go不支持指針運算
Go 由於支持垃圾回收,如果支持指針運算,則會給垃圾回收的 現帶來很多不
a := 1234 P := &a p++ // 不允許,報 non-numeric type *int 錯誤
4)函數中允許返回局部變量的地址
Go 編譯器使用“戰逃逸 機制將這種局部變量的空間分配在堆上
package main import "fmt" func sum (a , b int) *int { sum := a + b return &sum //允許, sum會分配在heap上 } func main(){ fmt.Println(sum(1,2)) //打印內存地址 }
指針練習
程序獲取一個int變量num的地址並打印
將num的地址賦給指針ptr,並通過ptr去修改num的值
package main import "fmt" func main(){ //定義變量 var num int =10 fmt.Println(&num) //定義指針 var ptr *int //指針賦值 ptr = &num //根據指針修改值 *ptr=20 fmt.Println(num) }
三: 數組
概述:
數組的類型名是 [n]emetType ,其中n是數組長度, elementType 是數組元素類型
數組一般在 創建時通過字面量初始化,單獨聲明一個數組類型變量而不進行初始化是沒有意義的。
1)數組初始化
a : = [3]int {1,2,3} //指定長度和初始化字面量 a : = [ . . . ]int{l , 2 , 3} //不指定長度,但是由后面的初始 列表數量來確定其長度 a : = [3]int{l : l , 2 : 3) // 指定總長度,並通過索引值進行初始化,沒有初始化元素時使用類型默認值 a : =[ ...}int{l :1,2 : 3) //不指定總長度,通過索引值進行初始化,數組長度由最后一個索引值確定,沒有指定索引的元 被初始化為類型的零值
2)數組的特點:
1 )數組創建完長度就固定了,不可以再追加元素。 2 )數組是值類型的,數組賦值或作為函數參數都是值拷貝。 3 )數組長度是數組類型的組成部分,[10]int 和[20]int 表示不同的類型。 4 )可以根據數組創建切片
3) 數組相關操作
1 數組元素的訪問
package main import "fmt" func main() { a :=[...]int{1,2,3} b := a[0] for i,v :=range a{ fmt.Println(i,v) fmt.Println(b) } }
2 數組切片的長度
package main import "fmt" func main(){ a := [...]int{1,2,3} alengh :=len(a) for i :=0; i<alengh; i++{ fmt.Println(a) //[1 2 3] fmt.Println(alengh) //3 數組長度 fmt.Println(i) //2 索引值 } }
四:切片
概述:
Go 語言的數組的定長性和值拷貝限制了其使用場景, Go 提供了另一種數據類型 lic (中文為切片),
這是 種變長數組,其數據結構中有指向數組的指針,所以是 種引用類型
package main import "unsafe" func main(){ type slice struct { arry unsafe.Pointer len int cap int //cap()函數返回的是數組切片分配的空間大小 } }
Go 為切片維護 個元素一一指向底層數組的指針、切片的元素數量和底層數組的容量。
切片的相關操作
1 切片的創建:
1)由數組創建
語法:
array[b:c],array表示數組名;b表示開始索引,可以不指定,默認是0;c表示結束索引,可以不指定,默認是len(array),數組長度,
package main import ( "fmt" ) func main(){ //創建有7個int類型元素的數組 var array = [...]int{0,1,2,3,4,5,6} s1 := array[0:4] s2 := array[:4] s3 := array[2:] fmt.Println("%v\n",s1) // [0 1 2 3] fmt.Println("%v\n",s2) // [0 1 2 3] fmt.Println("%v\n",s3) // [2 3 4 5 6] }
2)通過內置函數make創建切片
注意:由make創建的切片各元素被默認初始化為切片元素類型的零值
package main import "fmt" func main(){ //len=10,cap=10 創建數組a a := make([]int,10) //len=10,cap=5 創建數組b b := make([]int,10,15) fmt.Printf("%v\n",a) //[0 0 0 0 0 0 0 0 0 0] fmt.Printf("%v\n",b) //[0 0 0 0 0 0 0 0 0 0] }
注意:直接聲明切片類型變量是沒有意義的
func main(){ var a []int fmt.Printf("%v\n",a) //結采為 [] }
此時切片a底層的數據結構
3)切片支持的操作
1 內置函數 len()返回切片長度 2 內置函數 cap()返回切片底層數組容量。 3 內置函數 ppend()對切片追加元素。 4 內置函數 copy() 用於 一個切片
package main import ( "fmt" ) func main() { a := [...]int{0,1,2,3,4,5,6} b := make([]int,2,4) c := a[0:3] fmt.Println(b) //[0 0] fmt.Println(len(b)) //2 fmt.Println(cap(b)) // 4 b = append(b,1) //切片尾部追加元素 1 fmt.Println(b) //[0 0 1] fmt.Println(len(b)) //3 fmt.Println(c) //[0 1 2] fmt.Println(cap(b)) //4 b = append(b,c...) fmt.Println(b) //[0 0 1 0 1 2] fmt.Println(len(b)) //6 fmt.Println(cap(b)) //8 d := make([]int,2,2) copy(d,c) //copy只會復制d和c中長度最小的 fmt.Println(d) //[0 1] fmt.Println(len(d)) //2 fmt.Println(cap(d)) //2 }
示例二
package main import "fmt" func main() { // 定義數組 arr := [...]int{0,1,2,3,4,5,6,7} // 切片取值 fmt.Println("arr[2:6]=",arr[2:6]) fmt.Println("arr[:6]=",arr[:6]) fmt.Println("arr[2:]=",arr[2:]) fmt.Println("arr[:]=",arr[:]) }
l內建函數append():向切片尾部添加數據
package main import "fmt" func main() { //空切片 var s1 []int s1=append(s1,2,3) s1=append(s1, 4,5,6) fmt.Println("s1=" ,s1) //s1= [2 3 4 5 6] //創建指定大小的切片 s2 := make([]int,5) //創建長度為5的並初始化為0的切片 s2 =append(s2,6) fmt.Println(s2) //[0 0 0 0 0 6] //創建並初始化切片 s3 := []int{1,2,3} s3 =append(s3,4,5) fmt.Println(s3) //[1 2 3 4 5] }
示例2
package main import ( "fmt" ) func main() { //1 go 語言切片是對原數組的映射,並沒有創建一個真正的切片 //定義數組 arr :=[...]int{0,1,2,3,4,5,6,7} //取切片 s1 :=arr[2:6] fmt.Println(s1) //[2 3 4 5] s2 := s1[3:5] fmt.Println(s2) //[5 6] s3 := append(s2,10) fmt.Println(s3) //[5 6 10] s4 :=append(s3,11) fmt.Println(s4) //[5 6 10 11] s5 :=append(s4,12) fmt.Println(s5) //[5 6 10 11 12] }
內置函數copy()
package main import "fmt" func main() { //go語言切片是對原數組的映射,並滅有創建一個真正的切片 //定義數組 data := [...]int{0,1,2,3,4,5,6,7,8,9} //取切片 8,9 s1 := data[8:] s2 := data[:5] //將后面切片元素,拷貝到前面切面里面 //copy()是從前往后添加並覆蓋 copy(s2,s1) fmt.Println(s2) //[8 9 2 3 4] fmt.Println(data) // [8 9 2 3 4 5 6 7 8 9] }
go語言切片實際上是view操作
package main import ( "fmt" ) func main() { //1go語言切片是對原數組的映射,並沒有創建一個真正的切片 //定義數組 arr :=[...]int{1,2,3,4,5,6,7} //去切片 s1 :=arr[2:] //修改值 s1[0] = 100 fmt.Println(s1) //[100 4 5 6 7] fmt.Println(arr) //[1 2 100 4 5 6 7] fmt.Println() //go 語言切片美譽取到的位置,可以反向延申,不可向前延申 s3 :=arr[2:6] fmt.Println(s3) //[100 4 5 6] s4 :=s3[3:5] fmt.Println(s4) //[6 7] //容量大小 fmt.Println("s3=%v,len(s3)=%d,cap(s3)=%d\n",s3,len(s3),cap(s3)) // [100 4 5 6] 4 5 }
4)字符串切片的相互轉換
package main import "fmt" func main(){ str := "hello,世界" //通過字符串字面量初始化 個字符串 str a := []byte(str) //將字符串轉換為[]byte 類型切片 b := []rune(str) //將字符串轉換為[]rune 類型切片 fmt.Println(a) //[104 101 108 108 111 44 228 184 150 231 149 140] fmt.Println(b) //[104 101 108 108 111 44 19990 30028] }
五: map
概述:
go語言內置的字典類型叫map。map的類型格式:
map[K]T,其中K可以是任意可以進行比較的類型
T值類型。map也是一種引用類型
1)map的創建
package main import "fmt" func main() { //1 創建map var m1 map[int]string fmt.Println(m1==nil) //賦值報錯因為是空的 //m1[1]="xxx" //2 := m2 :=map[int]string{} m3 :=make(map[int]string) fmt.Println(m2,m3) fmt.Println(m2[1]) //3 指定容量 m4 :=make(map[int]string,10) fmt.Println(m4) fmt.Println(m4[1]) }
示例
package main import "fmt" func main(){ ma := map[string]int { "a": 1 , "b": 2} fmt.Println(ma ["a"]) //1 fmt.Println(ma ["b"]) //2 }
map初始化
package main import ( "fmt" ) func main() { //1 定義並初始化 var m1 map[int]string= map[int]string{1:"hello",2:"world"} fmt.Println(m1) //2 自動推斷類型 m2 := map[int]string{1:"hello",2:"world"} fmt.Println(m2) }
2)使用內置的make函數創建
package main import "fmt" func main(){ //make(map[K]T) //map 容量使用默認位 //make(map[K]T,len) //map 的容量使用給定 len值 mpl := make(map[int]string) mp2 := make(map[int]string,10) mpl[l] = "tom" mp2[1] = "pony" fmt.Println(mpl[l] ) //tom fmt.Println(mp2[1] ) //pony }
3)map支持的操作
1 map 的單個鍵值訪問格式為mapName[key], 更新某個key 值時mapName[key]等號左邊,訪某個key的值時 mapName[key]放在等號的右邊。 2 可以使用range遍歷一個map類型變量,但是不保證每次迭代 元素的順序。 3 刪除map中的某個鍵值用如下語法: delete(mapName,key)。 delete是內置函數用來刪除 map 中的某個鍵值對 4 可以使用內置的len()函數返回map中的鍵值對數量。
package main import ( "fmt" ) func main(){ mp := make(map[int]string) mp[1] = "tom" mp[1] = "pony" mp[2] = "jsky" mp[3] = "andes" delete(mp,3) fmt.Println(mp[1]) //pony fmt.Println(len(mp)) //2 for k,v := range mp{ fmt.Println("key",k,"value",v) //key 2 value jsky //key 1 value pony } }
示例2
package main import ( "fmt" ) func main() { m1 :=map[int]string{1:"湖南小小鮮肉",2:"成都小妹妹"} m1[0]="山西肉蒲團" m1[3] ="東莞小姐姐" fmt.Println(m1) //map[0:山西肉蒲團 1:湖南小小鮮肉 2:成都小妹妹 3:東莞小姐姐] //make() m2 :=make(map[int]string,10) m2[0]="山西挖煤團" m2[1]="內蒙放羊團" fmt.Println(m2) //map[0:山西挖煤團 1:內蒙放羊團] fmt.Println(m2[0],m2[1]) // 山西挖煤團 內蒙放羊團 }
遍歷與刪除
package main import ( "fmt" ) func main() { m1 := map[int]string{1:"肉蒲團",2:"黃梅"} m1[1] = "東京衛視" m1[3] = "北京衛視" fmt.Println(m1) //map[1:東京衛視 2:黃梅 3:北京衛視] //make() m2 :=make(map[int]string,10) m2[0]="東京熱" m2[1]="小澤瑪麗呀" fmt.Println(m2) //map[0:東京熱 1:小澤瑪麗呀] fmt.Println(m2[0],m2[1]) // 東京熱 小澤瑪麗呀 //map遍歷 for k := range m1{ fmt.Println("%d----->%s\n",k,m1[k]) /* %d----->%s 1 東京衛視 %d----->%s 2 黃梅 %d----->%s 3 北京衛視 */ //判斷map里面有沒有此鍵 value,ok := m1[7] fmt.Println("value1=",value,",ok1=",ok) //value1= ,ok1= false } //刪除delete delete(m1,2) fmt.Println(m1) //map[1:東京衛視 3:北京衛視] }
注意:
1 Go 內置的map不是並發安全的,並發安全的map可以使用標准包sync中的map 2 不要直接修改map value內某個元素的值,如果想修改 map 的某個鍵值,則必須整體賦值
package main import "fmt" type User struct { name string age int } func main(){ ma := make(map[int]User) andes := User{ name:"andes", age :18, } ma[1] = andes //必須整體賦值 //ma[1].age=19 //error ,不能通過map引用直接修改 andes.age=19 fmt.Printf("%v\n",ma) //map[1:{andes 18}] fmt.Println(andes.age) //19 }
六: struct(結構)
概述:由多個不同類型的元素組合而成,有兩層含義: 1 struct 結構中的類型可以是任意類型 2 struct 的存儲空間是連續的其字段按照聲明時的順序存放(字段之間有對齊的要求) struct有兩種形式: 1 struct字面量 2使用type聲明自定義struct類型
1 struct類型字面量
struct類型字面量的聲明格式如下
struct { FeildName FeildType //字段名 字段類型 FeildName FeildType FeildName FeildType }
2 自定義struct類
type TypeName struct { FeildName Fe ldType //字段名 字段類型 FeildName FeildType FeildName FeildType }
type自定義類型關鍵字,不但支持struct類型的創建,還支持任意其他任意子定義類型的創建
3 struct 類型變量的初始化
package main import "fmt" //定義學生結構體 type Student struct { id int name string sex byte age int addr string } func main(){ //1順序初始化 //字符串用雙引號,字符用單引 var s1 Student = Student{1,"約漢",'f',18,"xx"} fmt.Println(s1) //{1 約漢 102 18 xx} s2 :=Student{2,"Jack",'m',20,"yy"} fmt.Println(s2) //{2 Jack 109 20 yy} //2 指定初始化 s3 :=Student{id:3,age:20} fmt.Println(s3) //{3 0 20 } //3 結構體作為指針變量初始化 var s4 *Student = &Student{4,"擼死",'m',24,"jd"} fmt.Println(s4) //&{4 擼死 109 24 jd} //指針訪問變量 //go底層會先判斷傳的是值還是指針類型 //若是指針類型,go會將s4 id替換(*s4).id fmt.Println((s4).id) //4 fmt.Println(s4.id) //4 s5 :=&Student{5,"xx",'f',27,"sz"} fmt.Println(s5) //&{5 xx 102 27 sz} }
示例
package main import "fmt" func main() { type Person struct { name string age int } type student struct { *Person Number int } //按照類型聲明順序,逐個賦值 //不推薦這種初始化方式,一旦struct增加字段,則整個初始化語句會報錯 a := Person{"Tom", 21} //推薦這種使用Feild名字的初始化方式,沒有指定的字段則默認初始化為類型的零值 P := &Person{ name: "tata", age: 12, } s := student{ Person: P, Number:110, } fmt.Println(a) //{Tom 21} fmt.Println(P) //&{tata 12} fmt.Println(s) //{0xc000078420 110} }
4 結構體參數
1 結構體可以作為函數參數傳遞 2 結構體作為指針或非指針效果不一樣
package main import ( "fmt" ) type Student struct { id int name string sex string age int addr string } func tmpStudent(tmp Student) { tmp.id=250 fmt.Println("tmp=",tmp) //tmp= {250 zhangsan f 20 sz00} } func tmpStudent2(p *Student) { p.id =249 fmt.Println("tmp=",p) //tmp= &{249 zhangsan f 20 sz00} } func main() { var s Student = Student{1,"zhangsan","f",20,"sz00"} //傳遞非指針對象 tmpStudent(s) fmt.Println("main s=",s) //main s= {1 zhangsan f 20 sz00} //傳遞指針對象 tmpStudent2(&s) fmt.Println("main s2=",s) // main s2= {249 zhangsan f 20 sz00} }
七:面向對象
1 簡介:
1 go語言對於面向對象的設計非常簡潔而優雅 2 沒有封裝、繼承、多態這些概念,但同樣通過別的方式實現這些特性 1封裝:通過方法實現 2繼承:通過匿名字段實現 3多態:通過接口
2 匿名字段
go支持只提供類型而不寫字段名的方式,也就是匿名字段,也稱為嵌入字段
package main import "fmt" type Person struct { name string sex string age int } //學生 type Student struct { //匿名字段 Person id int addr string } func main() { //1.順序初始化 s1 :=Student{Person{"傑克","female",18},1,"sz"} fmt.Println(s1) //{{傑克 female 18} 1 sz} // 部分初始化 s2 := Student{Person: Person{name:"接客"}, id: 2} fmt.Println(s2) // {接客 0} 2 } }
同名字段情況
package main import ( "fmt" ) //人的結構體 type Person struct { name string sex string age int } type Student struct { //匿名字段 Person id int addr string //和Person中的字段同名 name string } func main() { var s Student s.name="約漢" fmt.Println(s) //{{ 0} 0 約漢} //默認是就近原則賦值 //若需要賦值上一層,需要顯式調用 s.Person.name="傑克" fmt.Println(s) //{{傑克 0} 0 約漢} }
所有的內置類型和自定義類型都是可以作為匿名字段去使用
package main import ( "fmt" ) //人的結構體 type Person struct { name string sex string age int } //自定義類型 type mystr string //學生 type Student struct { //結構體類型匿名字段 Person //內置類型匿名字段 int //自定義類型匿名字段 mystr } func main() { s1 := Student{Person{"lisi","male",18},1,"bj"} fmt.Println(s1) //{{lisi male 18} 1 bj} fmt.Println(s1.name) // lisi }
指針類型匿名字段
package main import ( "fmt" ) //人的結構體 type Person struct { name string sex string age int } //學生 type Student struct { //結構體類型的匿名字段 *Person id int addr string } func main() { s1 := Student{&Person{"wwangwu","male",18},1,"sz"} fmt.Println(s1) //{0xc000088300 1 sz} fmt.Println(s1.name) // wwangwu }
3 方法
1 在面向對象編程中,一個對象其實也就是一個簡單的值或者一個變量,在這個對象中會包含一些函數 2 這種帶有接收者的函數,我們稱為方法,本質上,一個方法則是一個和特殊類型關聯的函數 2 方法的語法如下 func (接收參數名 接收類型) 方法名(參數列表)(返回值) 1可以給任意自定義類型(包括內置類型,但不包括指針類型)添加相應的方法 2接收類型可以是指針或非指針類型 3為類型添加方法
基礎類型作為接收者
package main import "fmt" type Myint int //定義方法,實現兩個數相加 func Add(a,b Myint) Myint{ return a + b } func (a Myint) Add(b Myint) Myint{ return a + b } func main() { var a Myint =1 var b Myint=1 //面向過程調用 fmt.Println("Add(a,b)",Add(a,b)) //Add(a,b) 2 //面向對象的調用 fmt.Println("a.Add(b)",a.Add(b)) //a.Add(b) 2 }
結構體作為接收者
package main import "fmt" type Person struct { name string sex string age int } //給Person添加方法 func (p Person) PrintInfo() { fmt.Println(p.name, p.sex, p.age) } func main() { p := Person{"zhaoliu", "male", 20} p.PrintInfo() }
值語義和引用語義
package main import "fmt" type Person struct { name string sex string age int } //給成員賦值,引用語義 func (p *Person) SetInfoPointer() { (*p).name = "tianqi" p.sex = "female" p.age = 22 } //值語義 func (p Person) SetInfoValue() { p.name = "zhouba" p.sex = "male" p.age = 25 } func main() { //指針作為接收者的效果 p1 := Person{"xxx", "male", 18} fmt.Println("函數調用前=", p1) (&p1).SetInfoPointer() fmt.Println("函數調用后=", p1) //值作為接收者 p2 := Person{"yyy", "female", 30} fmt.Println("函數調用前=", p2) p2.SetInfoValue() fmt.Println("函數調用后=", p2) }
方法的繼承
package main import "fmt" type Person struct { name string sex string age int } //為Person定義方法 func (p *Person) PrintInfo() { fmt.Printf("%s,%s,%d\n", p.name, p.sex, p.age) } //繼承上面的方法 type Student struct { Person id int addr string } func main() { p := Person{"xxx", "male", 20} p.PrintInfo() s := Student{Person{"yyy", "male", 20}, 1, "bj"} s.PrintInfo() }
方法的重寫
package main import "fmt" type Person struct { name string sex string age int } //為Person定義方法 func (p *Person) PrintInfo() { fmt.Printf("%s,%s,%d\n", p.name, p.sex, p.age) } //繼承上面的方法 type Student struct { Person id int addr string } //相當於實現了方法重寫 func (s *Student) PrintInfo() { fmt.Printf("Student:%s,%s,%d\n", s.name, s.sex, s.age) } func main() { p := Person{"xxx", "male", 20} p.PrintInfo() s := Student{Person{"yyy", "male", 20}, 1, "bj"} s.PrintInfo() //顯式調用 s.Person.PrintInfo() }
方法值和方法表達式
package main import "fmt" type Person struct { name string sex string age int } func (p *Person) PrintInfoPointer() { //%p是地址,%v是值 fmt.Printf("%p,%v\n", p, p) } func main() { p := Person{"zhangsan", "male", 18} //傳統調用方式 p.PrintInfoPointer() //go語義方法值特性 pFunc1 := p.PrintInfoPointer pFunc1() //go方法表達式特性 pFunc2 := (*Person).PrintInfoPointer pFunc2(&p) }
練習:創建屬性的getter和setter方法並進行調用
package main import "fmt" //練習:創建屬性的getter和setter方法並進行調用 type Dog struct { name string // 1公0母 sex int } func (d *Dog) SetName(name string) { d.name = name } func (d *Dog) GetName() string { return d.name } //dog咬人的方法 func (d *Dog) bite() { fmt.Printf("%s咬人了,起鍋燒油", d.name) } func test01() { //創建dog對象 d := Dog{"二哈", 1} d.bite() } func main() { test01() }
注意:
new()和make() 1 new()用來分配內存,但與其他語言中的同名函數不同,它不會初始化內存,只會將內存置零 2 make(T)會返回一個指針,該指針指向新分配的,類型為T的零值,適用於創建結構體 3 make()的目的不同於new(),它只能創建slice、map、channel,並返回類型為T(非指針)的已初始化(非零值)的值