经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
Go语言开发笔记
来源:cnblogs  作者:听风走了八千里  时间:2021/4/19 8:58:53  对本文有异议

GO开发

  • Golang被誉为21世纪的C语言

    • 2012.3 - 2020.2 1.0 - 1.14版本
  • 为什么选择GO

    • 继承python的简洁 & C语言的性能于一身
  • 环境搭建

  • 执行golang代码。

    • go run **.go
  • 或者go文件中 go build 会生成一个文件 在执行可执行文件

    • 再或者 go install。会将可执行文件放到bin目录
  • 创建目录

  1. 目录结构如下:
  2. xxx
  3. - bin
  4. - pkg
  5. - src //用于存放项目代码的目录
  • 环境变量
  1. GOROOT, GO编译器安装目录
  2. GOPATH, 用于存放项目代码, 编译后的可执行文件, 编译后的包文件(go 1.11版本后 ==> go.mod).
  3. GOBIN, 编译后的可执行的文件存放的目录
  • 开发工具

    • goland ide
    • vscode 编辑器
  • 配置

    • 字体
    • 参数提示
  • 项目开发

    • 新项目

    • 打开已经存在的项目

      注意: 项目放在$GOPATH/src目录。

GO语法的使用

  • Go包管理初识 : 知道项目中文件和文件 文件和文件夹之间的关系

  • 输出 , 写代码 在go编译器运行时会在屏幕显示内容

  • Go的数据类型

    • 整型
    • 字符串
    • 布尔类型
  • 变量 & 常量。当作是昵称 别名

  • 输入

  • 条件语句 if else elif

1. GO包管理

  • 一个文件夹可以称为一个包
  • 在文件夹(包)中可以创建多个文件
  • 在同一个包下的每个文件中必须指定包名称 且必须相同

