本笔记主要是本人根据B站视频教程《8小时转职Golang工程师(如果你想低成本学习Go语言)》进行学习总结而来,也对互联网上其他的学习资料有所借鉴,如果有版权相关问题,敬请联系邮箱admin#basecat.cn!(请将#更换为@)


语言介绍

Go语言的优势

  • 简单部署(可直接编译成可执行的机器码、运行时不依赖外部库、直接运行即可部署)
  • 静态语言,编译时就可以检查出绝大多数隐藏的问题
  • 语言层面的并发,充分利用多核
  • 强大丰富的标准库、runtime系统调度机制、高效的GC垃圾回收
  • 只有25个保留关键字,内嵌C语法支持,面向对象特征,跨平台开发

Go语言的明星作品

  • Kubernetes
  • docker

Go语言的劣势

  • 不权威的包管理,大部分依赖包来自Github
  • 没有泛化类型
  • 所有Exception都使用Error来处理
  • 对C的降级处理不够完美

Go语法

package

  • package声明是常见于Java等语言的包概念,主要用于工程管理、划分单元等抽象的用途。
  • 每个golang程序都必须有一个package main,并且在这个package中必须包含一个main()函数。
  • 在golang工程中,同一个路径下只能存在一个package,一个package可以拆分为多个源文件。

import

  • import关键字用于在程序中导入特定的package(标准库包或第三方依赖包等)
  • import后面的参数是路径而非包名,只是常见的包基本都是路径名和包名一致
  • import导入的package必须需要源码,否则编译器会报错。但编译器实际使用的情况会有所不同:import第三方依赖包时,编译器会主动编译其最新版本的源码并链接其产生的.a文件;而import标准库包时,编译器只会确保源码存在,不会重新编译其最新版本的源码,而是直接链接曾经已经编译好的.a文件。

