• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

一、Go基础知识入门

武飞扬头像
Iamlvxiaosha
帮助1

1、go语言介绍

学新通

学新通

学新通

学新通 学新通

学新通

学新通

2、go开发环境搭建

2.1、go的安装

学新通

  • 双击go1.20.2.windows-amd64.msi,点击"Next",然后勾选同意,再点击"Next"。

学新通

学新通

  • 选择Go的安装位置,这里我选择了"D:\0-software\0-develop\10-GO\1-go1.20.2"。再点击"Next"。

学新通

  • 点击"Install"进行安装。然后点击"Finish"就安装完成了。

学新通

学新通

 学新通

2.2、go的环境变量配置 

  • 此电脑->右键"属性"->"高级系统设置"->"环境变量"

学新通

学新通

  • 在系统变量里面添加:

  • 变量名:GOPATH

    变量值:D:\0-software\0-develop\10-GO\1-go1.20.2

学新通

  • 在Path里面添加:%GOPATH%\bin

学新通

  • 然后打开cmd,输入"go version",打印下图所示,说明环境变量配置成功。

学新通

goLand的安装、配置

学新通

2.3、go开发之hello-world

  • 点击"File"->"new"->"Go File",编辑文件名为"Hello"

    学新通

     学新通

  • 可以看到默认会有一个package包名。每一个go的源码文件都要指定包名。并且只有package是main的代码才会运行。学新通

  • func表示函数的意思。

学新通

  • 注意:Go语言的Hello World你想要运行的话要注意:

    1. 包名必须叫main

    2. 函数名也必须叫main

    3. 同一个目录下只能有一个func main()

  • 点击绿色的三角按钮执行。

学新通

  • 还可以通过另外一种方式运行:

    1. 进入到Hello.go所在目录文件夹

      学新通

    2. 然后执行go build .\Hello.go,这样可以将Hello.go编译成可执行文件。

      学新通

    3. 然后执行:.\Hello.exe,就可以执行了。

      学新通

    4. 如果想省略编译过程直接执行,可以输入:

      go run .\Hello.go
      

      这样就不会编译生成Hello.exe文件,而且还会执行。

3、变量和常量

3.1、如何定义变量

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    // 全局变量和局部变量(定义在main外面的变量都是全局变量)
  6.  
    //var sex = "male"
  7.  
    //var ok bool = true
  8.  
     
  9.  
    // 简洁方式定义全局变量
  10.  
    var (
  11.  
    sex = "male"
  12.  
    ok = true
  13.  
    age = 18
  14.  
    )
  15.  
     
  16.  
    //局部变量定义了就必须使用,不使用会报错;全局变量定义了之后可以不使用,不会报错。
  17.  
     
  18.  
    func main() {
  19.  
    //go是静态语言,静态语言和动态语言相比,定义变量差异很大
  20.  
    //1、变量必须想定义后使用 2、变量必须有类型 3、变量类型定下来之后不能改变
  21.  
    //定义变量的方式
  22.  
    //方式一:var variableName variableType
  23.  
    var name string
  24.  
    name = "旺财"
  25.  
    //演示:变量类型定下来之后不能改变
  26.  
    //name = 1
  27.  
     
  28.  
    fmt.Print(name)
  29.  
     
  30.  
    /*
  31.  
    方式二:var variableName = variableValue
  32.  
    可以省略variableType,因为go可以通过variableValue进行类型推断。
  33.  
    */
  34.  
     
  35.  
    var name2 = "小强"
  36.  
    fmt.Print(name2)
  37.  
     
  38.  
    /*
  39.  
    方式三:variableName := variableValue
  40.  
    这种平时用的比较多
  41.  
    */
  42.  
     
  43.  
    //var age = 1
  44.  
    age := 1
  45.  
    fmt.Print(age)
  46.  
     
  47.  
    //go语言中局部变量定义了但是不使用,是不行的。
  48.  
     
  49.  
    //2、多变量定义
  50.  
    //2、多变量定义
  51.  
    //2.1、同时在一行里面定义3string类型的变量
  52.  
    var user1, user2, user3 string
  53.  
    fmt.Print(user1, user2, user3)
  54.  
    //2.2、同时在一行里面定义3个变量,并且初始化。
  55.  
    var user4, user5, user6 = "小强", "旺财", 1
  56.  
    fmt.Print(user4, user5, user6)
  57.  
     
  58.  
    /*
  59.  
    注意:
  60.  
    变量必须先定义再使用
  61.  
    go语言是静态语言,要求变量的类型和肤质类型一致
  62.  
    局部变量名不能冲突;全局变量和局部变量的变量名可以重复,这种情况下,局部变量的优先级高。
  63.  
    简洁变量定义不能用于全局变量(方式三:variableName := variableValue)
  64.  
    变量是有零值的
  65.  
    局部变量定义了就必须使用,不使用会报错;全局变量定义了之后可以不使用,不会报错。
  66.  
    */
  67.  
     
  68.  
    }

3.2、常量的定义和使用

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
    //常量,定义的时候就指定的值,不能修改。
  7.  
    //常量定义的时候全部大写。多个单词中间加下划线。
  8.  
    const PI1 float32 = 3.1415926535897932384626 //显式定义
  9.  
    const PI2 = 3.1415926535897932384626 //隐式定义
  10.  
     
  11.  
    // 同时定义多个常量
  12.  
    const (
  13.  
    UNKNOWN = 1
  14.  
    FEMALE = 2
  15.  
    MALE = 3
  16.  
    )
  17.  
     
  18.  
    // 同时定义多个常量,并且支持类型定义。
  19.  
    const (
  20.  
    X int = 1
  21.  
    Y
  22.  
    S = "abc"
  23.  
    Z
  24.  
    M
  25.  
    )
  26.  
    fmt.Println(X, Y, S, Z, M)
  27.  
     
  28.  
    /*
  29.  
    1、常量类型只可以定义bool、数值(整数、浮点数和复数) 和 字符串;
  30.  
    2、不曾使用的常量,没有强制使用的要求;
  31.  
    3、显式指定类型的时候,必须确保常量 左右值类型一致。
  32.  
    4、常量在定义的时候如果没有设置类型和值的话,它就用前面的类型和值。
  33.  
    */
  34.  
     
  35.  
    }