重点:

  1. - main包。如果是main 必须写一个main函数 此函数就是项目的入口(main 编译生成可生成可以执行进制文件
  2. - main

2. 输出

在终端将想要展示的数据显示出来, 例如欢迎登录, 请输入用户名等

  • 内置函数
    • print
    • println
  • fmt包(推荐使用)
    • fmt.print
    • fmt.println

扩展: 进程中有 stdin/stdout/stderr

  1. fmt格式化输出
  2. fmt.printf()
  3. 占位符
  4. %s 字符串
  5. %d 整型
  6. %f 小数
  7. %.2f 保留两位小数

注释: 单行注释 // 多行注释 /**/

3. 数据类型

  • 整型
  • 字符串
  • 布尔型 false true

4. 变量var

就是给各种类型的数据起别名 为了方便引用。还可以暂时存储数据

  • 变量名必须只包含:字母、数字、下划线

  • 变量不能以数字开头

  • 不能使用golang中的内置变量 关键字

5. 输入

fmt.scanf()

6. 变量简写

  1. var name string = "ha"
  2. var name = "ha"
  3. name := "ha"
  4. 因式分解
  5. var(
  6. name = "zj"
  7. age = 18
  8. hobby = "sing"
  9. )
go编译器会认为如果声明或者声明且赋值的变量没有进行引用, 就要被删除掉

7. 变量作用域 (namespace名称空间)

如果我们定义了大括号, 那么在大括号中定义的变量

  • 不能被上级使用
  • 可以在同级中引用使用
  • 子级可以引用上级的变量
  • 函数内的方法都是局部变量
  • 定义全局变量不能使用 := 简写

全局变量和局部变量(都可以进行因式分解):

  • 全局变量: 在函数外的定义的非简写变量称为全局变量
  • 局部变量: 在{}内定义的变量为局部变量

8. 变量的赋值及内存相关

  1. name := "zj"
  2. fmt.println(name, &name) //打印变量内容及内存地址

9. 常量const

不可修改的变量

  1. // 常量 - 不可修改的变量
  2. const age = 12
  3. //age = 13
  4. fmt.Println(age)
  5. const (
  6. va = 123
  7. vb = "111"
  8. )

10. iota

可有可无的东西 可以理解为计数器

  1. // iota 计数累加器 从0开始
  2. const (
  3. v1 = iota
  4. v2
  5. v3
  6. v4
  7. )
  8. fmt.Println(v1, v2, v3, v4)

11. 输入

  1. // 让用户输入数据, 完成数据交互
  2. fmt.scanf
  3. fmt.scan
  4. fmt.scanln
  5. Scan
  6. var name string
  7. var age int
  8. fmt.Println("请输入用户名,姓名:")
  9. _, error := fmt.Scan(&name, &age) // _是count输入的总值 , error 是报错nil报错是输入争取 如果不是就是报错的
  10. //fmt.Println(name, age)
  11. //fmt.Println(error)
  12. if error == nil {
  13. fmt.Println(name, age)
  14. } else {
  15. fmt.Println("输入值错误", error)
  16. }
  • 常用的为scanln
    • 但是scanln的问题在于如果输入的变量存在空格 默认取空格之前的问题 所以我们要使用os.stdin标准输入
  1. // os.stdin标准输入
  2. fmt.Println("请随便输入点东西:")
  3. reader := bufio.NewReader(os.Stdin)
  4. // line 从stdin中读取一行的数据 数据类型为byte 可以转换为字符串
  5. // reader默认一次只能读取4096个字节 如果读取的字节数小于4096 isprefix=false
  6. line, _, _ := reader.ReadLine()
  7. // 通过string可以将byte类型的数据换换成字符串 类似于python的decode
  8. fmt.Println(string(line))

12. 条件语句

  • 最基本的条件语句

    1. if 条件{
    2. 条件成立执行语句
    3. } else {
    4. 条件不成立执行语句
    5. }
    6. // 例子
    7. var (
    8. username string
    9. passwd string
    10. )
    11. fmt.Print("username:")
    12. fmt.Scanln(&username)
    13. fmt.Print("password:")
    14. fmt.Scanln(&passwd)
    15. if username == "zj" && passwd == "123456"{
    16. fmt.Printf("用户名%s登录成功", username)
    17. }else {
    18. fmt.Println("用户名或密码错误")
    19. }
  • 多条件判断

  1. if {
  2. } else if {
  3. } else {
  4. }
  • 嵌套
  1. if {
  2. if {
  3. } else {
  4. }
  5. } else {
  6. }
  • Switch case 语句 类似于shell中的switch case 语句

  • for循环语句

  • goto 语法 , 不建议使用

  • 字符串的格式化 ,

  • 运算符的优先级

1.switch case 语句

  1. package main
  2. import "fmt"
  3. func main() {
  4. var number int
  5. _, err := fmt.Scanln(&number)
  6. if err == nil {
  7. switch number {
  8. case 1 :
  9. fmt.Println("1111")
  10. case 2:
  11. fmt.Println("2222")
  12. case 3:
  13. fmt.Println("3333")
  14. default:
  15. fmt.Println("无法识别你输入的内容")
  16. }
  17. }
  18. }
  19. switch 数据类型必须一致

2.for 循环

  1. //1.死循环 for 或者 for true
  2. //fmt.Println("开始")
  3. //for true {
  4. // fmt.Println("123")
  5. // time.Sleep(time.Second * 2) // 一秒为单位 time.second
  6. // break
  7. //}
  8. //fmt.Println("end")
  9. number := 1
  10. for number < 5 {
  11. fmt.Println(number)
  12. number += 1
  13. }
  14. fmt.Println("end")
  1. // for 循环条件判断
  2. for i:=1; i<10{
  3. }
  4. // 循环10次 进行++
  5. for i:=1; i<10; i++{
  6. fmt.Println(i)
  7. }
  8. count := 0
  9. for i:=1; i<100; i++{
  10. fmt.Println(i)
  11. count += i
  12. }
  13. print(count)
  • continue
  1. // 退出本次循环
  2. for i := 1; i <= 10; i++ {
  3. if i == 7 {
  4. continue
  5. }
  6. fmt.Println(i)
  7. }
  • break
  1. //跳出整个循环
  2. for i := 1; i < 5; i++ {
  3. if i == 4 {
  4. fmt.Println("bye")
  5. break
  6. }
  7. fmt.Println(i)
  8. }
*对 for 进行打标签, 然后可以通过break和continue就可以时间多层循环的跳出和中止
  1. test1:
  2. for i := 1; i < 5; i++ {
  3. for j := 1; j < 3; j++ {
  4. if j == 2 {
  5. //continue test1
  6. break test1
  7. }
  8. fmt.Println(i, j)
  9. }
  10. }
goto 语句
  1. //跳跃到指定的行向下执行代码
  2. package main
  3. import "fmt"
  4. func main() {
  5. shibai:
  6. var name string
  7. fmt.Print("请输入用户名:")
  8. _, err := fmt.Scanln(&name)
  9. fmt.Println(name)
  10. if len(name) < 1 {
  11. goto shibai
  12. }
  13. if err == nil {
  14. if name == "zj" {
  15. fmt.Println("牛皮")
  16. } else {
  17. fmt.Println("弟弟")
  18. goto shibai
  19. }
  20. }
  21. }
  • 字符串格式化
  1. // 格式化字符串
  2. package main
  3. import "fmt"
  4. func main() {
  5. var name string
  6. var age int
  7. var score float64
  8. fmt.Println("please input name")
  9. fmt.Scanln(&name)
  10. fmt.Println("please input name")
  11. fmt.Scanln(&age)
  12. fmt.Println("please input name")
  13. fmt.Scanln(&score)
  14. result := fmt.Sprintf("name is %s, age is %d, score is %.2f", name, age, score)
  15. fmt.Println(result)
  16. }

必备基础知识

  1. - 进制
  2. - 单位
  3. - 字符编码

golang 数据类型

  1. 1.整型
  2. var v1 int8 = 10
  3. var v2 int16 = 20
  4. v3 := int16(v1) + v2
  5. 注意:
  6. - 低位转高位是没有问题
  7. - 但是高位转成低位 是很有可能出问题的
  8. 整型转换成字符串类型:Itoa
  9. v1 := 19
  10. result := strconv.Itoa(v1)
  11. fmt.Println(result, reflect.TypeOf(result))
  12. 2.字符串转换成整形Atoi
  13. v2 := "19"
  14. res, err := strconv.Atoi(v2)
  15. if err == nil {
  16. fmt.Println(res)
  17. } else {
  18. fmt.Println("转换失败请检查原数据")
  19. }
  • 进制转换

go:

  1. - 10进制是以整型的方式存在
  2. - 其他进制 都是以字符串形式存在的
  • 整型 : 10进制数 转换成其他进制 (FormatInt)
  1. // 使用方法进行进制转换
  2. v1 := 9
  3. // 通过strconv.FormatInt方法进行进制数转换 int类型必须是int64 所以必须进行声明 第二个参数代表的是转换成几进制
  4. // 10进制转换成其他进制
  5. v2 := strconv.FormatInt(int64(v1), 8)
  6. fmt.Println(v2, reflect.TypeOf(v2))
  • 其他进制转换成10进制parseint
  1. // 其他进制转换成整形=> 10进制
  2. // parseint(其他进制数据, 原本的进制类型, 要转换成的进制类型 )
  3. // 只要转换成功 转化出来的数据类型就是int64类型
  4. data := "10001000"
  5. res, err := strconv.ParseInt(data, 2, 10)
  6. //fmt.Println(err)
  7. if err == nil {
  8. fmt.Println(res, reflect.TypeOf(res))
  9. }
  1. - 自己出的小练习
  2. 2进制的10001000转换成8进制数
  3. 思路:因为2进制和8进制代码层面不能相互转换 所以我们先将2进制转换成10进制 在将10进制装换成8进行
  4. data := "10001000"
  5. res, err := strconv.ParseInt(data, 2, 10)
  6. //fmt.Println(err)
  7. if err == nil {
  8. fmt.Println(res, reflect.TypeOf(res))
  9. test := strconv.FormatInt(int64(res), 8)
  10. fmt.Println(test)
  11. }

![image-20201228175014934](/Users/mac/Library/Application Support/typora-user-images/image-20201228175014934.png)

常见的数据运算

  1. math 函数
  2. math abs
  3. math.Pow(2 ,5) // 2的五次方
  4. math.max(1, 2) // 两个值相比较 取大值
  5. math.min(1, 2) // 两个值相比较 取小值

指针/nil/声明变量/ new

  • 声明变量
  1. ### 指针/nil/声明变量/new
  2. * 声明变量
  3. var v1 int
  4. v2 := 999
  5. * 指针 (作用就是节省内存)
  6. var v3 *int
  7. v4 := new(int)
  8. nil
  9. v3 -> ^
  10. 0x0
  11. 0
  12. v4 -> ^
  13. 0x0
  14. * new 关键字
  15. * new用于创建内存并进行内部数据的初始化。并返回一个指针类型
  16. * nil
  17. * nilgo语言中的空值
  18. * var v6 *int
  19. * var v7 * int32
  20. * 超大整型
  21. var v1 big.int
  22. var v2 *big.int
  23. var new(big.int)
  24. v1.setint64(1000)
  25. var v1 big.Int
  26. v1.SetString("123456767898786898092355262738453726315", 2)
  27. fmt.Println(v1.String(), reflect.TypeOf(v1.String()))

go Array数组

  1. 数组是固定长度的特定类型元素组成的序列
  2. 一个数字由零个或多个元素组成
  3. 数组的长度是固定的,因此Go更常用slice(切片, 动态的增长或收缩序列)
  4. 数组是值类型, 用索引下标访问每一个元素, 范围是0 - len-1 访问超出数组长度范围 panic异常
  5. // Go Array 数组中没有复制的数组 会有相应的默认值
  6. // 声明数组 , 并且个数组中的元素赋值
  7. var intArr [5]int
  8. fmt.Println(intArr)
  9. intArr[0] = 12
  10. intArr[1] = 34
  11. fmt.Println(intArr)
  12. // 声明数组 并且直接赋值
  13. var namestr [5]string = [5]string{"1", "2"}
  14. fmt.Println(namestr)
  15. var namestr2 = [5]string{"1"}
  16. fmt.Println(namestr2)
  17. // 取数组最后一个元素 顺便展示指定索引赋值
  18. var namestr2 = [5]string{"1", 4: "124124"}
  19. fmt.Println(namestr2, namestr2[len(namestr2)-1])
  20. // 自适应数组大小[...]
  21. var namestr3 = [...]string{"zj", "zjj", "zjjj"}
  22. fmt.Println(namestr3)
  23. // 数据结构题类型数组
  24. var namestr5 = [...]struct{
  25. name string
  26. age int
  27. }{
  28. {"zj", 18},
  29. {"ccn", 18},
  30. }
  31. fmt.Println(namestr5)
  32. // 数组循环
  33. for i:=0; i < len(namestr3);i++{
  34. fmt.Println("for " + namestr3[i])
  35. }
  36. for index, value := range namestr3{
  37. fmt.Println(index, value)
  38. }
  39. ** 数组注意事项:
  40. 数组是多个相同数据的组合, 且长度固定, 无法扩容 [5]int
  41. 数组使用步骤:
  42. 1.声明数组
  43. 2.给数组元素赋值
  44. 3.使用数组
  45. 4.数组索引从0开始 不能index of range
  46. 5.Go数组是值类型, 变量传递默认是值传递, 因此会进行值拷贝
  47. 6.修改原本的数组, 可以使用引用传递(指针)
  • 字符串的本质

计算机中所有的操作和数据最终都是二进制 : 010101010...

  1. package main
  2. import (
  3. "fmt"
  4. "strconv"
  5. "unicode/utf8"
  6. )
  7. func main() {
  8. var name string = "张无忌"
  9. fmt.Println(name[1], strconv.FormatInt(int64(name[1]), 2))
  10. fmt.Println(len(name))
  11. age := 123
  12. res := strconv.Itoa(age)
  13. // len字节长度
  14. fmt.Println(len(res + ""))
  15. // 字符串转换为一个 "字节集合"
  16. byteset := []byte(name)
  17. fmt.Println(byteset) // [229 188 160 230 151 160 229 191 140]
  18. // 字节集合转换成字符串string
  19. fmt.Println(string(byteset))
  20. // rune集合
  21. testName := "11223ddd"
  22. runeset := []rune(testName)
  23. fmt.Println(runeset,)
  24. fmt.Println(string(runeset[:5]))
  25. // 字符串长度获取
  26. // len方法获取的是字节的长度
  27. test2Name := "122333zghang张鉴"
  28. runeset2 := []rune(test2Name)
  29. fmt.Println(len(runeset2))
  30. // or 使用utf8的方法获取长度
  31. test2Namelength := utf8.RuneCountInString(test2Name)
  32. fmt.Println(test2Namelength)
  33. }
  1. 字符串常见的使用方法
  2. // 获取长度
  3. var name string = "张无极"
  4. fmt.Println(name)
  5. fmt.Println(utf8.RuneCountInString(name))
  6. // 是否以xx开头
  7. name2 := "张无忌"
  8. reslut := strings.HasPrefix(name2, "张")
  9. fmt.Println(reslut)
  10. // 是否以xx结尾
  11. result := strings.HasSuffix(name2, "无忌")
  12. fmt.Println(result)
  13. // 是否包含 类似 python in
  14. result2 := strings.Contains(name2, "无")
  15. fmt.Println(result2)
  16. fmt.Println("===============")
  17. // 全变大写 类似 python upper
  18. stringTest1 := "fff afa fUUUU 涨"
  19. fmt.Println(strings.ToUpper(stringTest1))
  20. // 全变小写
  21. fmt.Println(strings.ToLower(stringTest1))
  22. // 替换所有是-1 是左到右第一个 是1
  23. fmt.Println(strings.Replace(stringTest1, "f", "", 1))
  24. fmt.Println(strings.Replace(stringTest1, "f", "", 2))
  25. fmt.Println(strings.Replace(stringTest1, "f", "", -1))
  26. // 分割 split
  27. splitTest := strings.Split(stringTest1, " ")
  28. fmt.Println(splitTest[len(splitTest)-1])
  29. // 拼接
  30. mes := "你好" + "我好" // 不建议使用
  31. fmt.Println(utf8.RuneCountInString(mes))
  32. // 高效率的字符串拼接方法 非常推荐使用
  33. var builder strings.Builder
  34. builder.WriteString("我爱你")
  35. builder.WriteString("中国")
  36. value := builder.String()
  37. fmt.Println(value, utf8.RuneCountInString(value))
  38. // 字符串转换成int
  39. var num int = 12
  40. fmt.Println(strconv.Itoa(num), reflect.TypeOf(strconv.Itoa(num)))
  41. textStr := "2141414141414"
  42. fmt.Println(strconv.Atoi(textStr))
  43. textStr2 := "0101010010"
  44. fmt.Println(strconv.ParseInt(textStr2, 2 ,10))
  45. fmt.Println(strconv.FormatInt(int64(num), 16))
  46. // 字符串 转换
  47. v1 := string(100)
  48. fmt.Println(v1)
  49. v2, size := utf8.DecodeRuneInString("d")
  50. fmt.Println(v2, size)
  • 索引切片和循环
  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. var name3 string = "你好你好"
  7. // 字节索引 切片
  8. vs1 := name3[0:3]
  9. fmt.Println(vs1)
  10. // 手动循环所有的字符 range
  11. for index, item := range name3 {
  12. if index == 6 || index == 3 {
  13. fmt.Println(index, string(item))
  14. }
  15. //fmt.Println(index, string(item))
  16. }
  17. // 转换成rune集合
  18. dataList := []rune(name3)
  19. fmt.Println(dataList, string(dataList))
  20. }
  • 数组

数组, 定长且元素类型一致的数据集合 (类型必须相同)

  1. // 声明加赋值自适应变量
  2. var numbers = [...]int{1, 2, 3}
  3. numbers[0] = 2
  4. fmt.Println(numbers)
  5. // 声明定长的数组 不足的用空格代替 len为5
  6. var numbers = [5]string{"test", "alex", "didi"}
  7. fmt.Println(numbers)
  8. // 指定索引赋值
  9. var namestr2 = [5]string{"1", 4: "124124"}
  10. fmt.Println(namestr2, namestr2[len(namestr2)-1])
  11. // 声明指针类型的数组 (指针类型, 不会开辟内存初始化数组中的值)
  12. var numbers *[]int

数组的特性(可变和拷贝)

  • 元素值是可以被修改的 但是数组一旦声明开辟了内存空间之后 数组的大小就不可以改变
  • 可以进行copy
  1. // 修改重新赋值
  2. var numbers = [...]int{1, 2, 3}
  3. numbers[0] = 2
  4. fmt.Println(numbers)
  • 长度切片[0:2]
  • 数组的嵌套
  1. 二维数组
  2. // 含义为 数组中有3个元素 每个元素是一个含有2个int元素的数组
  3. var nestData [3][2]int
  4. var nestData = [3][2]int{{1, 2}, {3, 4}}
  5. nestData[2] = [2]int{11, 22}
  6. nestData[2][1] = 6666
  7. fmt.Println(nestData)

