GO语言全栈学习咯 ~
1. GO 语言简介
起源于2007年,GO语言之年轻如你所见,抛弃了历史设计的包袱,从轻出发。兼具python和C/C++优点
。突出之处便是:网络编程、系统编程、并发编程、分布式编程等。
创始人: Ken Thompson、Rob Pike、Robert Griesemer(三位对计算机底层设计头顶大佬皇冠的大神)
GO是编译型语言
2.语言特性
- 语言简单:Go 与 C99、C11 相似之处颇多,这也是Go语言被冠以“NextC”名号的重要原因
- 并发模型:运行时用 Goroutine 运行所有的一切,包括 main.main 入口函数。
- 内存分配: Go 选择了 tcmalloc,它本就是为并发而设计的高性能内存分配组件。
- 垃圾回收:Go 的 1.5 版本实现并发标记,逐步引入三色标记和写屏障等等,都是为了能让垃圾回收在不影响用户逻辑的情况下更好地工作。
- 静态链接:态链接被当作优点宣传。只须编译后的一个可执行文件,无须附加任何东西就能部署。
- 标准库:功能完善、质量可靠的标准库为编程语言提供了充足动力。
- 工具链:内置完整测试框架,其中包括单元测试、性能测试、代码覆盖率、数据竞争,以及用来调优的 pprof,这些都是保障代码能正确而稳定运行的必备利器。
3.哪些公司使用GO语言?
- Google:作为创造了Go语言的 google 公司,当然会力挺Go语言了。Google 有很多基于 Go 开发的开源项目,比如 kubernets,docker,大家可以参考《哪些项目使用Go语言开发》一节了解更多的Go语言开源项目。
- FacebookFacebook 也在使用Go语言,为此他们还专门在 Github 上建立了一个开源组织 facebookgo。大家可以通过 https://github.com/facebookgo 访问查看 facebook 开源的项目,其中最具代表性的就是著名平滑重启工具 grace。
- 腾讯:腾讯在 15 年就已经做了 Docker 万台规模的实践。因为腾讯主要的开发语言是 C/C++ ,所以在使用Go语言方面会方便很多,也有很多优势,不过日积月累的 C/C++ 代码很难改造,也不敢动,所以主要在新业务上尝试使用 Go。
- 百度:百度主要在运维方面使用到了Go语言,比如百度运维的一个 BFE 项目,主要负责前端流量的接入,其次就是百度消息通讯系统的服务器端也使用到了Go语言。
- 七牛云:七牛云算是国内第一家选Go语言做服务端的公司。早在 2011 年,当Go语言的语法还没完全稳定下来的情况下,七牛云就已经选择将 Go 作为存储服务端的主体语言
- 京东:京东云消息推送系统、云存储,以及京东商城的列表页等都是使用Go语言开发的。
- 小米:小米对Go语言的支持,在于运维监控系统的开源,它的官方网址是 http://open-falcon.org/。此外,小米互娱、小米商城、小米视频、小米生态链等团队都在使用Go语言。
- 360:360 对Go语言的使用也不少,比如开源的日志搜索系统 Poseidon,大家可以通过 https://github.com/Qihoo360/poseidon 查看,还有 360 的推送团队也在使用Go语言。
3. 安装GO开发环境
- 下载GO SDK
- 安装GO开发工具GOLang(安装步骤略…)
4. 学习说明:
之前大家学习一门语言,都是从变量、类型、自定义类型等入手,再到表达式、运算符、方法等,再到面向对象这样的路径前进。这次呢,我们反其道行之,我们先对整个GO项目的结构有个了解,杀个回马枪,去接受和对比、类比学习GO语言,岂不胸有成竹,不迷路?Let 's go!
5. GO结构篇
5.1 工作空间
1. 工作空间讲解:
workspace/
|
+ ---src/ //源码目录(包含子包在内的所有源码)
| |
| + --server/
| | |
| | + --main.go
| |
| +--service/
| |
| +--user.go
|
+ ---bin/ //可执行文件安装路径,不会创建额外的子目录
| |
| +--server
|
+ ---pkg/ //包安装路径,按操作系统和平台隔离
|
+ --linux_amd64
|
+ --service.a
2. 环境变量
GOROOT: 是Go的安装路径。GOROOT在绝大多数情况下都不需要修改
GOPATH: GOPATH是开发时的工作目录。
保存编译后的二进制文件。
go get和go install命令会下载go代码到GOPATH。
import包时的搜索路径
在使用Git等版本控制工具时,建议忽略pkg、bin目录。直接在src,或具体的子包下创建代码仓库
(repository)。
5.2 导入包
1. import导入
起点是GOPATH的绝对路径
eg: import "net/http" => import /usr/local/go/src/net/http
2. 别名
import osx "github.com/apple/osx/lib"
import nix "github.com/linux/lib"
导入参数是路径,而非包名
3.四种导入方式:
import "github.com/qyuhen/test" 默认方式:test.A
import X "github.com/qyuhen/test" 别名方式:X.A
import . "github.com/qyuhen/test" 简便方式:A
import _ "github.com/qyuhen/test" 初始化方式: 无法引用,仅用来初始化目标包
5.3 组织结构
包(package)由一个或多个保存在同一目录下(不含子目录)的源码文件组成。包的用途类似名字
空间(namespace),是成员作用域和访问权限的边界。
包名与目录名并无关系,不要求保持一致。
go list net/... // 查看net下所有的包
权限: 所有成员在包内均可访问。只有名称首字母大写的为可导出成员,在包外可视。
可通过指针转换等方式绕开该限制
初始化:包内每个源码文件都可定义一到多个初始化函数,但编译器不保证执行次序。
编译器首先确保完成所有全局变量初始化,然后才开始执行初始化函数。直到这些全部结束后,
运行时才正式进入main.main入口函数。
是不是有个疑惑呢?
- 什么是初始化函数呢?
- 在Go语言中,初始化函数是指在程序执行前自动调用的特殊函数。
- 初始化函数没有参数和返回值,且无法手动调用。它们通常用于初始化程序的状态、执行一些必要的设置或者注册一些信息。在同一个包中可以定义多个初始化函数,它们的执行顺序是随机的。初始化函数会在程序开始执行之前自动调用,确保在程序正式运行之前进行必要的初始化操作。
内部包:Go语言内部包–控制包成员的对外暴露
总之:外部包调用外部包是可以的,只要属性是公开的;但是内部包只能在同父级目录下的子包引入,其他包是不能引入的。
5.4 依赖管理
这个是非常重要的,否则可能会导致项目编译失败。
走过岁月,仍发现,一个优秀的产品不是一蹴而就的。
-
模块与包:
关系如:集合与元素的关系。模块包含多个包。 -
包的分类:
标准包:随源码发布的
第三方包:第三方提供的,如github匿名包: 只导入而不使用的包。通常情况下,我们只是想使用导入包产生的副作用,即引用包级别的变量、常量、结构体、接口等,以及执行导入包的init()函数。
内部包: 项目内部的包,位于项目目录下。 -
go Modules 的命令
go mod download: 下载 go.mod 文件中记录的所有依赖包。
go mod edit:编辑 go.mod 文件。
go mod graph:查看现有的依赖结构。
go mod init:把当前目录初始化为一个新模块。
go mod tidy:添加丢失的模块,并移除无用的模块。默认情况下,Go 不会移除 go.mod 文件中的无
用依赖。当依赖包不再使用了,可以使用go mod tidy命令来清除它。
go mod vendor:将所有依赖包存到当前目录下的 vendor 目录下
go mod verify:检查当前模块的依赖是否已经存储在本地下载的源代码缓存中,以及检查下载后是否
有修改。
go mod why:查看为什么需要依赖某模块。
6. GO骨肉篇
1.变量
// 函数内定义变量
var a int = 8
b := "hello world"
var (
c = "test"
d = 88
)
// 包内定义
var a int = 66
var b,c string = "aa","bb"
var (
d = 45
ss = "tes"
)
2.内建变量类型
- bool string
- (u)int,(u)int8,(u)int16,(u)int32,(u)int64,uintptr
- byte,rune
- float32,float64,complex64,complex128
3.强制类型转换
var a,b int = 3,4
var c int =math.Sqrt(a*a + b*b) // 报错 ×
var c int =int(math.Sqrt(a*a + b*b)) // 将float64强制转换为int √
4. 常量
const filename ="abc.txt"
const a,b = 3,4 // 没有指定数据类型,可以作为float或者int等
const (
filename = "abc.txt"
a,b = 3,4
)
// 枚举
const (
CPP = 1
Java = 2
python = 3
golang = 4
)
const (
cpp = iota +1
java
python
golang
)
const (
b = 1 << (10 * iota)
kb
mb
gb
tb
pb
)
5. 程序结构控制
// 条件语句
if 条件 {
} else if 条件2 {
} else {
}
// if条件里可以赋值
// if条件里赋值的变量域就在这个if语句里
// swith
switch op {
case "+":
//operation one
case "-":
//operation two
}
// switch会自动break除非使用fallthrough
//switch后可以没有表达式
switch {
case score < 60:
return "F"
case score < 80:
return "C"
case scpre < 90:
return "B"
case score < 100:
return "A"
}
// 循环
sum :=0
for i:=1;i<=100;i++ {
sum +=i
}
//for的条件里不需要括号
//for的条件里可以省略初始条件,结束条件,递增表达式
13 -> 1011
func convertTOBin(){
result =""
for ;n >0; n /= 2 {
lsb := n %2
result = strconv.Itoa(lsb) + result
}
return result
}
func printFile(){
file, err = os.Open(filename)
if err != nil {
panic(err)
}
scanner = bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
// 死循环
for {
fmt.Println("abc") //一直输出abc
}
// 总结:
// for,if里面的条件没有括号
// if条件里也可以定义变量
// 没有while
// switch不需要break,也可以直接switch多个条件
6. 函数
func eval(a,b int, op string) int {
return a +b
}
// 函数返回多个值可以起名字
// 仅用于非常简单的函数
// 对于调用者而言没有区别
// 函数式编程
func pow(a,b int) int {
retunr int(math.pow(float64(a),float64(b)))
}
func apply(op func(int,int) int, a,b int) int {
p:=reflect.ValueOf(op).Pointor()
opName := runtime.FuncForPC(p).name()
fmt.printf("Calling function %s with args " + "(%d,%d)", opName,a,b)
return op(a,b)
}
res: = apply(pow, 3,4) // 81
// 可变参数列表
func sum(nums ...int) int {
s:=0
for _,i:= range nums {
s += i
}
}
func sum(nums ...int) int {
s:=0
for i:= range nums {
s += nums[i]
}
}
// 返回值类型写在最后面
// 可返回多个值
// 函数作为参数
// 没有默认参数,可选参数
7. 指针
var a int = 2
var pa *int = &a
*pa = 3
fmt.println(a)
// go中指针不能运算
// 值传递?引用传递?
// c/c++既可以值传递,引用传递;java和go只能值传递
// eg: 交换两个变量的值
func swap(a,b *int) {
*a,*b = *b,*a
}
8.内建容器
// 数组,切片,容器
// ①数组
var arr1 [5]int
arr2 := [3]int{1,3,5}
arr3 := [...]int{1,3,5}
// 数量写在类型前
for i:=0;i<len(arr3);i++ {
fmt.Println(arr3[i])
}
// range关键字
for i,v := range arr3{
fmt.Println(i,v)
}
// 可通过_省略变量
// 不仅range,任何地方都可通过_省略变量
// 如果只要i,可写成for i:= range numbers
// 数组是值类型,参数是数组的话,数组会进行一份拷贝。对外部没有影响
// [10]int 和[20]int是不同类型
// 调用func f(arr [10]int)会拷贝数组
// ################################################################
// 切片Slice
arr := [...]int{0,1,2}
// 字典