go语言学习笔记(一)

1. golang环境搭建

vscode安装地址:https://code.visualstudio.com

一句话:优美而丰富

2. golang变量常量命令规范

2.1 命令规范

在go语言中,任何标识符(包括变量、常量、函数、自定义类型等)都应该满足以下规律:

  • 由连续的字符或者数字组成
  • 以字符或者下划线开头
  • 不得与go关键字重复

这一点命令规则很多语言都相同,C语言也同样由此要求。

go的关键字包含以下几种类型

breakdefaultfuncinterfaceselect
casedefergomapstruct
chanelsegotopackageswitch
constfallthroughifrangetype
continueforimportreturnvar
| break   | default   | func   | interface | select |
| case    |defer      |  go    |  map      | struct |
| chan    |else       | goto   | package   | switch |
| const   |fallthrough|  if    | range     | type   |
| continue|for        | import | return    | var    |

2.2 变量声明

在go语言中使用后置类型声明方式,格式如: <变量名> <类型>

例如:

    x int //定义一个整型变量x

如此做法,据说不是为了标新立异,而是为了能让声明更加清晰

此外,在go中通常采用关键字var来声明变量。如果需要同时声明多个变量,则可以使用小括号()将声明的内容括起来。

    var x int       /*声明一个变量名为x的整型变量*/
    var b int = 1   /*声明一个整型变量b, 且初始值为1*/
    var b = 1       /*声明一个默认类型变量b, 且初始设置为1*/

    /*声明多个变量*/
    var (
        a,b int
        c float64
    )

在go中还支持简短声明方式,如:

    a := 1       /*声明一个整型变量a, 初值为1*/
    b := int(64) /*声明一个64位整型变量b, 初值为1*/

2.3 常量定义

常量是指定义之后不能改变的变量类型,他必须满足以下条件:

  • 定义时必须要进行初始化
  • 常量类型通常有三类:布尔、数字、字符串,其中数字类型包括(rune,integer,floating-point,complex)。它们都属于基本数据类型。
  • 不能使用简短声明方式定义,即不能使用:=

例子如下

    const a = 64

    const (
        b = 4
        c = 0.1
        d = "I'm learning Golang...."
    )

3. golang中基本数据类型

    /*布尔类型*/
    bool        :包括true,false
    /*无符号整型变量*/
    uint8       
    uint16
    uint32
    uint64
    /*有符号整型变量*/
    int8
    int16
    int32
    int64
    /*浮点类型*/
    float32
    float64
    /*复数类型*/
    complex64
    complex128
    
    byte            :字节,uint8 的别名
    rune            :int32 别名
    uint            :int32\int64 都可
    uintptr         :提供足够大空间的整型指针
    /*字符串类型*/
    string

我们将上述基本类型统一归为三类:

  • 布尔类型
  • 数字类型
  • 字符串类型

3.1 布尔类型

布尔类型常用于判断条件,取值范围只有true, flase。声明方式有如下几种:

    var a bool
    var a = true
    a := true
    
    const a = true

3.2 数字类型

数字类型主要分为有符号数和无符号数两类,此外还扩展到了复数域。有符号数和无符号数还有多种位数划分。

    var (
        a uint8 = 1
        b int8 = -1
        c int = 128
    )
  • 每一种数据类型都有固定的取值范围,如果超出了取值范围,则会出现溢出错误。
  • int, uint的位数长度取决于操作系统。如果操作系统是32位,则他们的长度时32位;如果操作系统是64位,则他们的位数是64位。

3.3 字符串类型

字符串程序实例如下

package main

import "fmt"

func main() {
	var a = "hello"
	var c = "\""
	var b = `hello`
	var d = `line1
	line2
	lin3`

	var str = "God bless you, China"

	fmt.Println(a)
	fmt.Println(b)
	fmt.Println(c)
	fmt.Println(d)
	fmt.Println(str)

	var e byte = str[0]
	fmt.Println(e)         /*以ASCII码方式打印str的第一个字符*/
	fmt.Println(string(e)) /*以字符方式打印str的第一个字符*/

	fmt.Println(len(str))         /*获取str字符串长度*/
	fmt.Println(len([]rune(str))) /*获取str字符串中字符的个数*/
}

4. golang中高级数据类型

golang中的高级数据类型包括如下几种:

  • 数组
  • 切片
  • Map
  • 自定义类型
  • 函数
  • 方法
  • 接口

下面分别对这几种数据类型进行介绍。

4.1 数组

  • 由相同类型的元素组成的序列
  • 数组长度是固定的,声明之后无法改变
  • 数组长度是数组的一部分,长度不同的数组,即使元素相同也是不同的类型

代码示例如下

    package main

    import "fmt"

    func main() {
        var arr1 [5]int /*定义一个有5个元素的整型数组*/
        for i := 0; i < 5; i++ {
            arr1[i] = i
        }

        fmt.Println(arr1)
        /*
        *result: [0 1 2 3 4]
        **/

        /*
        *	arr1 = [3]int{1, 2, 3}
        *这种赋值方式是错误的,编译运行时提示以下信息:
        *	Cannot use [3]int literal (type [3]int) as type [5]int in assignment
        **/

        arr1 = [5]int{12, 13, 14, 14, 15} /*元素类型和数组长度相同,可以直接赋值*/
        fmt.Println(arr1)

        /*简写模式,在定义的同时进行赋值*/
        arr2 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 0}
        fmt.Println(arr2)

        arr3 := [5]int{0, 1, 2, 3, 4}
        fmt.Println(arr3)

        arr4 := arr3
        //fmt.Println(arr1 == arr2) /*两个不同类型的数组不能比较大小*/
        fmt.Println(arr1 == arr3) /*两个相同类型的数组可以比较大小*/
        /*arr1 竟然与 arr3不相等,why???*/
        fmt.Println(arr3 == arr4) /*arr3 和 arr4相等*/

        /*数组长度可以不指定,由编译器来完成*/
        var arr5 = [...]int{1, 2, 3, 4, 5}
        fmt.Println(arr5, len(arr5))

        /*定义前四个元素默认0,最后一个为100*/
        var arr6 = [...]int{4: -1}
        fmt.Println(arr6)

        /*定义一个二维数组*/
        var twoDir [5][10]int

        for i := 0; i < 5; i++ {
            for j := 0; j < 10; j++ {
                twoDir[i][j] = i + j
            }
        }
        fmt.Println("TwoDir Array: ", twoDir)

    }

程序运行结果如下:

    [Running] go run "f:\golang\Go_basic\Day1\array\array.go"
    [0 1 2 3 4]
    [12 13 14 14 15]
    [1 2 3 4 5 6 7 8 9 0]
    [0 1 2 3 4]
    false
    true
    [1 2 3 4 5] 5
    [0 0 0 0 -1]
    TwoDir Array:  [[0 1 2 3 4 5 6 7 8 9] [1 2 3 4 5 6 7 8 9 10] [2 3 4 5 6 7 8 9 10 11] [3 4 5 6 7 8 9 10 11 12] [4 5 6 7 8 9 10 11 12 13]]

    [Done] exited with code=0 in 0.576 seconds

4.2 切片

切片的组成要素:

  • 指针: 指向底层数组
  • 长度: 切片中元素的长度,其值不能大于容量
  • 容量: 指针所指向的底层数组的总容量

常见的初始化方式

  • 使用make初始化

    slice := make([]int, 5) /*初始化长度和容量都是5的切片*/
    slice := make([]int, 5, 10) /*初始化长度为5,容量为10的切片*/
    
  • 使用简短定义

    slice := []int{1,2,3,4,5} 
    
  • 使用数组初始化切片

    arr := [5]int{1,2,3,4,5}
    slice := arr[0:3]   /*左闭右开区间,最终结果为[1,2,3]*/
    
  • 使用切片初始化切片

    sliceA := []int{1,2,3,4,5}
    sliceB := sliceA[0:3]   /*左闭右开区间,最终结果为[1,2,3]*/
    

