Golang就业方向

  • 区块链
  • 服务器端/游戏软件工程
  • 分布式/云计算开发

就业公司

  • 区块链 (分布式账本技术,去中心化,公开透明,参与数据库记录)
  • 盛大云 (云计算,云服务,CDN分发调度)
  • 京东 (消息推送系统,分布式文件系统)
  • 美团后台业务支撑 (排序,推荐,搜索)

核心开发团队

  • Ken Thompson - Unix,C
  • Rob Pike - Unix, Plan9, UTF-8
  • Robert Griesemer - Java Hotspot, Chrome V8引擎

Why Golang?

  • 主流语言无法适应多核多CPU
  • 不够简洁高效
  • C/C++ 编译慢,没有GC,内存泄漏

Golang = C + Python? (静态语言的安全和性能,

  • 继承C
  • 引入包的概念,package
  • 自动的垃圾回收机制
  • 天然的并发机制, goroutine轻量级线程,基于CSP
  • 管道通讯机制,goroutine间的通讯
  • 函数支持返回多值
  • 创新语法:切片,延时执行(回收资源)defer

注释 comments

  • 支持C语言的/* */的块注释
  • C++的行注释//

格式化

  • 缩进和空白 (vscode:shift+tab -> tab)
  • 不允许花括号换行
  • 不需要分号
  • Golang自带格式化方法: gofmt -w xxx.go

Go语言设计者哲学,一个问题只有一个最好的解决方案,没有的话只是还没有找到。

Golang的变量

  • 变量表示内存中的一个存储空间
  • 变量有自己的值和类型
    • 声明后不赋值,会使用默认值 var i int (i = 0)
    • 类型推导方式 var i = 10.10
    • 省略var,使用:=赋值,但不能是已经声明过的变量,否则报错。
  • 支持多变量声明 var i,j int
  • 在一定的区域内,同一类型下,只可以发生改变
  • 变量在同一个作用域下不能重名
  • 变量 = 变量名 + 值 + 数据类型

Golang的数据类型

  • 基本类型
    • 数值型
      • 整数类型(int,int8,int16,int32,int64)
      • 浮点数类型(float32,float64)
    • 字符型 (没有专门的字符型,使用byte来存储单个字符)
    • 布尔类型(bool)
    • 字符串类型(string)
    • 复数类型
  • 复杂类型
    • 指针
    • 数组
    • 结构体
    • 管道
    • 函数
    • 切片
    • 接口
    • map

Golang Numeric

超出范围会溢出

  • uint8 the set of all unsigned 8-bit integers (0 to 255)

  • uint16 the set of all unsigned 16-bit integers (0 to 65535)

  • uint32 the set of all unsigned 32-bit integers (0 to 4294967295)

  • uint64 the set of all unsigned 64-bit integers (0 to 18446744073709551615)

  • int8 the set of all signed 8-bit integers (-128 to 127)

  • int16 the set of all signed 16-bit integers (-32768 to 32767)

  • int32 the set of all signed 32-bit integers (-2147483648 to 2147483647)

  • int64 the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

  • float32 the set of all IEEE-754 32-bit floating-point numbers

  • float64 the set of all IEEE-754 64-bit floating-point numbers

  • complex64 the set of all complex numbers with float32 real and imaginary parts

  • complex128 the set of all complex numbers with float64 real and imaginary parts

  • byte alias for uint8

  • rune alias for int32

%T返回变量的类型,%d —> unsafe.Sizeof(n), 返回变量占用的字节数

bit 最小存储单位,byte基本存储单元,1 byte = 8 bits

Golang 浮点类型

  • 单精度float32 4字节
  • 双精度float64 8字节
  • 浮点数 = 符号位 + 指数位 + 尾数位
  • 浮点数都有符号
  • 尾数部分可能造成精度丢失。
  • float固定的范围和字段长度,不受OS的影响,默认64位

Golang 字符类型

使用byte类型来存储单个字符 - uint8

Golang的字符串由字节组成,不是有字符组成。

  • 如果保存的字符在ASCII范围内,可直接保存为byte 0 ~ 255
  • 如果超出ASCII范围,需要使用int
  • 如果需要按照字符输出,需要格式化。fmt.Printf(%c,c)

Golang使用UTF-8
英文字母 - 1个字节,中文 - 3个字节

Golang字符串 string类型

一串固定长度的字符连接起来的字符序列。

  • 单个字符使用UTF-8编码
  • 字符串不能被修改
  • 双引号可以识别转义字符;反引号可以输出原生形式``
  • 字符串拼接 + ,不可放在代码行开头

Golang 类型转换

  • 变量之间赋值,需要显式转换,不能自动转换。
var i int = 100
var num float32 = float32(i)
  • 被转换的是变量存储的数据,变量本身的类型没有改变

  • 高精度向低精度转换,编译不会报错,但是结果可能不是我们想要的。

Golang 基本数据类型和string类型的转换

基本类型转string

  • 方法1: fmt.Sprintf()
  • 方法2:strconv函数
  • 函数Itoa,int转string

string转基本类型

  • strconv
  • 确保能进行有效转换,不然Go将直接赋默认值。(不能把字符串”hello”转换成整型,直接赋值0.

Golang 指针 pointer

  • 基本数据类型,变量存的就是值,所以是值类型
  • 获取变量的地址,使用符号&
  • 指针类型, 指针变量存的是地址
    • var ptr *int = &num (定义指针变量ptr,赋值变量num的地址)
  • 获取指针指向的值,使用, 例如ptr
  • 值类型都有对应的值类型, int -> *int, float32 -> *float

Golang值类型和引用类型

  • 值类型:int系列,float系列,bool,string,数组和结构体struct

    • 变量存储值,内存通常在栈中分配
  • 引用类型:指针,切片,map,chan,interface

    • 变量存储的是一个地址,内存通常在堆中分配。当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,被GC回收。

标识符命名

  • 包名和目录保持一致,不要和标准库冲突

  • 变量名,函数名,常量名,采用驼峰法

  • 如果变量名,函数名,常量名首字母大写,则可以被其他包访问。如果首字母小写,
    则只能在本包中使用。

Golang 算术符

  • ++,–只能独立使用
    i++
    a=i
    a=i++ // error
  • 只有i++,没有++

Golang逻辑运算

&& 短路与, 前面条件有false,则后面条件不会执行。

|| 短路或,如果前面条件有true,则后面条件不会执行。

Golang 键盘输入语句

  • fmt.Scanf() 格式化输入

  • fmt.Scanln() 读取整行输入

Golang 位运算

  • 对于有符号数,最高位为符号位,0->正数,1->负数
    - 正数的原码,反码,补码一致
    - 负数的反码 = 符号位不变,其他位取反
    - 负数的补码 = 反码 + 1
    - 0的反码补码都是0
    - 计算机运算时,采用补码
    - 位运算:按位与,按位或,按位异或
    - 移位运算符
        - 右移 >> 低位溢出,符号位不变,并用符号位补溢出的高位
        - 左移 << 符号位不变,低位补0
        
    ### Golang 分支控制
    
    - if语句必须有{},即使只有一行代码
        - Go语言中,支持直接在if语句中定义变量 :=
        
    ### Golang
    - 每一个case分支都是唯一的,从上到下逐一测试,直到匹配为止
    - switch 匹配项语句块不需要加break,自动break
    - case 后的表达式可以是多个
    
    ### 递归调用
    - 执行一个函数时,就创建一个新的受保护的独立空间(新的函数栈)
    - 函数局部变量独立,不互相影响
    - 递归必须向退出递归的条件逼近,否则会进入死循环
    - 执行完毕或者遇到return,会返回。返回给条用函数,新建的函数栈会被销毁
    
    ### 函数
    - 函数形参可以是多个,返回值也可以是多个
    - 形参列表和返回值列表可以是值类型和引用类型
    - 函数名遵循标识符命名规范,首字母大写即为public`变量
    - 函数中变量是局部的,函数外不生效
    - 基本数据类型和数组默认都是值传递的,即进行值拷贝,函数中修改,不会影响原来的值
    - 希望函数中的变量能够修改函数外的变量,可以传入变量的地址。
    - Go函数不支持函数重载 (使用了别的方式实现)
    - GO中,函数也是一种数据类型,可以复制给变量,则该变量就是一个函数变量类型
    - 函数既然是一种数据类型,就可以作为形参
    - GO 支持自定义数据类型, 相当于起了一个别名,但还是两种类型,依旧需要显式转换。 type myInt int
    - 支持对函数返回值命名,调用函数时,直接定义返回值的变量
    - Go支持可变参数   ```func sum(n1 int, args... int) int{}
    • args是切片

init函数

  • 通常可以在init函数中完成初始化的工作,例如给全局变量赋值(赋值语句必须在一个函数中) Name := "zhening" // 全局变量不能再函数外赋值
  • 每一个源文件都可以后一个init函数,在main函数被调用前被Go框架调用
  • 如果包含全局变量,编译器执行顺序是 全局变量定义 >> init函数 >> main函数
  • 引入包时,包中的init函数就会执行
  • 引入包中变量定义 >> 引入包中init函数 >>

匿名函数

  • 定义函数时就直接调用
    res1 := func(n1 int, n2 int) int {
        return n1 + n2
    } (10, 20) // 赋值
 ```   
- 将匿名函数赋给函数变量
 
``` 
     a := func(n1 int, n2 int) int {
          return n1 + n2
     } 
    a(10,20)

闭包

一个函数与其相关的引用环境组合成的整体(实体)

  • 闭包的说明: 返回的时一个匿名函数,但是这个函数引用到了函数外的n,因此这个匿名函数就和n形成了一个整体,构成了闭包
  • 类比理解,闭包时类,函数时操作,n是字段。函数和它使用到的n构成了闭包
  • 当我们反复调用f函数时,n只初始化一次,因此每调用一次就进行一次累加
  • 闭包可以保留上次引用到的某个值,所以传入一次就可以反复使用

函数defer

  • 程序中经常需要创建资源(数据库连接,文件句柄,锁),为了在函数执行完毕后,释放资源,可以使用defer
  • 当程序执行到defer时,会将defer后面的语句压入到独立的栈中,暂时不执行
  • 当函数执行完毕之后,再从栈中,按照先入后出的方式出栈执行
  • 放入栈中时,会将相关的值拷贝入栈 (记录函数当时的状态)
  • 最佳实践
    • defer后可以继续使用资源
    • 函数完毕后,系统依次从defer栈中,取出语句,关闭资源
    • 非常简洁
      file = openfile("文件名"defer file.close() //函数结束后释放文件句柄
      // 其他代码
      connect = openDatabase()
      defer connect.close() // 函数结束后释放数据库资源
      // 其他代码

函数传递方式

  • 两种传递方式 (传递给变量的都是不笨)
    • 值传递:传递值 - 传递的效率取决于数据量的大小
      • 默认 int, float, bool, string, 数组,结构体struct
    • 引用传递: 传递地值 - 效率高,因为数据量小
      • 默认 指针,切片,map,管道chan, interface

变量作用域

  • 函数内部声明、定义的变量叫做局部变量,作用域仅限于函数内部
  • 函数外部声明、定义的变量叫做全局变量,作用域在整个包都有效,如果其首字母为大写,则作用域在整个程序都有效
  • 如果变量是在一个代码块中,比如for/if中,那么这个代码只在代码块中起作用

字符串常用的系统函数

  • 返回str的长度(字节数)len(str)
    • golang编码统一为UTF-8(ascii的字符(字母和数字)占用一个字节,汉字占用3个字节)
  • 遍历str,解决中文乱码的问题 r := []rune str
  • 字符串转整数 n, err := strconv.Atoi("12")
  • 整数转字符串 str := strconv.Itoa(12345)
  • 字符串转[]byte var bytes = []byte("hello")
  • []byte转字符串 str = string([]byte{97,98,99})
  • 10 进制数转2,8,16进制的数 strconv.FormatInt(,)
  • 查找子串是否在指定的字符串中 strings.Contains("seafood","foo")
  • 统计一个字符串中,有多少个不重复的指定子串 strings.Count("cecheese","e")
  • 不区分大小姐的字符串比较 strings.EqualFold(,)
  • 返回子串第一次出现的位置 strings.Index("")
  • 返回子串在字符串最后一次出现的index strings.LastIndex("")
  • 替换指定的子串,可以指定数量 strings.Replace(string,string,string,int)
  • 按照指定的某个字符拆分,讲一个字符串拆分成字符串数组 strArr := strings.Split("Hello,World",",")
  • 字符串进行大小写的转换 string.ToLower("") strings.ToUpper("")
  • 去掉字符串左右两边的空格 strings.TrimSpace(" test ")
  • 去掉字符串左右两边指定的字符(可以放多个字符) strings.Trim(""," !"
  • 去掉左边 strings.TrimLeft()
  • 去掉右边 strings.TrimRight()
  • 判断字符串是否以指定的字符串开头 strings.HasPrefix()
  • 判断字符串是否以指定的字符串结尾 strings.HasSuffix()

时间和日期的函数

  • 需要引入time包 import time
  • time.Time 类型, 用于表示时间, struct
  • 获取当前时间 now := time.now() // 带着时区,毫秒数, type = time.Time
  • 通过now()获取年月日
    • now.Year()
    • now.Month() // int()
    • now.Day()
    • now.Hour()
    • now.Minute()
    • now.Second()
  • 格式化日期时间
    • 方式1:使用fmt.Printf("%d-%d-%d %d:%d:%d",now)
    • 方式2:now.Format("2006-01-02 15:04:05")
  • 时间常量
    • 只能从小单位乘到大单位,不能使用除法
  • 获取Unix时间戳和Unix纳秒时间戳(用来当做随机数的种子)
    • now.Unix() // int64 , 1970-01-01 UTC
    • now.UnixNano() // int64

Go内置函数

  • len 用于求长度,比如str,数组,slice,map,channel
  • new 用于分配内存,主要用来分配值类型,比如int, float32,sturct … 返回的是一个指针
    • new takes a type T, allocates storage for a variable of that type at run time, and returns a value of type *T pointing to it
  • make 用于分配内存,主要用来分配引用类型,比如chan, map, slice

Golang的错误处理

  • 默认情况下,当程序发生错误后,程序就会退出(发生panic)
  • 如果发生错误,可以捕获到错误,并继续执行,就是错误处理
  • 不支持try…catch…finally…
  • Go中使用panic,defer,recover来处理错误
  • 抛出一个panic的异常,然后在defer中通过recover捕获这个异常,然后正常处理
  • 防止程序挂起,保证代码的健壮
  • 可以自定义错误 errors.New("错误说明") //返回error类型
  • panic内置函数,接受一个interface{}类型的值作为参数,可以接受error类型的变量,输出错误信息,并退出函数

Golang数组

  • 定义 var a [5]int //var 数组名 [数组大小] 数据类型
  • 内存布局(重要)
    • 数组的地址可以通过数组名来获取 &数组名
    • 数组的第一个元素的地址,就是数组的首地址
    • 数组的各个元素的地址间隔是依据数组的类型决定,比如 int64 -> 8 int32 ->4
  • 遍历方式
    • 常规遍历
    • for-range结构遍历 for index,value := range array01 {}
      • index 数组下标
      • value 对应下标的值
      • 仅在for循环中可见的局部变量
      • 不需要index时,可以写成 for _, value := range array01 {}
  • 数组是多个相同类型的数据的组合,一个数组一旦声明、定义,其长度就是固定(必须定义),不能动态变化
  • var arr []int 不是数组,而是切片
  • 元素可以是任何数据类型,但是不能混用
  • 数组创建后,如果没有赋值,会使用默认值
  • 使用数组的步骤 1. 声明数组并开辟空间 2. 给数组各个元素赋值 3. 使用数组
  • 数组下标从0开始
  • Go的数组是值类型,在默认情况下是值传递,因此会进行值拷贝,数组间不会互相影响 (函数调用时不会影响原来的值),这一点和其他编程语言不同
  • 长度是数组类型的一部分,在传递函数参数的时候,需要考虑数组的长度

Golang 切片

  • 数据个数不确定的情况下使用切片

  • 切片是数组的引用,是引用类型,在进行传递时,遵循引用传递的机制

  • 切片的使用和数组类似

  • 长度可变

  • 定义 var a []int

    var intArr [5]int = [...]int{1, 22, 33, 66, 99}
    //定义一个切片
    slice := intArr[1:3]
    fmt.Println("slice 的元素是", slice)
    fmt.Println("slice 的长度是", len(slice))
    fmt.Println("slice 的容量是", cap(slice)) // 返回切片的容量
  • slice 从底层来说,其实就是一个数据结构 (struct 结构体)

    type slice struct{
         ptr *[2]int
         len int
         cap int
     }
  • 切片的使用

    • 第一种方式: 定义一个切片,然后让切片引用一个已经创建好的数组
    • 第二种方式: 通过make来创建切片,make也会创建一个数组,是有切片在底层进行维护,外部不可见
      // var 切片名 [] type = make([]type, len, cap)
      var slice []int = make([]int, 4, 10)
  • 切片初始化时, var slice = arr[startindex,endindex] // 从数组下标startindex,取到endindex(不包括)

  • 不能越界,范围在0 - len(arr)之间,但是可以动态增长

    • var slice = arr[0:end] 可以写作 arr[:end]
    • var slice = arr[start:len(arr)] 可以写作 arr[start:]
    • var slice = arr[0:len(arr)] 可以写作 arr[:]
  • cap内置函数,查看最大可以存储个数

  • 切片定义完后不能直接使用,需要引用到一个数组,或者使用make开辟一个新的空间

  • 切片可以继续切片

  • 使用append函数,对切片进行动态追加 slice = append(slice, 300, 400) // 需要重新赋值接收

    • 对切片扩容
  • 使用copy函数 copy(slice1,slice2)

    • copy参数的数据类型是切片
    • 数据空间独立,互不影响
  • string底层是一个byte数组,所以可以使用切片来处理

  • 字符串本身不可改变,可以先传换成byte切片,修改完之后再转换回去字符串

  • byte切片通过字节来处理,处理中文会乱码; 需要使用[]rune

Golang map类型

  • map是key-value的数据结构,又称字段或者关联数组,类似其他编程语言的集合
  • // var map_name map[key_type] value_type
  • slice,map,function不可以用来做key, key通常是int或者string
  • 需要先开辟空间,才可以赋值var a map[string]string = make(map[string]string,10)
  • key不可以重复,唯一,无序key
  • value可以重复
  • map使用
    • 方式1
      var a map[string] string //此时map为nil
      a = make(map[string] string, 10)
      a["no1"] = "lenovo"
    • 方式2
      cities := make(map[string] string, 10) // 类型推导
      cities["n1"] = "beijing"
    • 方式3
      heros := map[string] string {
          "num1":"test"
      }
  • map的增删改查
    • 增改a["test"] = "test"
    • delete(map,key) // key不存在时,delete不起作用也不会报错
      • 全部删除? 1. 遍历 delete
      • 直接make一个新空间,旧的空间就会被GC
    • 查找
      val,ok := a["test"] // ok为bool类型,表示元素是否存在
      if ok {
        
      }else{
        
      }
  • 可以使用for-range遍历map
  • len(map)
  • map切片 slice of map, map的长度就可以动态增长, append
  • map 排序, map本身无序,每次输出可能都不一样 - 现将key放入切片,对切片排序,遍历切片输出map的值
  • map 是引用类型
  • map的容量达到时,还可以继续添加元素,自动扩容,不会发生panic
  • value经常是struct,来管理复杂数据

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

Go-进阶 上一篇