最后三种数据结构概览

  • 切片
  • 字典
  • 指针

#切片

切片, 动态数组

切片是Go中重要的数据类型,每个切片对象内部都维护着 : 数组指针,切片长度,切片容量 三个数据

  • 创建切片
  1. // 每一个切片内部都存储了三个数据
  2. 数组指针*array 切片长度len 切片容量[:3]
  3. 再向切片中追加数据个数大于容量时, 内部会自动扩容且每次扩容都是当前容量的2 [:3] -> [:6]
  4. // 创建切片
  5. var nums []int
  6. // 创建切片 并赋值
  7. var nums = []int{1,2,3}
  8. // 创建切片
  9. // make只用于 切片、字典、channel
  10. // 推荐使用
  11. int 类型 默认长度为2 容量为3 cap()
  12. var users = make([]int,2,3)
  • 自动扩容
  1. v1 := make([]int,1,3)
  2. v2 := append(v1, 123)
  3. 注意: 扩容前和扩容后的切片内存地址 是不同的
  • 切片追加高级用法
  1. // 基本相当于python中的extend 扩展列表
  2. arrayList = append(arrayList, []int{100,200,300,400}...)
  3. fmt.Println(arrayList)
  • 切片删除
  1. // 切片数据实际上是没有删除操作的 我们可以使用append来拼接生成新的切片 代替删除
  2. newAarrayList := append(arrayList[:6])
  3. newAarrayList = append(newAarrayList, arrayList[7:]...)
  4. fmt.Println(newAarrayList)
  5. // or
  6. newAarrayList := append(arrayList[:6], arrayList[7:]...)
  7. fmt.Println(newAarrayList)
  • 切片插入
  1. newAarrayList := append(arrayList[:6])
  2. newAarratList = append(arrayList, 123356)
  3. newAarrayList = append(newAarrayList, arrayList[7:]...)
  4. fmt.Println(newAarrayList)