切片的长度和容量

package main

import "fmt"

func main() {
    slice := []int{1, 2, 3, 4, 5}
    fmt.Println("Slice 内容:", slice)
    fmt.Println("Slice 长度:", len(slice))
    fmt.Println("Slice 容量:", cap(slice))

    /*改变切片大小*/
    slice = append(slice, 6) /*在切片中添加一个元素6, 但是切片总容量为5,因此底层会自动扩容*/
    fmt.Println("Slice 内容:", slice)
    fmt.Println("扩容后 Slice 长度:", len(slice))
    fmt.Println("扩容后 Slice 容量:", cap(slice)) /*容量不够时,自动扩容,大小变为原来的两倍*/

}

执行结果如下:

[Running] go run "f:\golang\Go_basic\Day1\slice\slice.go"
Slice 内容: [1 2 3 4 5]
Slice 长度: 5
Slice 容量: 5
Slice 内容: [1 2 3 4 5 6]
扩容后 Slice 长度: 6
扩容后 Slice 容量: 10

[Done] exited with code=0 in 0.577 seconds

特别注意的是: 多个切片共享一个底层数组时,当其中一个切片修改数组的值,其他切片也会发生相应的变化

举一个例子:

package main

import "fmt"

func main() {
	slice := []int{1, 2, 3, 4, 5}
	newSlice := slice[0:3]

	fmt.Println("切片内容修改前, 切片内容如下:")
	fmt.Println("slice:", slice)
	fmt.Println("newSlice:", newSlice)
	fmt.Println()

	/*修改其中一个切片的内容*/
	newSlice[1] = 10

	fmt.Println("修改后的切片:")
	fmt.Println("slice:", slice)
	fmt.Println("newSlice:", newSlice)
}

执行结果如下:

[Running] go run "f:\golang\Go_basic\Day1\slice\shareAarray.go"
切片内容修改前, 切片内容如下:
slice: [1 2 3 4 5]
newSlice: [1 2 3]

修改后的切片:
slice: [1 10 3 4 5]
newSlice: [1 10 3]

[Done] exited with code=0 in 0.593 seconds

可以使用copy方法来避免上述共享数组问题的发生

package main

import "fmt"

func main() {
	slice := []int{1, 2, 3, 4, 5}
	cpySlice := make([]int, len(slice))
	copy(cpySlice, slice)

	fmt.Println("切片内容修改前, 切片内容如下:")
	fmt.Println("slice:", slice)
	fmt.Println("cpySlice:", cpySlice)
	fmt.Println()

	cpySlice[0] = 10
	fmt.Println("使用copy方法拷贝的切片:")
	fmt.Println("slice:", slice)
	fmt.Println("cpySlice:", cpySlice)
}

程序执行结果如下:

[Running] go run "f:\golang\Go_basic\Day1\slice\cpySlice.go"
切片内容修改前, 切片内容如下:
slice: [1 2 3 4 5]
cpySlice: [1 2 3 4 5]

使用copy方法拷贝的切片:
slice: [1 2 3 4 5]
cpySlice: [10 2 3 4 5]

[Done] exited with code=0 in 0.586 seconds

4.3 Map

在GO语言中,Map是无需的键值对,它是哈希表的一种实现方式。类似于python中的字典

4.3.1 语法

使用关键字map在声明 Map类型变量。

map[keyType]ValueType

需要注意的是:

  • 必须指定key、value的类型,且插入的类型必须匹配
  • key具有唯一性,插入记录的key值不能重复
  • KeyType可以是基础数据类型(bool型,数字类型、字符串类型),但是不能是数组、切片或者map。它的取值必须能够使用==进行比较
  • ValueType可以是任意数据类型
  • 无序性
  • 线程不安全,一个goroutine在对map进行写的时候,另外的gorouting不能进行读和写操作。
4.3.2 声明和初始化
  • 使用var进行声明
    var cMap map[string]int   //只定义,不初始化,此时cMap为Nil
    fmt.Println(cMap==nil)

	/*此时cMap为nil, 因此下面均添加失败*/
	cMap["北京"] = 1
	cMap["石家庄"] = 2
	cMap["邯郸"] = 3
	fmt.Println(cMap)

结果如下:

    [Running] go run "f:\golang\Go_basic\Day2\map.go"
    true
    panic: assignment to entry in nil map
  • 使用make进行声明
    在使用make初始化map时,可以指定初始容量,从而减少内存分配次数。
	cMap := make(map[string]int)
	cMap["北京"] = 1
	cMap["石家庄"] = 2
	cMap["邯郸"] = 3
	fmt.Println(cMap)

	/*创建时指定容量*/
	cMap2 := make(map[string]int, 100)
	fmt.Println(cMap2, len(cMap2))

结果如下:

    map[北京:1 石家庄:2 邯郸:3]
    map[] 0
  • 简短声明方式
    cMap := map[string]int{"北京":1}
4.3.3 Map 基本操作
package main

import "fmt"

func main() {
	cMap := map[string]string{}

	cMap["天安门"] = "北京"
	cMap["东方明珠"] = "上海"
	cMap["小蛮腰"] = "广州"
	cMap["秦始皇兵马俑"] = "西安"
	cMap["大渡桥横铁索寒"] = "云贵川"
	fmt.Println(cMap)

	city := cMap["小蛮腰"]
	fmt.Println(city)

	city2 := cMap["八大胡同"]
	fmt.Println("------", city2) /*读不存在的key时,结果为空*/

	city3, ok := cMap["八大胡同"] /*检查某一个key是否存在*/
	if ok {
		fmt.Println(city3)
	} else {
		fmt.Println("八大胡同不知道在哪里!!!")
	}

	delete(cMap, "大渡桥横铁索寒")
	fmt.Println(cMap)
	fmt.Println("MAP结构是无序滴滴滴滴滴!!!")
}

4.3.4 Map 的无序性
package main

import "fmt"

func main() {
	cMap := map[string]string{"东方明珠": "上海", "天安门": "北京", "小蛮腰": "广州", "秦始皇兵马俑": "西安"}

	for famous, city := range cMap {
		fmt.Printf("%s:%s", famous, city)
		fmt.Println()
	}
}

运行结果如下:

秦始皇兵马俑:西安
东方明珠:上海
天安门:北京
小蛮腰:广州
4.3.5 Map 线程不安全

有关线程不安全问题,后续再做补充

4.3.6 Map 嵌套
package main

import (
	"fmt"
)

func main() {
	provinces := make(map[string]map[string]string)

	provinces["李德胜"] = map[string]string{
		"踏遍青山人未老,这边风景独好":       "井冈山时期",
		"更喜眠山千里雪,三军过后尽开颜":      "长征时期",
		"秦皇汉武、唐宗宋祖,成吉思汗":       "陕甘宁边区",
		"百万雄师过大江,不可沽名学霸王":      "解放战争时期",
		"当年忠贞为国愁,何曾怕断头,如今江山红遍": "晚年时期",
	}
	fmt.Println(provinces["李德胜"])

}

运行结果如下:

map[当年忠贞为国愁,何曾怕断头,如今江山红遍:晚年时期 更喜眠山千里雪,三军过后尽开颜:长征时期 百万雄师过大江,不可沽名学霸王:解放战争时期 秦皇汉武、唐宗宋祖,成吉思汗:陕甘宁边区 踏遍青山人未老,这边风景独好:井冈山时期]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叨陪鲤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值