代码执行结果:

学新通

         从打印结果可以看出,Y没有定义类型和值,但是Y可以使用前面X的类型和值。M也是同样的道理。

3.3、iota的使用细节

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
    //iota,特殊常量,可以认为是一个可以被编译器修改的常量。
  7.  
    const (
  8.  
    ERR1 = iota 1
  9.  
    ERR2
  10.  
    ERR3
  11.  
    ERR4 = "abc"
  12.  
    ERR5
  13.  
    ERR6 = 100
  14.  
    ERR7 = iota
  15.  
    )
  16.  
     
  17.  
    const (
  18.  
    ERRNEW1 = iota
  19.  
    )
  20.  
     
  21.  
    fmt.Println(ERR1, ERR2, ERR3, ERR4, ERR5, ERR6, ERR7)
  22.  
    fmt.Println(ERRNEW1)
  23.  
    /*
  24.  
    如果中断了iota那么必须显式的恢复,后续会自动递增。
  25.  
    自增类型默认是int类型
  26.  
    iota能简化const类型的定义
  27.  
    每次出现const的时候,iota初始化为0
  28.  
    */
  29.  
     
  30.  
    }
代码运行结果:

3.4、匿名变量的定义和用途

  1.  
    package main
  2.  
     
  3.  
    func a() (int, bool) {
  4.  
    return 0, false
  5.  
    }
  6.  
     
  7.  
    func main() {
  8.  
    //匿名变量:就是一个下划线
  9.  
    var _ int
  10.  
    //r1, ok := a()
  11.  
    _, ok := a()
  12.  
    /*
  13.  
    如果我接下来只想使用ok,不想使用r1,根据Go的局部变量定义规定的,定义了就必须使用,那我就必须在下面被迫的加一个多余的打印r1的代码。
  14.  
    有什么好办法吗?
  15.  
    这时,匿名变量就登场了。可以使用"_"代替r1,这样在下面可以不用也不会报错了。
  16.  
    */
  17.  
    if ok {
  18.  
    //打印
  19.  
     
  20.  
    }
  21.  
    }

3.5、变量的作用域

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    // 全局变量:任何一个函数都可以使用这个变量
  6.  
    var globalVariable = "旺财"
  7.  
     
  8.  
    func main() {
  9.  
    //变量的作用域
  10.  
     
  11.  
    //局部变量
  12.  
    localVariable := "小强"
  13.  
    fmt.Print(localVariable)
  14.  
     
  15.  
    {
  16.  
    //局部变量
  17.  
    localVariable2 := "张三"
  18.  
    fmt.Print(localVariable2)
  19.  
    }
  20.  
    //在局部变量作用域外部打印该变量会报错
  21.  
    //fmt.Print(localVariable2)
  22.  
     
  23.  
    }

4、go的基础数据类型

4.1、数值、浮点数和字符类型

学新通

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
    //基础数据类型
  7.  
    //var a int8
  8.  
    //var b int16
  9.  
    //var c int32
  10.  
    //var d int64
  11.  
    //var ua uint8
  12.  
    //var ub uint16
  13.  
    //var uc uint32
  14.  
    //var ud uint64
  15.  
    动态类型,用的时候就会知道,用起来挺麻烦的
  16.  
    //var e int
  17.  
    //
  18.  
    类型转换需要强转才行
  19.  
    //a = int8(b)
  20.  
    //
  21.  
    //var f1 float32
  22.  
    //var f2 float64
  23.  
    //
  24.  
    //f1 = 3
  25.  
    //f2 = 3.14
  26.  
     
  27.  
    //主要是用来存放英文字符的
  28.  
    var c byte
  29.  
    c = 'a' 1
  30.  
    fmt.Println(c) //值是98
  31.  
    fmt.Printf("c=%c", c) //打印的是 c=b
  32.  
     
  33.  
    c1 := 97
  34.  
    fmt.Println()
  35.  
    fmt.Printf("c1=%c", c1) //打印的是 c=a
  36.  
     
  37.  
    //也是字符,主要是用来存放中文字符的
  38.  
    var c2 rune
  39.  
    c2 = '旺'
  40.  
    fmt.Println()
  41.  
    fmt.Printf("c=%c", c2) //打印的是 c=
  42.  
     
  43.  
    //字符串
  44.  
    var name string
  45.  
    name = "My name is Peter Parker,I am a Super Hero. I don't like the Criminals."
  46.  
    fmt.Println()
  47.  
    fmt.Println(name)
  48.  
    }
运行结果:

学新通

bool类型

        布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true

数值型

  1. 整数型

可以简单讲解一下二进制和位数的关系,以及int和uint的关系

  • int8 有符号 8 位整型 (-128 到 127) 长度:8bit

  • int16 有符号 16 位整型 (-32768 到 32767)

  • int32 有符号 32 位整型 (-2147483648 到 2147483647)

  • int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

  • uint8 无符号 8 位整型 (0 到 255) 8位都用于表示数值:

  • uint16 无符号 16 位整型 (0 到 65535)

  • uint32 无符号 32 位整型 (0 到 4294967295)

  • uint64 无符号 64 位整型 (0 到 18446744073709551615)

  1. 浮点型

  • float32 32位浮点型数

  • float64 64位浮点型数

其他

  • byte 等于 uint8

  • rune 等于 int32

  • uint 32 或 64 位