// 删除插入 效率低下 不使用。应该使用链表

  • 切片嵌套
  1. // 嵌套
  2. testList := [][]int{[]int{1, 2}, []int{66, 77, 99}}
  3. fmt.Println(testList)

目前为止 上面所学的数据类型中, 在修改切片的内部元素时, 会造成所有的赋值的变量同时修改 (不扩容的前提下)

字典类型(Map)

  • 任何编程语言中都会存在字典或者映射 , 同python中的字典 是以键值对的形式存在的数据集合
  1. {
  2. 'name' : 'zj',
  3. 'age': 15,
  4. }
  • 字典的特性map
    • 键不能重复
    • 键必须可hash(切片和map都不可hash所以不能做键)
    • 无序
  • map声明
  1. // 第一个string代表键的类型 第二个string代表值的类型
  2. userInfo := map[string]string{
  3. "name": "zj",
  4. "age": "18",
  5. }
  6. fmt.Println(userInfo["name"])
  7. //or
  8. // dataInfo := make(map[string]string, 10)
  9. dataInfo := make(map[string]string)
  10. dataInfo["name"] = "ccn"
  11. fmt.Println(dataInfo["name"])
  • map 长度和容量
  1. len(userInfo)
  2. dataInfo := make(map[string]string, 10)
  3. // 根据穿参10 计算出合适的容量
  4. // 一个map 中会包含很多筒,每个筒可以存放8个键值对
  • map 增删改查
  1. userInfo := map[string]string{
  2. "name": "zj",
  3. "age": "18",
  4. }
  5. // 增
  6. userInfo["test"] = "123ff"
  7. // 改
  8. userInfo["name"] = "ccc"
  9. // 删
  10. delete(userInfo, "name")
  11. // 查
  12. for key, value := range userInfo {
  13. fmt.Println(key, value)
  14. }
  15. fmt.Println(userInfo["name"])
  • map变量赋值

...

注意 : 无论是否存在扩容都指向同一个地址

  • map初始化详解
  1. info = make(map[int]int, 10)
  2. - 第一步 创建一个hmap的结构体对象
  3. - 第二部 生成一个hash因子 hash0 并赋值到hmap对象中(用于后续为key创建hash值)
  4. - 第三部 根据hint=10 并根据算法来创建 2B次方筒数,当前应该是1 所以就是创建两筒
  • map扩容原理
  1. 再向map中添加数据时, 当达到某一个条件, 则会引发字典扩容
  2. 扩容的条件:
  3. - map中数据总个数/筒个数 > 6.5时, 便会引发翻倍扩容

指针数据

什么是指针 一个指针变量指向了一个内存地址, 类似于变量和常量, 在使用指针前需要声明指针。指针声明格式如下

  1. // 指针类型生来就是用来节省内存的
  2. // 声明指针类型的数据只需要在声明数据类型前加* 即可声明为指针类型
  3. package main
  4. import "fmt"
  5. func main() {
  6. //var a int = 10
  7. //fmt.Printf("%x" , &a)
  8. var name string = "yunZhOngKeXin"
  9. // 查看内存地址为&符号
  10. fmt.Printf("name的内存地址: %v\n", &name)
  11. // 指针变量, 存的是内存地址
  12. // ptr 指针变量指向变量name的内存地址
  13. var ptr *string
  14. ptr = &name
  15. fmt.Printf("指针变量ptr的内存地址: %v \n", ptr)
  16. // 获取指针变量的值, 用*ptr
  17. // *ptr表示读取指针指向变量的值
  18. // *ptr 代表真正的数据
  19. fmt.Printf("指针变量ptr指向的值是: %v\n", *ptr)
  20. }
  • 指针

指针 , 是一种数据类型, 用于表示数据的内存地址。

  1. // 声明一个 字符串类型的变量 (默认初始化为空字符串)
  2. var v1 string
  3. // 声明一个字符串的指针类型
  4. var v2 *string
  1. // 声明一个字符串类型的变量并赋值
  2. var name string = "zj"
  3. // 声明一个字符串的指针类型的变量, 值为name对应的内存地址
  4. var pointer *string = &name
  • 指针存在的意义

相当于创建了一个**引用**, 以后可以根据这个引用来获取他里面的值 如果原本的数据发生变化引用也会发生变化,类似于软连接, 快捷方式这种

  1. v1 := "zh"
  2. v2 := &v1
  3. fmt.println(v1, v2, *v2)
  • 使用指针的场景
  1. // 下边的这种函数传参的方式testData字符串会重新copy一份
  2. func main {
  3. Test("zjjjj")
  4. }
  5. func Test(testData string) {
  6. testData = "hahah"
  7. fmt.println(testData)
  8. }
  9. //
  10. func main {
  11. Test("zzjj")
  12. }
  13. func Test(ptr *string){
  14. ptr = "fffff"
  15. fmt.println(ptr) // 修改指针的内容上边的内容也会被修改
  16. }
  • 指针的指针
  1. n1 := "zhjjj"
  2. n2 := &n1
  3. fmt.Println(n1, *n2)
  4. n3 := &n2
  5. fmt.Println(*n2, **n3)
  6. n4 := &n3
  7. fmt.Println(**n3, ***n4)
  • 指针的小高级操作
  1. // 指针的相加操作
  2. func Compl() {
  3. // 指针的相加操作
  4. // 定义一个int8数组
  5. dataList := [3]int8{11, 22, 33}
  6. // 取出第一个元素的内存地址
  7. var firstData *int8 = &dataList[0]
  8. // 将第一个铁元素的内存转化成pointer类型
  9. ptr := unsafe.Pointer(firstData)
  10. // pointer类型 +1
  11. targetAddress := uintptr(ptr) + 1
  12. // 相加之后重新转换成pointer类型
  13. newPtr := unsafe.Pointer(targetAddress)
  14. // 将pointer对象转换成int8 指针类型
  15. value := (*int8)(newPtr)
  16. // 使用指针类型获取数据
  17. fmt.Println(*value)
  18. }

结构体

什么是结构体?

| 结构体是一个复合类型, 用于表示一组数据

| 结构体由一系列属性组成, 每个数据都有自己的类型和 值

  1. // 定义
  2. type Person struct {
  3. name string
  4. age int
  5. email string
  6. }
  7. // 初始化
  8. var p1 = Person{"zh", 12, "153"}
  9. fmt.Println(p1, reflect.TypeOf(p1))
  10. 结构体基本格式
  11. type 结构体名称 struct{
  12. 字段 类型
  13. ...
  14. }
  • 结构体的定义
  1. type Person struct {
  2. name string
  3. age int
  4. hobby []string. // 可以是切片哦
  5. }

结构体

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main() {
  6. // 定义结构体
  7. type Person1 struct {
  8. name, sex string
  9. age int
  10. }
  11. var p1 = Person1{"ccn", "sunhat", 18}
  12. fmt.Println(p1)
  13. // 结构体嵌套
  14. type Person2 struct {
  15. score int
  16. p1 Person1
  17. }
  18. v2 := Person2{99, Person1{"zj", "nan", 18}}
  19. fmt.Println(v2)
  20. //结构体匿名
  21. type Person3 struct {
  22. city string
  23. hobby string
  24. Person1 // 匿名结构体
  25. }
  26. v3 := Person3{city: "CN", hobby: "students", Person1:Person1{"11","nan",18}}
  27. // 由于结构体是匿名的所以可以直接取p1中的name 否则必须v3.变量名.name
  28. fmt.Println(v3.city, v3.name)
  29. // 注意结构体赋值的时候 数据会全部都的拷贝一份 无论什么类型
  30. // 指针结构体 是不拷贝的 软连接
  31. // 切片和map 数据拷贝的的时候可能会发现感觉并没有拷贝 但其实不是的是因为切片和map没有扩容的时候使用的都是同一块地址导致的 数据的特型
  32. }
  33. // 如果我们想要在赋值时不拷贝数据 我们可以将数据变成指针类型数据即可
  34. *[2]string
  35. *map[string]int
  • 结构体指针
  1. type Person2 struct {
  2. lambda string
  3. *Person
  4. }
  5. p3 := Person2{lambda: "这是一个匿名函数", Person: &Person{"Miho", "naming ", 12}}
  6. p4 := p3
  7. p3.name = "testzhizhen"
  8. fmt.Println(p3, *p3.Person, p4, *p4.Person)
  • 结构体标签
  1. // 标签
  2. type Person2 struct {
  3. lambda string "声明匿名"
  4. *Person "我是Person"
  5. }
  6. p3 := Person2{lambda: "这是一个匿名函数", Person: &Person{"Miho", "naming ", 12}}
  7. p4 := p3
  8. p3.name = "testzhizhen"
  9. // 标签1
  10. testStruct := reflect.TypeOf(p3)
  11. fmt.Println(testStruct.Field(1).Tag)
  12. // 标签2
  13. field, _ := testStruct.FieldByName("lambda")
  14. fmt.Println(field.Tag)
  15. // 循环获取tag
  16. fieldNum := testStruct.NumField()
  17. for i := 0; i < fieldNum; i++ {
  18. fmt.Println(testStruct.Field(i).Tag)
  19. }

函数

