go語言之字符串、指針、數組、切片、結構struct、面向對象


一: 字符串

概述:
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 {123} //指定長度和初始化字面量
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(非指針)的已初始化(非零值)的值

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM