Go 语言入门指南:基础语法和常用特性解析
1.Go的发展历史
Go(又称 Golang)是 Google 的 Robert Griesemer(Unix操作系统的发明人之一,B语言的设计者,UTF-8编码设计者之一,图灵奖得主)、Rob Pike(参与贝尔实验室Unix的开发,设计了UTF8) 、Ken Thompson(C语言前身B语言的作者,Unix的发明人之一) 于2007开发的一种静态强类型、编译型语言。Go 语言语法与 C 相近,但功能上有:内存安全、GC(垃圾回收),结构形态及 CSP-style 并发计算。
Go语言具有如下优点:
- 可直接编译成机器码,不依赖其他库。 glibc的版本有一定要求,部署就是扔一个文件上去就完成了。
- 静态类型语言。 Go语言虽然是静态语言,但是有动态语言的感觉,静态类型的语言可以在编译的时候检查出来隐藏的大多数问题,动态语言的感觉就是有很多的包可以使用,写起来的效率很高。
- 语言层面支持并发。 这个就是Go最大的特色,天生的支持并发。Go就是基因里面支持的并发,可以充分的利用多核,很容易的使用并发。Go语言的并发特性由贝尔实验室的Hoare与1978年发布的CSP理论演化而来。
- 内置runtime,支持垃圾回收。 这属于动态语言的特性之一吧,虽然目前来说GC(内存垃圾回收机制)不算完美,但是足以应付我们所能遇到的大多数情况,特别是Go1.1之后的GC。
- 简单易学,语言简洁。 Go语言的作者都有C的基因,那么Go自然而然就有了C的基因,那么Go关键字是25个,但是表达能力很强大,几乎支持大多数你在其他语言见过的特性:继承、重载、对象等。
- 丰富的标准库。 Go目前已经内置了大量的库,特别是网络库非常强大。
- 内置强大的工具。 Go语言里面内置了很多工具链,最好的应该是gofmt工具,自动化格式化代码,能够让团队review变得如此的简单,代码格式一模一样,想不一样都很困难。
- 跨平台编译。 如果你写的Go代码不包含cgo,那么就可以做到window系统编译linux的应用,如何做到的呢?Go引用了plan9的代码,这就是不依赖系统的信息。
Go语言生态:
Docker: Docker是一款基于go语言实现的容器引擎。
Kubernetes: Kubernetes是一款基于go语言实现的用于管理云平台中多个主机上的容器化的应用。
Consul: Consul是一个的支持多数据中心分布式高可用的服务发现和配置共享的服务软件,由 HashiCorp 公司用 Go 语言开发, 基于 Mozilla Public License 2.0 的协议进行开源. Consul 支持健康检查,并允许 HTTP 和 DNS 协议调用 API 存储键值对。
ETCD: ETCD是用于共享配置和服务发现的分布式,一致性的KV存储系统。
2.Go语言特征
- 自动立即回收。
- 更丰富的内置类型。
- 函数多返回值。
- 错误处理。
- 匿名函数和闭包。
- 类型和接口。
- 并发编程。
- 反射。
- 语言交互性。
- 泛型。Go在1.8支持泛型特性。
3.搭建Go环境
3.1 安装Go
Go下载地址是:golang.google.cn/dl/,目前最新稳定版本是1.16.5。windows系统下载对应winwods版本,mac系统下载对应的darwin版本,linux系统下载对应的linux系统。
安装完毕后通过go -version 检查是否安装成功,若输出版本则说明安装成功,例如:
4.编写第一个Go程序
//新建一个main.go文件
package main //声明 main 包,表明当前是一个可执行程序,一定要声明main包不然程序运行不了
import "fmt" //导入go内部fmt模块
//声明main函数,main函数为程序入口函数
func main(){
fmt.Print("hello golang") //在终端打印"hello golang"
}
//可以直接通过golang idea 直接运行得到结果,也可以通过go build编译go文件的得到可执行文件
打开终端进入main.go所在文件目录,执行go build one.go 就可以编译go文件并生成一个可执行文件。
//执行go build main.go命令会在当前文件下生成一个可执行文件,如果是windows系统则会生成main.exe,双击main.exe即可执行。也可以通过-o 选项指定编译后可生成文件的名字,例如:go build -o java.exe
C:go>go build main.go
C:go>main.exe
hello golang
4.1 init函数与main函数
go语言中init函数用于package(包)的初始化,init函数是go语言一个重要特征。具有如下特性:
-
init函数是用于程序执行前package的初始化的函数,比如初始化包里的变量、资源初始化等。
-
每个包可以拥有多个init函数,包的每个源文件也可以拥有多个init函数。
-
同一个包中多个init函数的执行顺序go语言没有明确的定义。
-
不同包的init函数按照包导入的依赖关系决定该初始化函数的执行顺序。
-
init函数不能被其他函数调用,而是在main函数执行之前(init执行顺序优于main函数),自动被调用。
main函数则是go程序的默认入口函数(主函数),与init函数具有以下区别:
相同:
两个函数在定义时不能有任何的参数和返回值,且Go程序自动调用。
不同:
init可以应用于任意包中,且可以重复定义多个。
main函数只能用于main包中,且只能定义一个。
package main //声明 main 包,表明当前是一个可执行程序
import "fmt" //导入go内部fmt模块
//声明main函数,main函数为程序入口函数
func main(){
fmt.Println("hello golang") //在终端打印"hello golang"
}
func init(){
fmt.Println("init func...")
}
/**
执行结果:
init func...
hello golang
*/
5.Go内置类型、函数、接口
5.1 内置类型
//基本类型
bool
int(32 or 64), int8, int16, int32, int64
uint(32 or 64), uint8(byte), uint16, uint32, uint64
float32, float64
string
complex64, complex128
array -- 固定长度的数组
//引用类型
slice -- 序列数组(最常用)
map -- 映射
chan -- 管道
5.2 内置函数
append -- 用来追加元素到数组、slice中,返回修改后的数组、slice
close -- 主要用来关闭channel
delete -- 从map中删除key对应的value
panic -- 停止常规的goroutine (panic和recover:用来做错误处理)
recover -- 允许程序定义goroutine的panic动作
real -- 返回complex的实部 (complex、real imag:用于创建和操作复数)
imag -- 返回complex的虚部
make -- 用来分配内存,返回Type本身(只能应用于slice, map, channel)
new -- 用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
cap -- capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
copy -- 用于复制和连接slice,返回复制的数目
len -- 来求长度,比如string、array、slice、map、channel ,返回长度
print、println -- 底层打印函数,在部署环境中建议使用 fmt 包
5.3 内置Error接口
type error interface { //只要实现了Error()函数,返回值为String的都实现了err接口
Error() String
}
6.Go语言命名规范
Go语言从语法层面进行了以下限定:任何需要对外暴露的名字必须以大写字母开头,不需要对外暴露的则应该以小写字母开头。
- 当命名(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,表示可以被外部包的代码所使用。等同于其他语言中的public修饰符。
- 命名如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的。等同于其他语言的private修饰符。
- 包名称:Go语言中的包名称一般以小写单词,不要使用下划线或者混合大小写。如下图
javaee // 正确的
Javaee // 错误的
j2ee // 正确的
j2_ee // 错误的
- 文件命名:Go语言中的文件命名应尽量采取有意义的文件名,简短,有意义,应该为小写单词,使用下划线分隔各个单词。
- 结构体命名:Go语言中的结构体命名采用驼峰命名法,首字母根据访问控制大写或者小写。
- 接口命名:Go语言中的接口命名与结构体命名是一致的,单个函数的结构名以 "er"作为后缀,例如 Reader、Writer。
- 变量命名:Go语言中的变量命名规则和结构体命名规则一致,变量名称一般遵循驼峰法,首字母根据访问控制原则大写或者小写,但遇到特有名词时,需要遵循以下规则:
-
- 如果变量为私有,且特有名词为首个单词,则使用小写,如 appService。
- 若变量类型为 bool 类型,则名称应以 Has, Is, Can 或 Allow 开头。
- 常量命名:Go语言中的常量均需使用全部大写字母组成,并使用下划线分词。如果是枚举类型的常量,需要先创建相应类型。
// 普通常量
const APP_NAME = "xxxApp" // 好的
const APPNAME = "xxxApp" // 坏的
const appName = "xxxApp" // 坏的
// 枚举常量
type Scheme = string
const (
HTTP Scheme = "http"
HTTPS Scheme = "https"
)
7.变量
变量(Variable)的功能是存储数据。常见的变量类型有整型、浮点型、字符串、布尔类型等等。变量必须经过声明后才能使用(强类型语言),在同一作用域下不支持重复声明,并且Go语言声明的变量必须使用(定义常量除外),否则无法通过编译。
7.1 声明变量
Go声明变量语法
//声明变量语法:
var 变量名 变量类型
//例子:
var name string
var age int
var isOk bool
7.2 批量声明变量
每声明一个变量就需要写var关键字会比较繁琐,所以有了如下
var (
a string
b string
c string
d int
)
7.3 变量初始化
//声明变量初始化语法,可省略变量类型,go根据变量值进行类型推断自动推断出变量的类型
var 变量名 变量类型 = 表达式
//例子:
var name string="ikun"
var age int=18
var isOk bool=true
//一次初始化多个变量,go会根据变量值自动推荐变量的类型
var a,b="ojbk",18
//类型推断:go会根据变量值自动推荐变量的类型
var userName="测试" //推断userName变量类型为string
var myAge=18 //推断myAge变量类型为int
7.4 短变量声明
在函数内部,可以使用更简略的 := 方式声明并初始化变量。
package main
import "fmt"
//全局变量
var a=10
func main() {
//短变量
b:=20
fmt.Println(a,b)// 10 20
}
//那个b就是短变量
7.5 匿名变量
package main
import "fmt"
func test1()(int,string){
return 1,"ces"
}
func main() {
//使用_忽略第二个变量
age,_:=foo()
//使用_忽略第一个变量
_,name:=foo()
fmt.Println(age,name)// 1 ces
}
8.常量(不会变的量)
同理有如下几种方式定义
const pi = 3.14159
const e = 2.71826
//通过const 关键字定义多个常量
const ( a="zhangsan"; year=2 )
//const同时声明多个常量时,如果省略了值则表示和上面一行的值相同,n1、n2、n3的值都为100
const (
n1=100
n2
n3
)
9.下划线
_是特殊标识符,用于忽略结果。
10.简单的流程控制
if语句
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
switch语句 ps:从上至下逐一测试,直到匹配为止。
switch var1 {
case val1:
...
case val2:
...
default:
...
}
for循环语句 ps:go里没有while语句(学完Java思想要稍微转变一下)
// for循环有三种形式
// 形式1(fori循环)
for init; condition; post {
}
// 例子:
for i:=0;i<10;i++{
}
//形式2
n:=10
for n>0 { // 替代while(n>0){} 和 for (; n > 0;) {}
n--
}
//形式3:for{} 相当于while(true){} 和 for(;;){}
for {
}
range循环语句
//for range相比较for循环区别有两点:(1)遍历key为string类型的map并同时获取key和value;
// (2)遍历channel
for key, value := range oldMap {
newMap[key] = value
}
goto跳转语句
跳转完就不回来了执行原来语句了
package main
import "fmt"
func controlFn(){
for i := 0; i < 5 ; i++ {
if i==2{
goto gotoHere //当i为2时goto语句跳转到gotoHere标签处并终止for循环
}
if i==4{
goto breakHere // 不会执行,i==2时使用goto会退出for
}
fmt.Println(i)
}
//标签
gotoHere:
fmt.Println("标签") // 标签
breakHere:
fmt.Println("")
}
效果
break、continue循环控制语句
package main
import "fmt"
func main(){
for i := 0; i < 5 ; i++ {
if i==2{
continue
}
if i==4{
break
}
fmt.Println(i)
}
}
效果
11. 下面我们来试试字节MarsCode,一起coding看看
案例一、百万负翁
案例二、算一下领导画的饼的周长和你做不完deadline的心里阴影面积
案例三、编写一个初中生写的冒泡排序
那么简单的题目你有没有oc呢,如果oc的请在评论区打出非常开门