可以把函数当作一个公用的代码块,用于实现某一个功能. 并且提高代码的重用性和可读性(函数传参数据会重新拷贝 相当于赋值)

  1. // 函数使用方法
  2. func 函数名(参数 类型) 返回值类型 {
  3. 函数执行体
  4. }
  1. package main
  2. import "fmt"
  3. // 定义了返回值的类型时必须有return , name为接收的参数 string 为返回值的类型
  4. func FirstFun(name string) string {
  5. if len(name) > 2 {
  6. //fmt.Println(name)
  7. return name
  8. }
  9. return "error"
  10. }
  11. func main() {
  12. res := FirstFun("zjccn")
  13. fmt.Println(res)
  14. }
  • 函数调用指针类型 (节省内存)
  1. package main
  2. import "fmt"
  3. func FirstFun(name *string) string {
  4. *name = "123"
  5. if len(*name) > 2 {
  6. //fmt.Println(name)
  7. return *name
  8. }
  9. return "error"
  10. }
  11. func main() {
  12. name := "ZjCcn"
  13. res := FirstFun(&name)
  14. fmt.Println(res, name)
  15. }
  • 函数也可以作为参数
  1. func main() {
  2. TestProxy(10, Add100)
  3. }
  4. func Add100(data int) (int, bool) {
  5. return data + 100, true
  6. }
  7. func TestProxy(data int, function func(int) (int, bool)) string {
  8. resData , flag := function(data)
  9. if flag{
  10. fmt.Println(resData, flag)
  11. }else {
  12. fmt.Println("error")
  13. }
  14. return "complete"
  15. }
  • 函数传递可变长参数 可以不确定传多少参数 (变长参数必须放在最后 类似 **kwargs 且只能存在一个)
  1. // 这个num的类型实际上是切片类型。可以任意扩容
  2. func Do(num ... int) int {
  3. sum := 0
  4. for _, value := range num{
  5. sum += value
  6. }
  7. return sum
  8. }
  9. func main() {
  10. numcount := Do(1,2,2,2,2,2,2,2,2,3,3,3,3,3,3)
  11. fmt.Println(numcount)
  12. }
  • 匿名函数
  1. func main() {
  2. // 匿名函数
  3. f1 := func(data int) int {
  4. return data
  5. }
  6. fmt.Println(f1(11))
  7. // 第二种匿名函数表达
  8. f2 := func(data int) string {
  9. return strconv.Itoa(data)
  10. }(110)
  11. fmt.Println(f2, reflect.TypeOf(f2))
  12. }
  • 函数不仅能做传递的参数 也可以作为返回值
  1. test1 := Test1()
  2. fmt.Println(test1(120))
  3. func Test1() func(t1 int) int {
  4. funtion := func(t1 int) int {
  5. return t1
  6. }
  7. return funtion
  8. }
  • 闭包函数(通过一个函数将我们需要的数据固定包裹在一个包里)
  1. // 闭包函数的作用就是将生成好的函数封装好值也传好, 之后调用的函数的值返回值 都是定义好的无需再次传参 可直接调用
  2. package main
  3. import (
  4. "fmt"
  5. )
  6. func main() {
  7. // 这个切片会存储着五个函数 分别打印 0 1 2 3 4
  8. var funtionList []func()
  9. for i := 0; i < 5; i++ {
  10. funtion := func(data int) func(){
  11. return func() {
  12. fmt.Println(data)
  13. }
  14. }(i)
  15. funtionList = append(funtionList, funtion)
  16. }
  17. // 运行封装好的包
  18. funtionList[0]()
  19. funtionList[2]()
  20. funtionList[3]()
  21. }
  • defer

用于在一个函数执行体完成之后自动触发的语句, 一般用于结束操作之后释放资源

  1. // go中称为延迟执行 可以随意定义位置
  2. // 相当于python中的 __del__ 释放资源
  3. // 类似于堆栈。先进后出
  4. package main
  5. import "fmt"
  6. func main() {
  7. TestDefer()
  8. }
  9. func TestDefer() {
  10. fmt.Println("这是一次测试")
  11. for i := 0; i < 10; i++ {
  12. defer fmt.Println(i)
  13. }
  14. }
  • 自执行函数 (类似于python init)
  1. // ~= python __init__
  2. // 自执行函数就是匿名函数
  3. func TestDefer() {
  4. result := func(age int) int{
  5. return age
  6. }(18)
  7. fmt.Println(result)
  8. }
  • 结构体做函数返回值
  1. type funstruct struct {
  2. name string
  3. age int
  4. }
  5. func StructTest(name string) (funstruct) {
  6. return funstruct{name: name, age: 18}
  7. }
  8. func main(){
  9. t1 := StructTest("testzh")
  10. fmt.PrintLn(t1)
  11. }

类型方法

项目开发中可以为type声明的类型编写一些方法, 从而实现对象.方法的操作

  1. // 可以定义执行自己的代码的方法
  2. package main
  3. import "fmt"
  4. // 声明类型
  5. type myInt int
  6. func main() {
  7. var testMy myInt = 110
  8. res := testMy.Dosomething(1,2)
  9. fmt.Println(res)
  10. }
  11. // 这个因为前边加上了(i *myInt) 所以他就不是函数了 而是自我定义的方法
  12. myInt可以是指针 也可以是正常的 为了节省内存我们是用指针
  13. func (i *myInt) Dosomething (data1 int , data2 int) int {
  14. return data1+data2+int(*i)
  15. }
  • 方法继承(类似于面向对象的继承)
  1. type Base struct {
  2. name string
  3. }
  4. type Son struct {
  5. Base // 匿名结构体
  6. age int
  7. test string
  8. }
  9. func (b1 *Base) Test1() int {
  10. return 123
  11. // return b1.name name: "张无忌"
  12. }
  13. func (s1 *Son) Test2() int {
  14. return 222
  15. }
  16. func main() {
  17. son := Son{Base:Base{name: "张无忌"}, age:13, test:"测试"}
  18. fmt.Println(son.Test1(), son.Test2())
  19. }
  20. >>>>>>>>>>>>>>>执行结果:
  21. 123 222
  • 结构体工厂
  1. 声明任何数据类型的时候。只要变量名称为小写 就是私有变量 大写就是公共变量。私有就是只有当前这个.go文件可以使用 公有都能使用
  • Go接口类型

GO语言中的接口是一种特殊的数据类型, 定义格式如下:

type name interface{

方法名称() 返回值

}

例如:

type Name interface {

f1()

f2() int

f3() (int, bool)

}