字符

        Golang中没有专门的字符类型,如果要存储单个字符(字母),一般使用byte来保存。 字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。也就是说对于传统的字符串是由字符组成的,而Go的字符串不同,它是由字节组成的。

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    )
  6.  
     
  7.  
    func main() {
  8.  
     
  9.  
    var a byte
  10.  
    a = 'a'
  11.  
    //输出ascii对应码值 。。 这里说明一下什么是ascii码
  12.  
    fmt.Println(a)
  13.  
    fmt.Printf("a=%c", a)
  14.  
    }

        字符常量只能使用单引号括起来,例如:var a byte = ‘a’ var a int = ‘a’

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    )
  6.  
     
  7.  
    func main() {
  8.  
     
  9.  
    var a byte
  10.  
    a = "a"
  11.  
    //输出ascii对应码值 。。 这里说明一下什么是ascii码
  12.  
    fmt.Println(a)
  13.  
    fmt.Printf("a=%c", a)
  14.  
    }

        字符本质是一个数字, 可以进行加减乘除

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "reflect"
  6.  
    )
  7.  
     
  8.  
    func main() {
  9.  
     
  10.  
    a := 'a'
  11.  
     
  12.  
    //这里注意一下 1. a 1可以和数字计算 2.a 1的类型是32 3. int类型可以直接变成字符
  13.  
     
  14.  
    fmt.Println(reflect.TypeOf(a 1))
  15.  
    fmt.Printf("a 1=%c", a 1)
  16.  
    }

字符串

        字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。

4.2、基本类型的转换

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "strconv"
  6.  
    )
  7.  
     
  8.  
    func main() {
  9.  
    //基本类型转换
  10.  
    //int和int相互转换
  11.  
    var a int8 = 12
  12.  
    //var b = uint8(a)
  13.  
    //fmt.Print(b)
  14.  
     
  15.  
    //float转int
  16.  
    //var f float32 = 3.14
  17.  
    //var c = int32(f)
  18.  
    //fmt.Println(c) //打印的是3
  19.  
     
  20.  
    //int转float
  21.  
    //var f64 = float64(a)
  22.  
    //fmt.Println(f64) //打印的是12
  23.  
     
  24.  
    //定义别名
  25.  
    type IT int //类型要求很严格
  26.  
    var c = IT(a)
  27.  
    fmt.Println(c)
  28.  
     
  29.  
    //字符串转数字
  30.  
    var istr = "12"
  31.  
    myint, err := strconv.Atoi(istr)
  32.  
    if err != nil {
  33.  
    fmt.Println("convert erro")
  34.  
    }
  35.  
    fmt.Println(myint)
  36.  
     
  37.  
    //数字转字符串
  38.  
    var myi = 32
  39.  
    fmt.Println(strconv.Itoa(myi))
  40.  
     
  41.  
    }

type 关键字:

        在 Go 语言中,type 关键字常被我们用来创建新的结构体。同样的,type 也可以用来创建其它的用户自定义类型。在创建新类型时,有类型别名和类型定义两种方式,你知道它们的区别吗?

1、type 关键字

        对于使用过 Go 语言的人,相信对type关键字都不陌生,它可以帮助我们定义结构体或接口:

  1.  
    type Student struct{
  2.  
     
  3.  
    }
  4.  
     
  5.  
    type Man interface {
  6.  
    Name()
  7.  
    }

        在使用 type 定义结构体时,我们可以把它看作是基于struct{}类型定义了一个新的类型Student

        其实,除了用于创建新类型,type还有创建类型别名的作用。

2、类型别名 vs 类型定义

基于一个类型创建一个别名,称之为类型别名 (alias)。

基于一个类型创建一个新类型,称之为类型定义 (definition)。

  1.  
    type Int1 = int // 类型别名,Int1 是 int 类型的别名
  2.  
    type Int2 int // 类型定义,Int2 是新类型

它们之间的区别是什么呢?

        以上述定义的Int1Int2为例,我们可以用int类型的变量初始化Int1类型,因为Int1只是int类型的一个别名。

        但如果我们用一个int类型的变量初始化Int2类型时,对Int2类型的初始化会报错,因为Int2是一个新的类型。

  1.  
    type Int1 = int // 类型别名,Int1 是 int 类型的别名
  2.  
    type Int2 int // 类型定义,Int2 是新类型
  3.  
     
  4.  
    var i int = 0
  5.  
     
  6.  
    var i1 Int1 = i
  7.  
    var i2 Int2 = i // error

        不过,Int1类型和Int2类型都可以用相应类型的字面量来初始化。

  1.  
    type Int1 = int // 类型别名,Int1 是 int 类型的别名
  2.  
    type Int2 int // 类型定义,Int2 是新类型
  3.  
     
  4.  
    var i1 Int1 = 0
  5.  
    var i2 Int2 = 0

4.2.1、简单的转换操作

  1.  
    valueOfTypeB = typeB(valueOfTypeA)
  2.  
    代码块1
  3.  
    // 浮点数
  4.  
    a := 5.0
  5.  
     
  6.  
    // 转换为int类型
  7.  
    b := int(a)
  8.  
     
  9.  
    //Go允许在底层结构相同的两个类型之间互转。例如:
  10.  
    // IT类型的底层是int类型
  11.  
    type IT int
  12.  
     
  13.  
    // a的类型为IT,底层是int
  14.  
    var a IT = 5
  15.  
     
  16.  
    // 将a(IT)转换为int,b现在是int类型
  17.  
    b := int(5)
  18.  
     
  19.  
    // 将b(int)转换为IT,c现在是IT类型
  20.  
    c := IT(b)
  21.  
     
  22.  
    var a int32 = 1
  23.  
    var b int64 = 3
  24.  
    b = int64(a)
  25.  
    fmt.Println(a, b)
  26.  
     
  27.  
    /*
  28.  
    不是所有数据类型都能转换的,例如字母格式的string类型"abcd"转换为int肯定会失败
  29.  
    低精度转换为高精度时是安全的,高精度的值转换为低精度时会丢失精度。例如int32转换为int16,float32转换为int
  30.  
    这种简单的转换方式不能对int(float)和string进行互转,要跨大类型转换,可以使用strconv包提供的函数
  31.  
    */

4.2.2、strconv

Itoa和Atoi

int转换为字符串:Itoa()

println("a"   strconv.Itoa(32))  // a32

string转换为int:Atoi()(表示 alphanumeric to integer)是把字符串转换成整型数的一个函数

  1.  
    i,_ := strconv.Atoi("3")
  2.  
    println(3 i) // 6
  3.  
     
  4.  
    // Atoi()转换失败
  5.  
    i,err := strconv.Atoi("a")
  6.  
    if err != nil {
  7.  
    println("converted failed")
  8.  
    }
  9.  
    //由于string可能无法转换为int,所以这个函数有两个返回值:第一个返回值是转换成int的值,第二个返回值判断是否转换成功。

4.3、format进行格式化转换

Parse类函数

        Parse类函数用于转换字符串为给定类型的值:ParseBool()、ParseFloat()、ParseInt()、ParseUint()。

  1.  
    b, err := strconv.ParseBool("true")
  2.  
    f, err := strconv.ParseFloat("3.1415", 64)
  3.  
    i, err := strconv.ParseInt("-42", 10, 64)
  4.  
    u, err := strconv.ParseUint("42", 10, 64)

ParseInt()和ParseUint()有3个参数:

  1.  
    func ParseInt(s string, base int, bitSize int) (i int64, err error)
  2.  
    func ParseUint(s string, base int, bitSize int) (uint64, error)

说明:

  1. bitSize参数表示转换为什么位的int/uint,有效值为0、8、16、32、64。当bitSize=0的时候,表示转换为int或uint类型。例如bitSize=8表示转换后的值的类型为int8或uint8。

  2. base参数表示以什么进制的方式去解析给定的字符串,有效值为0、2-36。当base=0的时候,表示根据string的前缀来判断以什么进制去解析:0x开头的以16进制的方式去解析,0开头的以8进制方式去解析,其它的以10进制方式解析。

Format类函数

        将给定类型格式化为string类型:FormatBool()、FormatFloat()、FormatInt()、FormatUint()。

  1.  
    s := strconv.FormatBool(true)
  2.  
    s := strconv.FormatFloat(3.1415, 'E', -1, 64)
  3.  
    s := strconv.FormatInt(-42, 16) //表示将-42转换为16进制数,转换的结果为-2a。
  4.  
    s := strconv.FormatUint(42, 16)

        第二个参数base指定将第一个参数转换为多少进制,有效值为2<=base<=36。当指定的进制位大于10的时候,超出10的数值以a-z字母表示。例如16进制时,10-15的数字分别使用a-f表示,17进制时,10-16的数值分别使用a-g表示。

FormatFloat()参数众多:

func FormatFloat(f float64, fmt byte, prec, bitSize int) string

        bitSize表示f的来源类型(32:float32、64:float64),会据此进行舍入。

        fmt表示格式:‘f’(-ddd.dddd)、‘b’(-ddddp±ddd,指数为二进制)、‘e’(-d.dddde±dd,十进制指数)、‘E’(-d.ddddE±dd,十进制指数)、‘g’(指数很大时用’e’格式,否则’f’格式)、‘G’(指数很大时用’E’格式,否则’f’格式)。

        prec控制精度(排除指数部分):对’f’、‘e’、‘E’,它表示小数点后的数字个数;对’g’、‘G’,它控制总的数字个数。如果prec 为-1,则代表使用最少数量的、但又必需的数字来表示f。

4.4、运算符和表达式

4.4.1、算数运算符

      • / %(求余) –

4.4.2、关系运算符

== != > < >= <=

4.4.3、逻辑运算符

&& 所谓逻辑与运算符。如果两个操作数都非零,则条件变为真
|| 所谓的逻辑或操作。如果任何两个操作数是非零,则条件变为真
! 所谓逻辑非运算符。使用反转操作数的逻辑状态。如果条件为真,那么逻辑非操后结果为假

这个和python不一样,python中使用 and or来连接

  1.  
    package main
  2.  
  3.  
    import "fmt"
  4.  
  5.  
    func main() {
  6.  
      var a bool = true
  7.  
      var b bool = false
  8.  
      if ( a && b ) {
  9.  
         fmt.Printf("第一行 - 条件为 true\n" )
  10.  
      }
  11.  
      if ( a || b ) {
  12.  
         fmt.Printf("第二行 - 条件为 true\n" )
  13.  
      }
  14.  
      /* 修改 a 和 b 的值 */
  15.  
      a = false
  16.  
      b = true
  17.  
      if ( a && b ) {
  18.  
         fmt.Printf("第三行 - 条件为 true\n" )
  19.  
      } else {
  20.  
         fmt.Printf("第三行 - 条件为 false\n" )
  21.  
      }
  22.  
      if ( !(a && b) ) {
  23.  
         fmt.Printf("第四行 - 条件为 true\n" )
  24.  
      }
  25.  
    }

4.4.4、位运算符

位运算符对整数在内存中的二进制位进行操作。 下表列出了位运算符 &, |, 和 ^ 的计算:

P Q P & Q P | Q P ^ Q
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

假定 A = 60; B = 13; 其二进制数转换为: A = 0011 1100 B = 0000 1101

A&B = 0000 1100

A|B = 0011 1101

A^B = 0011 0001 Go 语言支持的位运算符如下表所示。假定 A 为60,B 为13:

运算符 描述 实例
& 按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 (A & B) 结果为 12, 二进制为 0000 1100
| 按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或 (A | B) 结果为 61, 二进制为 0011 1101
^ 按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 (A ^ B) 结果为 49, 二进制为 0011 0001
<< 左移运算符"<<“是双目运算符。左移n位就是乘以2的n次方。 其功能把”<<“左边的运算数的各二进位全部左移若干位,由”<<"右边的数指定移动的位数,高位丢弃,低位补0。 A << 2 结果为 240 ,二进制为 1111 0000
>> 右移运算符">>"是双目运算符。右移n位就是除以2的n次方。  
其功能是把">>“左边的运算数的各二进位全部右移若干位,”>>"右边的数指定移动的位数。 A >> 2 结果为 15 ,二进制为 0000 1111  
  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
     
  7.  
    var a uint = 60 /* 60 = 0011 1100 */
  8.  
    var b uint = 13 /* 13 = 0000 1101 */
  9.  
    var c uint = 0
  10.  
     
  11.  
    c = a & b /* 12 = 0000 1100 */
  12.  
    fmt.Printf("第一行 - c 的值为 %d\n", c )
  13.  
     
  14.  
    c = a | b /* 61 = 0011 1101 */
  15.  
    fmt.Printf("第二行 - c 的值为 %d\n", c )
  16.  
     
  17.  
    c = a ^ b /* 49 = 0011 0001 */
  18.  
    fmt.Printf("第三行 - c 的值为 %d\n", c )
  19.  
     
  20.  
    c = a << 2 /* 240 = 1111 0000 */
  21.  
    fmt.Printf("第四行 - c 的值为 %d\n", c )
  22.  
     
  23.  
    c = a >> 2 /* 15 = 0000 1111 */
  24.  
    fmt.Printf("第五行 - c 的值为 %d\n", c )
  25.  
    }

4.4.5、赋值运算符

下表列出了所有Go语言的赋值运算符。

运算符 描述 实例
= 简单的赋值运算符,将一个表达式的值赋给一个左值 C = A B 将 A B 表达式结果赋值给 C
= 相加后再赋值 C = A 等于 C = C A
-= 相减后再赋值 C -= A 等于 C = C - A
*= 相乘后再赋值 C *= A 等于 C = C * A
/= 相除后再赋值 C /= A 等于 C = C / A
%= 求余后再赋值 C %= A 等于 C = C % A
<<= 左移后赋值 C <<= 2 等于 C = C << 2
>>= 右移后赋值 C >>= 2 等于 C = C >> 2
&= 按位与后赋值 C &= 2 等于 C = C & 2
^= 按位异或后赋值 C ^= 2 等于 C = C ^ 2
|= 按位或后赋值 C |= 2 等于 C = C | 2
  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
    var a int = 21
  7.  
    var c int
  8.  
     
  9.  
    c = a
  10.  
    fmt.Printf("第 1 行 - = 运算符实例,c 值为 = %d\n", c )
  11.  
     
  12.  
    c = a
  13.  
    fmt.Printf("第 2 行 - = 运算符实例,c 值为 = %d\n", c )
  14.  
     
  15.  
    c -= a
  16.  
    fmt.Printf("第 3 行 - -= 运算符实例,c 值为 = %d\n", c )
  17.  
     
  18.  
    c *= a
  19.  
    fmt.Printf("第 4 行 - *= 运算符实例,c 值为 = %d\n", c )
  20.  
     
  21.  
    c /= a
  22.  
    fmt.Printf("第 5 行 - /= 运算符实例,c 值为 = %d\n", c )
  23.  
     
  24.  
    c = 200;
  25.  
     
  26.  
    c <<= 2
  27.  
    fmt.Printf("第 6行 - <<= 运算符实例,c 值为 = %d\n", c )
  28.  
     
  29.  
    c >>= 2
  30.  
    fmt.Printf("第 7 行 - >>= 运算符实例,c 值为 = %d\n", c )
  31.  
     
  32.  
    c &= 2
  33.  
    fmt.Printf("第 8 行 - &= 运算符实例,c 值为 = %d\n", c )
  34.  
     
  35.  
    c ^= 2
  36.  
    fmt.Printf("第 9 行 - ^= 运算符实例,c 值为 = %d\n", c )
  37.  
     
  38.  
    c |= 2
  39.  
    fmt.Printf("第 10 行 - |= 运算符实例,c 值为 = %d\n", c )
  40.  
     
  41.  
    }

4.4.6、其他运算符

此处讲解一下什么是地址

运算符 描述 实例
& 返回变量存储地址 &a; 将给出变量的实际地址。
* 指针变量。 *a; 是一个指针变量
  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
    var a int = 4
  7.  
    var b int32
  8.  
    var c float32
  9.  
    var ptr *int
  10.  
     
  11.  
    /* 运算符实例 */
  12.  
    fmt.Printf("第 1 行 - a 变量类型为 = %T\n", a );
  13.  
    fmt.Printf("第 2 行 - b 变量类型为 = %T\n", b );
  14.  
    fmt.Printf("第 3 行 - c 变量类型为 = %T\n", c );
  15.  
     
  16.  
    /* &* 运算符实例 */
  17.  
    ptr = &a /* 'ptr' 包含了 'a' 变量的地址 */
  18.  
    fmt.Printf("a 的值为 %d\n", a);
  19.  
    fmt.Printf("*ptr 为 %d\n", *ptr);
  20.  
    }

4.4.7、运算符优先级

有些运算符拥有较高的优先级,二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低

优先级 分类 运算符 结合性
1 逗号运算符 , 从左到右
2 赋值运算符 =、 =、-=、*=、/=、 %=、 >=、 <<=、&=、^=、|= 从右到左
3 逻辑或 || 从左到右
4 逻辑与 && 从左到右
5 按位或 | 从左到右
6 按位异或 ^ 从左到右
7 按位与 & 从左到右
8 相等/不等 ==、!= 从左到右
9 关系运算符 <、<=、>、>= 从左到右
10 位移运算符 <<、>> 从左到右
11 加法/减法 、- 从左到右
12 乘法/除法/取余 *(乘号)、/、% 从左到右
13 单目运算符 !、*(指针)、& 、 、–、 (正号)、-(负号) 从右到左
14 后缀运算符 ( )、[ ]、-> 从左到右