特殊的import方式

  • 标准方式:import 路径
  • 别名方式:import 别名 路径 (此时别名会在当前程序里作为一个包名存在,用于替代路径下唯一的包名)
  • 匿名方式:import _ 路径 (此时当前程序只会执行路径所给包的系列init函数,不能用路径所给包的函数)
  • 全局方式:import . 路径 (将路径所给包中的所有函数全部导入到当前所在包中,此时在当前程序中使用路径所给包的函数时不需要使用包名.函数(),可直接用函数()
  • 在复杂工程中不推荐使用全局方式,不利于工程管理。
  • 多个包统一引用的多行写法略

括号

Go语言中的左大括号{要求和函数名在同一行,否则会编译报错

行末分号

Go语言程序源码中每一行的末尾可以加分号也可以不加,建议不加

变量

声明变量

  • 方法一:var 变量名 变量类型 声明一个变量,数值类型变量默认初值为0(不区分变量所在作用域)
  • 方法二:var 变量名 变量类型 = 初值 声明一个变量并为其赋一个初值
  • 方法三:var 变量名 = 初值 声明一个变量并为其赋一个初值,变量类型自动根据初值进行匹配
  • 方法四:变量名 := 初值 最常用的方法,省略var关键词,变量类型自动根据初值进行匹配
  • 特别注意:方法四不适用于全局变量的声明

声明多个变量

  • 单行写法:var 变量名1, 变量名2, ... = 初值1, 初值2, ... 位置一一对应,变量类型根据初值自动匹配
  • 多行写法略

常量与iota

  • 声明常量:const 变量名 变量类型 = 初值
  • iota只用于多行常量声明,常量声明首行的iota值为0,之后每行累加1,空行也会加1

函数

多返回值

func 函数名(参数1 参数1类型, 参数2 参数2类型, ...) (返回值1类型, 返回值2类型, ...) {
    ...
    return 返回值1, 返回值2, ...
}

带形参名称的多返回值

func 函数名(参数1 参数1类型, 参数2 参数2类型, ...) (返回值1 返回值1类型, 返回值2 返回值2类型, ...) {
    ...
    返回值1 = 值1
    返回值2 = 值2
    ...
    return
}

init()函数

init()函数是每个package中都会有的隐藏的一个函数,运行在main()函数之前。
golang调包的执行顺序如下图所示
execute.png

指针

因为是C++转Golang,指针部分略

defer

defer用来设定函数在执行完毕后要执行的操作,函数块内的defer语句序列是用栈来实现的,因此在函数执行完毕后,defer语句是按后进先出的顺序执行。值得一提的是执行顺序:defer所执行的指令会在return后才会执行。

数组与切片slice

数组声明:var myArray [10]int{0, 1, 2}myArray2 := [10]int{0, 1, 2}
数组长度函数:len(myArray)
数组遍历:for i := 0; i < len(myArray); i++for index, value := range myArray
匿名变量:_
数组传参:func proc(myArray [10]int)此传参方式会产生数组所有值的拷贝

slice声明:

var myArray []int{0, 1, 2} //声明一个slice并初始化默认值为1、2、3,长度为3

myArray2 := []int          //声明一个slice,但不给slice分配空间
myArray2 = make([]int, 3)  //为这个slice开辟3个长度的内存空间,初始值均为0

var myArray3 []int = make([]int, 3) // 声明一个slice,分配3个长度的内存空间,初始化值均为0

myArray4 := make([]int, 3) // 声明一个slice,分配3个长度的内存空间,初始化值均为0,最常用

判断slice是否是空切片(长度是否为0):if myArray == nil

slice动态调整容量

var numbers = make([]int, 3, 5)
// len = 3, cap = 5, numbers = [0 0 0]
// len指示slice中的合法元素数量,cap指示最大元素数量上限

numbers = append(numbers, 1) // 向numbers切片追加一个元素1
// len = 4, cap = 5, numbers = [0 0 0 1]

numbers = append(numbers, 2)
// len = 5, cap = 5, numbers = [0 0 0 1 2]

numbers = append(numbers, 3)
// len = 6, cap = 10, numbers = [0 0 0 1 2 3]
// golang的slice动态扩容会直接将cap翻倍

// 弹幕(存疑):动态扩容实际上是新申请一块内存然后拷贝

slice的截取

s := []int{1, 2, 3} // len = 3, cap = 3
s1 := s[0:2] // [lower-bound, upper-bound]
// s1 = [1, 2]
// lower-bound默认值为0,upper-bound默认值为len(s),截取区间左闭右开

// 通过这种方式的截取,s1是s的浅拷贝,和s指向的实际切片是一致的,对一个切片的修改会同步到另一个切片上

s2 := make([]int, 3) // s2 = [0 0 0]
copy(s2, s) // s2 = [1 2 3]
// copy()函数可以实现slice的深拷贝

slice传参:func proc(myArray []int)此传参方式可以理解为传递的是slice的头指针,类同C++的引用
弹幕(存疑):其实go的所有传参都是值传递,但slice和map数据类型的变量本身是一个指针

map

map声明:

var myMap map[int]string // key为int,value为string
myMap = make(map[int]string, 10) //在使用map之前,必须用make给map分配内存空间
myMap[1] = "java"
myMap[2] = "c++"
// map内部不按key排序,是乱序的

myMap2 := make(map[int]string)
myMap2[1] = "java"
myMap2[2] = "c++"
// 随使用动态扩容

myMap3 := map[int]string {
    1 : "java"
    2 : "c++"
}

判断是否是空map:if myMap == nil

map使用:

cityMap := make(map[string]string)

// 添加
cityMap["China"] = "Beijing"
cityMap["Japan"] = "Tokyo"
cityMap["USA"] = "NewYork"

// 遍历
for key, value := range cityMap {
    fmt.Println("key =", key, " value =", value)
}

// 删除
delete(cityMap, "China")

// 修改
cityMap["USA"] = "Washington D.C."

// (引用)传参
func printMap(cityMap map[string]string) {
   ...
}

struct

type myint int // 声明一种新的数据类型myint,作为int的别名

type Book struct { // 定义一个结构体
    title string
    auth string
}

// 新建变量
var book1 Book
book1.title = "Golang"
book1.auth = "zhang3"

// (非引用)传参
func changeBook(book Book) {
    ...
}

// 引用传参用指针,用指针类型的book参数直接 book.auth = "777"

不再更新

因为我发现我跟着看的教程视频讲的太好了,推荐大家都过去看看吧

发表评论

正在加载 Emoji

仅有一条评论

  1. 您好~我是腾讯云开发者社区运营,关注了您分享的技术文章,觉得内容很棒,我们诚挚邀请您加入腾讯云自媒体分享计划。完整福利和申请地址请见:https://cloud.tencent.com/developer/support-plan
    作者申请此计划后将作者的文章进行搬迁同步到社区的专栏下,你只需要简单填写一下表单申请即可,我们会给作者提供包括流量、云服务器等,另外还有些周边礼物。