go快速入门
考虑到java竞争过于激烈 , 很多情况会造成不必要的内卷, 因此打算扩充知识面, 简单学习go语言 , 为日后做打算
[toc]
环境搭建
Downloads - The Go Programming Language (google.cn)
这里我的系统是windows10 , 因此选择OS 为 Windows的压缩包 , 下载解压到磁盘
这里放到了D盘
接下来来配置环境变量 , 直接搜索
指定go的bin目录 , 点击确定
同时我们还需要配置GOROOT以及GOPATH
GOPATH顾名思义就是我们go的工作目录 , 如果不配置的话就会需要反复下载依赖 , 非常麻烦
接下来我们还需要进行一些配置
我们打开windowsPowerShell , 输入
$env:GO111MODULE = "on"
$env:GOPROXY "http://goproxy.cn"
输入go env
检测我们是否设置成功
前者是设置 模块管理 , 后者是设置 代理
使用go mod管理需要 科学上网
git clone 下载gin
goroot就是go安装的根目录,gopath就是go项目所在的路径,高版本go项目已经不再依赖gopath来管理项目,使
用go mod:来管理项目。
win+R cmd 输入go version
, 出现了版本信息表明语言环境搭建成功
配置vscode
jetbrains也有go语言对应的ide , goland , 不过考虑到jb家的ide普遍占用过高, 因此这里暂时先用vscode进行语言层面的学习, 等到日后进行go项目开发再去使用ide。
在vscode商店搜索go , 选中下载量最高的那个插件下载 ,
其实多下载几个也没什么事, 根据个人选择进行安装即可
如果不知道自己的
GOPATH
或者GOROOT
路径在哪里,可以按win+R
键,输入cmd
,打开「命令提示符」输入go env
,即可看到自己的GOPATH
路径以及GOROOT
路径。
如果在vscode下载依赖的时候 一直failed , 可以试试重启电脑同时检查代理是否配置成功
https://goproxy.cn/
go env -w GOPROXY=https://goproxy.cn/
⚠️
⚠️⚠️⚠️这里遇到了一个很大的坑, 就是在配置GOROOT的时候, 虽然我们在path变量里面配置的是 D:\go\bin
, 可是实际上我们的GOROOT 变量应该是 D:\go
, 否则就是不断的出现main.go:3:8: package fmt is not in GOROOT (D:\go\bin\src\fmt) package command-line-arguments: cannot
这里还是查看了go的目录结构 , 发现src是go的子目录 , 但是我们吧GOROOT配置成go\bin
, 编译器就会识别成go\bin\src
, 因此导致找不到fmt
helloworld
在vscode中下载code Runner插件, 无需命令, 右键即可直接运行go程序
接着我们打开之前创建的gopath目录 , 写下我们的第一个go文件
注意go语言代码结尾不需要写 ;
Go 语言虽然看起来不使用分号作为语句的结束,但实际上这一过程是由编译器自动完成,因此才会引发像上面这样的错误
1 | package main |
Go基本要素
包是结构化代码的一种方式:每个程序都由包(通常简称为 pkg)的概念组成,可以使用自身的包或者从其它包中导入内容。
如同其它一些编程语言中的类库或命名空间的概念,每个 Go 文件都属于且仅属于一个包。一个包可以由许多以 .go 为扩展名的源文件组成,因此文件名和包名一般来说都是不相同的。
你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main 表示一个可独立执行的程序,==每个 Go 应用程序都包含一个名为 main 的包。==
package
一共有四种基本写法 , 推荐最后一种
如果需要多个包,它们可以被分别导入:
1 | import "fmt" |
或:
import "fmt"; import "os"
但是还有更短且更优雅的方法(被称为因式分解关键字,该方法同样适用于 const、var 和 type 的声明或定义):
1 | import ( |
它甚至还可以更短的形式,但使用 gofmt 后将会被强制换行:
import ("fmt"; "os")
在我们导入多个包时,==最好按照字母顺序排列包名==,这样做更加清晰易读。
如果包名不是以 . 或 / 开头,如 “fmt” 或者 “container/list”,则 Go 会在全局文件进行查找;
如果包名以 ./ 开头,则 Go 会在相对目录中查找;
如果包名以 / 开头(在 Windows 下也可以这样使用),则会在系统的绝对路径中查找。
这里就是简单的相对路径/绝对路径的知识
可见性
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,
如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),
这被称为导出(像面向对象语言中的 public);
如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 private )。
这里比如fmt.Print()
的方法就是大小字母开头, 这里注意区分于java的驼峰命名
别名
你可以通过使用包的别名来解决包名之间的名称冲突,或者说根据你的个人喜好对包名进行重新设置,如:import fm "fmt"
。下面的代码展示了如何使用包的别名:
1 | package main |
这里的逻辑跟很多语言都是相似的
如果导入了一个包却没有使用它,则会在构建程序时引发错误,如
imported and not used: os
,这正是遵循了 Go 的格言:“没有不必要的代码!“。
函数
一个函数可以拥有多返回值,返回类型之间需要使用逗号分割,并使用小括号
()
将它们括起来,
比如下面就是定义了一个函数
func functionName()
可以在括号 ()
中写入 0 个或多个函数的参数(使用逗号 ,
分隔),每个参数的名称后面必须紧跟着该参数的类型。
main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init () 函数则会先执行该函数)。
如果 main 包的源代码没有包含 main 函数,则会引发构建错误 undefined: main.main。
main 函数既没有参数,也没有返回类型(与 C 家族中的其它语言恰好相反)。
如果你不小心为 main 函数添加了参数或者返回类型,将会引发构建错误:
func main must have no arguments and no return values results.
在程序开始执行并完成初始化后,第一个调用(程序的入口点)的函数是 main.main()
,该函数一旦返回就表示程序已成功执行并立即退出。
需要注意的是⚠️ : 左大括号 {
必须与方法的声明放在同一行,这是编译器的强制规定,否则你在使用 gofmt 时就会出现错误提示:
build-error: syntax error: unexpected semicolon or newline before {
这是因为编译器会产生
func main() ;
这样的结果,很明显这错误的
- go在编译的时候会自动给每一行后面添加
;
Go 语言虽然看起来不使用分号作为语句的结束,但实际上这一过程是由编译器自动完成,因此才会引发像上面这样的错误
右大括号 }
需要被放在紧接着函数体的下一行。如果你的函数非常简短,你也可以将它们放在同一行:
比如func Sum(a, b int) int { return a + b }
那么go中函数的一般形式就是
1 | func functionName(parameter_list) (return_value_list) { |
- parameter_list 的形式为 (param1 type1, param2 type2, …)
- return_value_list 的形式为 (ret1 type1, ret2 type2, …)
只有当某个函数==需要被外部包调用的时候==才使用==大写字母开头==,并遵循 Pascal 命名法;
否则就遵循骆驼命名法,即第一个单词的首字母小写,其余单词的首字母大写。
基本数据类型
可以包含数据的变量(或常量),可以使用不同的数据类型或类型来保存数据。
使用 var 声明的变量的值会自动初始化为该类型的零值。
类型定义了某个变量的值的集合与可对其进行操作的集合。
类型可以是
- 基本类型,如:int、float、bool、string;
- 结构化的(复合的),如:struct、array、slice、map、channel;
- 只描述类型的行为的,如:interface。
结构化的类型没有真正的值,它使用 nil 作为默认值(在 Objective-C 中是 nil,在 Java 中是 ==null==,在 C 和 C++ 中是 NULL 或 0)。
值得注意的是,Go 语言中不存在类型继承。
函数也可以是一个确定的类型,就是以函数作为返回类型。这种类型的声明要写在函数名和可选的参数列表之后,例如:
type
使用 type 关键字可以定义你自己的类型,你可能想要定义一个结构体 (第 10 章),但是也可以定义一个已经存在的类型的别名
type IZ int
这里并不是真正意义上的别名,因为使用这种方法定义之后的类型可以拥有更多的特性,且在类型转换时必须显式转换。
然后我们可以使用下面的方式声明变量:
var a IZ = 5
这里我们可以看到 int 是变量 a 的底层类型,这也使得它们之间存在相互转换的可能。
如果你有多个类型需要定义,可以使用因式分解关键字的方式,例如:
1 | gotype ( |
每个值都必须在经过编译后属于某个类型(编译器必须能够推断出所有值的类型),==因为 Go 语言是一种静态类型语言==。
一般代码结构
1 | package main |
Go 程序的执行(程序启动)顺序如下:
按顺序导入所有被 main 包引用的其它包,然后在每个包中执行如下流程:
如果该包又导入了其它的包,则从第一步开始递归执行,但是每个包只会被导入一次。
然后以相反的顺序在每个包中初始化常量和变量,如果该包含有 init 函数的话,则调用该函数。
在完成这一切之后,main 也执行同样的过程,最后调用 main 函数开始执行程序。
总结起来就是,
- 对于一个普通go程序 , 执行的顺序是
init()
,main()
, main函数里面可能会执行其他的方法 - 对于导包了的go程序, 就会先递归的执行上一层的程序, 直接到底 , 执行一般情况
那么伪代码就是这样
1 | run(GoProgram){ |
类型转换
在必要以及可行的情况下,一个类型的值可以被转换成另一种类型的值。由于 Go 语言不存在隐式类型转换,因此所有的转换都必须显式说明,就像调用一个函数一样(类型在这里的作用可以看作是一种函数):
1 | valueOfTypeB = typeB(valueOfTypeA) |
类型 B 的值 = 类型 B (类型 A 的值)
1 | a := 5.0 |
但这只能在定义正确的情况下转换成功,
例如从一个取值范围较小的类型转换到一个取值范围较大的类型(例如将 int16 转换为 int32)。
当从一个取值范围较大的转换到取值范围较小的类型时(例如将 int32 转换为 int16 或将 float32 转换为 int),
会发生精度丢失(截断)的情况。==当编译器捕捉到非法的类型转换时会引发编译时错误,否则将引发运行时错误==。
具有相同底层类型的变量之间可以相互转换:
1 | Lvar a IZ = 5 |
Go 命名规范
干净、可读的代码和简洁性是 Go 追求的主要目标。通过 gofmt 来强制实现统一的代码风格。
Go 语言中对象的命名也应该是简洁且有意义的。
像 Java 和 Python 中那样使用混合着大小写和下划线的冗长的名称会严重降低代码的可读性。
java码农无语。。。
名称不需要指出自己所属的包,因为在调用的时候会使用包名作为限定符。
返回某个对象的函数或方法的名称一般都是使用名词,没有 Get… 之类的字符,
如果是用于修改某个对象,则使用 SetName。
1 | String name = person.getName(); |
1 | name:=person.name |
有必须要的话可以使用大小写混合的方式,如 MixedCaps 或 mixedCaps,而不是使用下划线来分割多个名称。
2023年1月28日补充 package相关内容
关于GO的package的坑
首先我们需要知道Go
语言使用包来组织源代码的,并实现命名空间的管理,任何一个Go
语言程序必须属于一个包,即每个go
程序的开头要写上package <pkg_name>
。
Go
语言包一般要满足如下==三个条件==:
- 同一个目录下的同级的所有
go
文件应该属于一个包;- 包的名称可以跟目录不同名,不过建议同名;
- * 一个
Go
语言程序有且只有一个main
函数,他是Go
语言程序的入口函数,且必须属于main
包,没有或者多于一个进行Go语言
程序编译时都会报错;
昨天尝试了很多次, 都无法导入本地包, 一直显示main.go:4:2: malformed import path "/pack1": empty path element
诸如此类,
网上找了很多 , 绝大多数是让调整 GOROOT以及GOPATH的路径 , 要么就是设置GO111MODULE=on
不过设置了还是没用, 并不能解决问题 , 最后找到了这篇博客总算是找到了方法go mod 如何导入本地的包 - wind-zhou - 博客园 (cnblogs.com)
首先是文件夹的结构, 可以使用tree
命令快速展示
D:
tree GOSTUDY /F
TREE [drive:][path] [/F] [/A]
/F 显示每个文件夹中文件的名称。
/A 使用 ASCII 字符,而不使用扩展字符。
1 | D:\GOSTUDY |
需要提前使用gomod 来构建项目
go mod init
注意一定要在工作区的根目录
接下来写一点测试代码
main.go
这里的
goStudy/pack1
都表示目录 , 实际上与包名无关
- 不过go语言的开发规范建议我们最好还是 包的名称跟目录同名;
1 | package main |
pack1/pack1.go
需要注意的是 , go 的package可以不与文件名相同, 但是同一个目录下的所有go文件的package的name都需要相同
- 注意导出的 变量以及函数的首字母需要大写
1 | package pack1 |
最后的目录结构
1 | D:\GOSTUDY |
接着我们go rum main.go
运行程序