当然,你可以通过使用括号来临时提升某个表达式的整体运算优先级。

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
    var a int = 20
  7.  
    var b int = 10
  8.  
    var c int = 15
  9.  
    var d int = 5
  10.  
    var e int;
  11.  
     
  12.  
    e = (a b) * c / d; // ( 30 * 15 ) / 5
  13.  
    fmt.Printf("(a b) * c / d 的值为 : %d\n", e );
  14.  
     
  15.  
    e = ((a b) * c) / d; // (30 * 15 ) / 5
  16.  
    fmt.Printf("((a b) * c) / d 的值为 : %d\n" , e );
  17.  
     
  18.  
    e = (a b) * (c / d); // (30) * (15/5)
  19.  
    fmt.Printf("(a b) * (c / d) 的值为 : %d\n", e );
  20.  
     
  21.  
    e = a (b * c) / d; // 20 (150/5)
  22.  
    fmt.Printf("a (b * c) / d 的值为 : %d\n" , e );
  23.  
    }

5、字符串基本操作

5.1、rune和字符串长度

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
    //5.1节 rune和字符串长度
  7.  
    //长度计算
  8.  
    name := "lvxiaosha学go"
  9.  
    fmt.Println(len(name))
  10.  
     
  11.  
    //将字符串类型的name转换为切片,然后就可以for循环了。
  12.  
    bytes := []byte(name)
  13.  
    fmt.Println(len(bytes))
  14.  
    //因为 len() 传入 返回字符串所占的字节 ,而中文转 utf8 编码后占 3 字节,英文占 1 字节。所以上面两个输出结果都是:9 3 2 = 14
  15.  
     
  16.  
    //如果我们想得到字符长度,而不是字节长度,就需要转为 切片 后再计算长度。
  17.  
    runes := []rune(name)
  18.  
    fmt.Println(len(runes)) // 10
  19.  
     
  20.  
    }

运行结果:

学新通

5.2、转义符

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
    //5.2节转义符
  7.  
    //想在字符串中打印双引号
  8.  
    //方法1:使用反斜线加双引号(\")
  9.  
    name := "\"吕小傻\""
  10.  
    fmt.Println(name)
  11.  
     
  12.  
    //方法2:使用Tab键上面的那个键(`),注意不是单引号(')。
  13.  
    //这个和python中的三引号("""xxx""")里面可以随便写内容类似。
  14.  
    //我们大多数使用的都是这个方法。
  15.  
    name2 := `"吕小傻"`
  16.  
    fmt.Println(name2)
  17.  
    }

转义符

转义字符 意义 ASCII码值(十进制)
\n 换行(LF) ,将当前位置移到下一行开头 010
\r 回车(CR) ,将当前位置移到本行开头 013
\t 水平制表(HT) (跳到下一个TAB位置) 009
\\ 代表一个反斜线字符’’\’ 092
\’ 代表一个单引号(撇号)字符 039
\" 代表一个双引号字符 034
\? 代表一个问号 063

5.3、格式化输出

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "strconv"
  6.  
    )
  7.  
     
  8.  
    func main() {
  9.  
    //5.3节格式化输出
  10.  
    userName := "吕小傻"
  11.  
    address := "山东省青岛市"
  12.  
    age := 18
  13.  
    mobile := "13624558575"
  14.  
     
  15.  
    fmt.Println("用户名:" userName, "地址:" address, "年龄:" strconv.Itoa(age), "电话:" mobile) //这种拼接方式极难维护
  16.  
    fmt.Printf("用户名:%s,地址:%s,年龄:%d,电话:%s\r\n", userName, address, age, mobile) //这种方式更常用,但是性能没有上一种好。
  17.  
     
  18.  
    userMessage := fmt.Sprintf("用户名:%s,地址:%s,年龄:%d,电话:%s", userName, address, age, mobile)
  19.  
    fmt.Println(userMessage)
  20.  
     
  21.  
    var ages []int = []int{1, 2, 3}
  22.  
    agesMessage := fmt.Sprintf("年龄:%#v", ages)
  23.  
    fmt.Println(agesMessage)
  24.  
    }

学新通

缺省格式和类型

格式化后的效果 动词 描述
[0 1] %v 缺省格式
[]int64{0, 1} %#v go语法打印
[]int64 %T 类型打印

整型(缩进, 进制类型, 正负符号)

格式化后的效果 动词 描述
15 %d 十进制
15 % d 必须显示正负符号
␣␣15 M Pad空格(宽度为4,右对齐)
15␣␣ %-4d Pad空格 (宽度为4,左对齐)
1111 %b 二进制
17 %o 八进制
f %x 16进制,小写

字符(有引号, Unicode)

Value: 65   (Unicode letter A)

格式化后的效果 动词 描述
A %c 字符
‘A’ %q 有引号的字符
U 0041 %U Unicode
U 0041 ‘A’ %#U Unicode 有引号

浮点(缩进, 精度, 科学计数)

Value: 123.456

格式化后的效果 动词 描述
1.234560e 02 %e 科学计数
123.456000 %f 十进制小数

字符串or 字节slice (引号, 缩进, 16进制)

Value: "cafe"

格式化后的效果 动词 描述
cafe %s 字符串原样输出
␣␣cafe %6s 宽度为6,右对齐

5.4、高性能字符串拼接-string.builder

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "strconv"
  6.  
    "strings"
  7.  
    )
  8.  
     
  9.  
    func main() {
  10.  
    //5.4节 高性能字符串拼接-string.builder
  11.  
    userName := "吕小傻"
  12.  
    address := "山东省青岛市"
  13.  
    age := 18
  14.  
    mobile := "13624558575"
  15.  
     
  16.  
    var builder strings.Builder
  17.  
    builder.WriteString("用户名:")
  18.  
    builder.WriteString(userName)
  19.  
    builder.WriteString(",地址:")
  20.  
    builder.WriteString(address)
  21.  
    builder.WriteString(",年龄:")
  22.  
    builder.WriteString(strconv.Itoa(age))
  23.  
    builder.WriteString(",电话:")
  24.  
    builder.WriteString(mobile)
  25.  
     
  26.  
    response := builder.String()
  27.  
    fmt.Println(response)
  28.  
    }

