zoukankan      html  css  js  c++  java
  • 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 
    ", 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 
    ", 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
    ")
    }
    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
    "
    }
    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
    ")
    }
    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
    ", &a)
       //指针变量存储的地址
       fmt.Printf("ip变量存储的指针地址:%x
    ", ip)
       //使用指针访问值
       fmt.Printf("*ip变量的值:%d
    ", *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
    ",s1)   // [0 1 2 3]
        fmt.Println("%v
    ",s2)    // [0 1 2 3]
        fmt.Println("%v
    ",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
    ",a)   //[0 0 0 0 0 0 0 0 0 0]
        fmt.Printf("%v
    ",b)   //[0 0 0 0 0 0 0 0 0 0] 
    }

      注意:直接声明切片类型变量是没有意义的

    func main(){
        var a  []int
        fmt.Printf("%v
    ",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
    ",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
    ",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
    ",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
    ", 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
    ", p.name, p.sex, p.age)
    }
    
    //继承上面的方法
    type Student struct {
       Person
       id   int
       addr string
    }
    
    //相当于实现了方法重写
    func (s *Student) PrintInfo() {
       fmt.Printf("Student:%s,%s,%d
    ", 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
    ", 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(非指针)的已初始化(非零值)的值
  • 相关阅读:
    服务器与本地时间的倒计时
    没有花括号(大括号)的for循环也能正确执行
    js瀑布流效果
    AQS详解(AbstractQueuedSynchronizer)
    SimpleDateFormat的线程安全问题与解决方案
    jvm不打印异常栈
    Java中的序列化Serialable高级详解
    java梳理-序列化与反序列化
    AQS详解
    对ConditionQueue和锁的理解
  • 原文地址:https://www.cnblogs.com/liucsxiaoxiaobai/p/10802696.html
Copyright © 2011-2022 走看看