定义接口的特点是 不需要写任何逻辑代码 只需要将方法的名和返回值传参数 写好即可

  • 接口的作用

    在程序开发中接口一般有两大作用 : 代指类型 和 约束。

  • 空接口, 代指任意类型

  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. // 定义个空接口 空接口中可以传任意类型的值
  7. type base interface {
  8. }
  9. func main() {
  10. //dataList := make([]base, 0)
  11. // 上边这些接口定义可以简写为:
  12. dataList := make([]interface{}, 0)
  13. dataList = append(dataList, "test")
  14. dataList = append(dataList, 234)
  15. dataList = append(dataList, map[string]string{"key": "values"})
  16. fmt.Println(dataList[2], reflect.TypeOf(dataList))
  17. something("test")
  18. something("test")
  19. something("test")
  20. caseSth("haha")
  21. caseSth(123)
  22. caseSth([]string{"xii", "haha"})
  23. }
  24. // 像python一样 弱类型
  25. type person struct {
  26. name string
  27. }
  28. // 接收到数据类型都是接口类型 所以我们要使用.(类型或者类型结构体)转换成我们想要的格式
  29. func something(arg interface{}) {
  30. s,ok := arg.(string)
  31. if ok{
  32. fmt.Println(s, reflect.TypeOf(s))
  33. } else {
  34. fmt.Println("转换失败")
  35. }
  36. }
  37. func caseSth(arg interface{}) {
  38. switch data := arg.(type) {
  39. case string:
  40. fmt.Println(data)
  41. case int:
  42. fmt.Println(data)
  43. case []string:
  44. fmt.Println(data)
  45. default:
  46. fmt.Println("WOZHAOBUDAO")
  47. }
  48. }
  • 非空接口 规范约束
  1. type Ibase interface {
  2. test1 () int
  3. }
  4. type Person struct {
  5. name string
  6. age int
  7. }
  8. func (p *Person) test1() int {
  9. return 123
  10. }
  11. type User struct {
  12. username string
  13. password string
  14. }
  15. func (u *User) test1() int {
  16. return 567
  17. }
  18. func main() {
  19. p := Person{"zj", 18}
  20. //u := User{"1533333", "520cnn.."}
  21. p.test1()
  22. List := []Ibase{&Person{"COMPELTE",12}, &User{"完成", "LE"}}
  23. fmt.Println(List)
  24. for _ , value := range List{
  25. fmt.Println(value, reflect.TypeOf(value))
  26. }
  27. }
  • flag包

基于os.Args可以获取传入的所有参数

  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. )
  6. func main() {
  7. //fmt.Println(os.Args)
  8. /* >>>结果
  9. mac$ ./main 213 123414155 5125
  10. [./main 213 123414155 5125]
  11. */
  12. // h 是命令行要-h 默认是127.0.0.1 后边是备注
  13. host := flag.String("h", "127.0.0.1", "主机名")
  14. port := flag.Int("p", 1234, "端口号")
  15. //host := flag.String("h", "127.0.0.1", "主机名")
  16. flag.Parse() // 必须parse 才能将命令行传入的数据传进来
  17. fmt.Println(*host, *port)
  18. /* >>结果
  19. mac$ ./main -h 192.168.1.1 -p 1212
  20. 192.168.1.1 1212
  21. */
  22. }
  • Go regexp 正则表达式
  1. package main
  2. import (
  3. "fmt"
  4. "regexp"
  5. )
  6. func main() {
  7. // 根据字符串匹配返回 是否成功
  8. matchString, err := regexp.MatchString(".*?:(.*?)n.*?", "vaiuhguifg:xixizhangjiannihaonfaoghaiug")
  9. if err == nil {
  10. fmt.Println(matchString) // true
  11. }
  12. // 根据匹配值字符串查找
  13. reg1 := regexp.MustCompile(`\d{11}`)
  14. res1 := reg1.FindString("1242563837613") // 结果 12425638376
  15. fmt.Println(res1)
  16. res2 := reg1.FindAllString("1241245569005837163414115", -1) // 结果就是找所有的11个数字的集合[12412455690 05837163414]
  17. fmt.Println(res2)
  18. // 获取分组信息
  19. }

文件路径相关的内置包

  • 文件增删改查
  1. // 创建单级目录 付给权限
  2. os.Mkdir("testDir", 0755)
  3. // 创建多级目录
  4. os.MkdirAll("/test/123/code", 0755)
  5. // 删除文件或空文件夹, 文件夹下存在内容数据 就会报错
  6. os.Remove("testDir")
  7. // 删除文件或文件夹 (同时删除子目录中的数据)
  8. os.RemoveAll("test123")
  • 判断目录 或文件是否存在. os.IsNotExist(err)
  1. _, err := os.Stat("test1/test2/123.go")
  2. if err != nil {
  3. if os.IsNotExist(err) {
  4. fmt.Println("文件或文件夹不存在")
  5. }else{
  6. fmt.Println("存在")
  7. }
  8. }
  • 判断是否是文件夹IsDir()
  1. file, _ := os.Stat("test/123/")
  2. res := file.IsDir()
  • 获取绝对路径
  1. abs, err := filepath.Abs("test/123/")
  2. // 获得的结果就是绝对路径+test/123/
  3. // 获得绝对路径
  4. abs, err := filepath.Abs(".")
  5. if err == nil {
  6. // 获得上层路径
  7. fmt.Println(filepath.Dir(abs))
  8. }
  • 遍历目录下的文件
  1. package main
  2. import (
  3. "fmt"
  4. "io/ioutil"
  5. "path/filepath"
  6. )
  7. func main() {
  8. abs, _ := filepath.Abs(".")
  9. dir, err := ioutil.ReadDir(abs)
  10. fmt.Println(dir)
  11. if err == nil {
  12. for _, value :=range dir{
  13. if !value.IsDir(){
  14. fmt.Println(value.Name())
  15. }
  16. }
  17. }
  18. }
  • walk
  1. // walk会深层递归的查询数据
  2. filepath.Walk(abs, func(path string, info os.FileInfo, err error) error {
  3. fmt.Println(info.Name())
  4. fmt.Println(path)
  5. return nil
  6. })
  • 路径拼接 or 文件扩展名
  1. // 拼接
  2. filePath := path.Join("1","2","3","7.dmg")
  3. // 文件扩展名
  4. ext := path.Ext("/fres/faf/xxx.txt")
  5. 获取到的就是扩展名txt

原文链接:http://www.cnblogs.com/zjaiccn/p/14667125.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号