运行结果:

学新通

 5.5、字符串的比较

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    func main() {
  6.  
    //5.5节 字符串的比较
  7.  
    a := "hello"
  8.  
    b := "hello"
  9.  
    fmt.Println(a == b)
  10.  
    fmt.Println(a != b)
  11.  
     
  12.  
    //字符串的大小比较
  13.  
    fmt.Println(a > b)
  14.  
     
  15.  
    }

学新通

5.6、字符串操作常用方法

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "strings"
  6.  
    )
  7.  
     
  8.  
    func main() {
  9.  
    //5.6节 字符串操作常用方法
  10.  
    //strings常用方法
  11.  
    //是否包含
  12.  
    name := "My name is Peter Parker,I am a Super Hero.I don't like the Criminals."
  13.  
    fmt.Println(strings.Contains(name, "Peter Parker"))
  14.  
     
  15.  
    //字符串长度
  16.  
    runes := []rune(name)
  17.  
    fmt.Println(len(runes))
  18.  
     
  19.  
    //查询字符串出现的次数
  20.  
    fmt.Println(strings.Count(name, "a"))
  21.  
     
  22.  
    //分割字符串
  23.  
    fmt.Println(strings.Split(name, ","))
  24.  
     
  25.  
    //字符串是否包含前缀,是否包含后缀。
  26.  
    fmt.Println(strings.HasPrefix(name, "My"))
  27.  
    fmt.Println(strings.HasSuffix(name, "Criminals."))
  28.  
     
  29.  
    //查询子串出现的位置
  30.  
    fmt.Println(strings.Index(name, "Peter Parker"))
  31.  
    fmt.Println(strings.IndexRune(name, []rune(name)[17]))
  32.  
     
  33.  
    //字符串替换
  34.  
    fmt.Println(strings.Replace(name, "Peter Parker", "Super Man", 1)) // 1表示只替换第一个
  35.  
    fmt.Println(strings.Replace(name, "Peter Parker", "Iron Man", -1)) // -1表示全部替换
  36.  
     
  37.  
    //大小写转换
  38.  
    fmt.Println(strings.ToLower("Peter Parker"))
  39.  
    fmt.Println(strings.ToUpper("Peter Parker"))
  40.  
     
  41.  
    //去掉字符串左右两边的特殊字符,下面的方法表示,只要字符串两边出现了"#""$",就去掉。
  42.  
    //除了strings.Trim,还有strings.TrimLeft和strings.TrimRight方法
  43.  
    fmt.Println(strings.Trim("$#Peter #Parker#", "#$"))
  44.  
     
  45.  
    }

运行结果:

学新通

6.1、if条件判断

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    /*
  6.  
    *
  7.  
     
  8.  
    if 布尔表达式 {
  9.  
    逻辑
  10.  
    }
  11.  
    */
  12.  
    func main() {
  13.  
    //6.1if条件判断
  14.  
    age := 18
  15.  
    country := "中国"
  16.  
    //简单的if条件判断,布尔表达式的括号省略
  17.  
    if age < 18 {
  18.  
    fmt.Println("未成年")
  19.  
    }
  20.  
     
  21.  
    //复杂的if条件判断,布尔表达式的括号不能省略
  22.  
    if (age < 18) && (country == "中国") {
  23.  
    fmt.Println("未成年")
  24.  
    } else if age == 18 {
  25.  
    fmt.Println("刚刚成年")
  26.  
    } else {
  27.  
    fmt.Println("成年了")
  28.  
    }
  29.  
     
  30.  
    }

6.2、for循环基础用法

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    )
  6.  
     
  7.  
    /*
  8.  
    * go语言中只有for循环,没有while循环。
  9.  
    * for init; condition; post {
  10.  
    * //do something
  11.  
    * }
  12.  
    */
  13.  
    func main() {
  14.  
    //6.1for循环基础用法
  15.  
    //标准写法
  16.  
    for i := 0; i < 3; i {
  17.  
    fmt.Println(i)
  18.  
    }
  19.  
     
  20.  
    //初始变量在循环外面定义
  21.  
    var j int
  22.  
    for ; j < 3; j {
  23.  
    fmt.Println(j)
  24.  
    }
  25.  
     
  26.  
    // init; condition; post 都没有,就等效与while(true)
  27.  
    //var k int
  28.  
    //for {
  29.  
    // //睡眠2
  30.  
    // time.Sleep(2 * time.Second)
  31.  
    // fmt.Println(k)
  32.  
    // k
  33.  
    //}
  34.  
     
  35.  
    // 可以将post部分放到循环体里面
  36.  
    var m int
  37.  
    for m < 3 {
  38.  
    fmt.Println(m)
  39.  
    m
  40.  
    }
  41.  
     
  42.  
    }

6.3、for循环打印九九乘法表

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    /*
  6.  
    * 6.3节 for循环打印九九乘法表
  7.  
    * for循环打印九九乘法表
  8.  
    */
  9.  
    func main() {
  10.  
    //遍历,处理第几行
  11.  
    for y := 1; y <= 9; y {
  12.  
    //遍历,处理第几列。
  13.  
    for x := 1; x <= y; x {
  14.  
    fmt.Printf("%d * %d = %d\t", x, y, x*y)
  15.  
    }
  16.  
    fmt.Println()
  17.  
    }
  18.  
     
  19.  
    }

运行结果:

学新通

6.4、for range的循环用法

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    )
  6.  
     
  7.  
    /*
  8.  
    * 6.4节 for range的循环用法,主要用于 字符串、数组、切片、map、channel
  9.  
    */
  10.  
    func main() {
  11.  
    name := "My name is Peter Parker."
  12.  
    //for index, value := range name {
  13.  
    // fmt.Printf("%d, %c\r\n", index, value)
  14.  
    //}
  15.  
     
  16.  
    for _, value := range name {
  17.  
    fmt.Printf("%c\r\n", value)
  18.  
    }
  19.  
     
  20.  
    /*
  21.  
    * 字符串 字符串的索引(key) 字符串对应的索引的字符值的拷贝(index),因为是字符串值的拷贝,所以在字符串循环时是改不了原字符串的 如果不写key,那么index返回的是索引
  22.  
    * for index := range name {
  23.  
    * fmt.Printf("%c\r\n", index)
  24.  
    * }
  25.  
    *
  26.  
    * 数组 数组的索引 索引的对应值的拷贝 如果不写key,那么value返回的是索引
  27.  
    * 切片 切片的索引 索引的对应值的拷贝 如果不写key,那么value返回的是索引
  28.  
    * map 字符串的索引(key) value 返回的是 key 对应值的拷贝 如果不写key,那么value返回的是map的值
  29.  
    * channel value 返回的是 channel 接受的数据
  30.  
    */
  31.  
     
  32.  
    //下面这种方式循环字符串时会出现中文乱码
  33.  
    name2 := "哈哈哈"
  34.  
    for index := range name2 {
  35.  
    fmt.Printf("%c\r\n", name2[index])
  36.  
    }
  37.  
     
  38.  
    //下面这种方式循环字符串时,就不会出现中文乱码
  39.  
    name3 := "哈哈哈"
  40.  
    for _, value := range name3 {
  41.  
    fmt.Printf("%c\r\n", value)
  42.  
    }
  43.  
     
  44.  
    }

6.5、for循环的continue和break语句

  1.  
    package main
  2.  
     
  3.  
    import (
  4.  
    "fmt"
  5.  
    "time"
  6.  
    )
  7.  
     
  8.  
    func main() {
  9.  
    index := 0
  10.  
    for {
  11.  
    time.Sleep(1 * time.Second)
  12.  
    index
  13.  
     
  14.  
    //如果index等于5,就直接进入下一次循环,不执行下面的代码。
  15.  
    if index == 5 {
  16.  
    continue
  17.  
    }
  18.  
     
  19.  
    fmt.Println(index)
  20.  
    if index > 10 {
  21.  
    //退出循环
  22.  
    break
  23.  
    }
  24.  
    }
  25.  
    }

学新通

6.6、goto语句的基本用法

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    /*
  6.  
    * 6.6节 goto语句的基本用法
  7.  
    * goto语句可以让我们的代码跳到指定的代码块中运行,灵活性很强。但是开发中不建议使用。
  8.  
    *
  9.  
    * goto语句可以实现程序的跳转,goto语句使用场景最多的是程序的错误处理,也就是说当程序出现错误的时候统一跳转到相应的标签处,统一处理。
  10.  
    */
  11.  
    func main() {
  12.  
    for i := 0; i < 5; i {
  13.  
    for j := 0; j < 5; j {
  14.  
    if j == 2 {
  15.  
    goto over
  16.  
    }
  17.  
    fmt.Println(i, j)
  18.  
    }
  19.  
     
  20.  
    }
  21.  
     
  22.  
    over:
  23.  
    fmt.Println("over")
  24.  
     
  25.  
    }

运行结果:

学新通

6.7、switch语法

  1.  
    package main
  2.  
     
  3.  
    import "fmt"
  4.  
     
  5.  
    /*
  6.  
    * 6.7节 switch语句 比if语句执行性能更高一些,而且代码更整洁。
  7.  
    * switch var1 {
  8.  
    * case val1:
  9.  
    * ...
  10.  
    * case val2:
  11.  
    * ...
  12.  
    * case val3:
  13.  
    * ...
  14.  
    * default:
  15.  
    * ...
  16.  
    *
  17.  
    * var1变量可以是任意的变量类型
  18.  
    */
  19.  
    func main() {
  20.  
    //中文的星期几,输出对应的英文
  21.  
    day := "星期三"
  22.  
    switch day {
  23.  
    case "星期一":
  24.  
    fmt.Println("Monday")
  25.  
    case "星期二":
  26.  
    fmt.Println("Tuesday")
  27.  
    case "星期三":
  28.  
    fmt.Println("Wednesday")
  29.  
    case "星期四":
  30.  
    fmt.Println("Thursday")
  31.  
    case "星期五":
  32.  
    fmt.Println("Friday")
  33.  
    case "星期六":
  34.  
    fmt.Println("Saturday")
  35.  
    case "星期日":
  36.  
    fmt.Println("Sunday")
  37.  
    default:
  38.  
    fmt.Println("Unknown")
  39.  
    }
  40.  
     
  41.  
    //还有更灵活的switch用法
  42.  
    score := 95
  43.  
    switch {
  44.  
    case score < 60:
  45.  
    fmt.Println("E")
  46.  
    case score >= 60 && score < 70:
  47.  
    fmt.Println("D")
  48.  
    case score >= 70 && score < 80:
  49.  
    fmt.Println("C")
  50.  
    case score >= 80 && score < 90:
  51.  
    fmt.Println("B")
  52.  
    case score >= 90 && score < 100:
  53.  
    fmt.Println("A")
  54.  
    default:
  55.  
    fmt.Println("Unknown")
  56.  
    }
  57.  
     
  58.  
    //还有可以这样用switch
  59.  
    count := 90
  60.  
    switch count {
  61.  
    case 60, 70, 80:
  62.  
    fmt.Println("Ordinary")
  63.  
    case 90:
  64.  
    fmt.Println("Excellent")
  65.  
    default:
  66.  
    fmt.Println("Unknown")
  67.  
    }
  68.  
     
  69.  
    }

运行结果:

学新通

跳转链接:

下一篇:二、容器、go编程思想

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbhejf
系列文章
更多 icon
同类精品
更多 icon
继续加载