1、Go简介
Go是2009年开源的编程语言,Go语言具有以下特性:语法简洁、并发编程、编译迅速、数组安全、丰富的内置类型、错误处理、匿名函数和闭包、反射、函数多返回值、自动垃圾回收。
2、Go语言安装与配置
Go语言支持以下系统:Linux、Mac、Windows。
安装包下载地址:https://golang.org/dl/
如果打不开可以使用这个地址:https://golang.google.cn/dl/
下面介绍Windows和Linux系统的安装:
2.1、Windows系统下安装:
可以直接点击.msi进行下载go1.19.1.windows-amd64.msi,下载完后打开msi,进行安装,安装目录可以使用默认,也可以自己配置,安装目录不可以有中文。然后配置系统环境变量:添加GOROOT,值为Go的安装目录,例如D:\Go。然后可以在cmd里执行go version,如果出现类似输出:go version go1.19.1 windows/amd64,说明安装配置成功,接下来就可以进行开发运行go程序了。
2.2、Linux系统下的安装:
2.2.1 下载二进制包:go1.19.1.linux-amd64.tar.gz
2.2.2 将下载的二进制包解压至/usr/local目录
tar -C /usr/local -xzf go1.19.1.linux-amd64.tar.gz
2.2.3 将 /usr/local/go/bin 目录添加至 PATH 环境变量:
export PATH=$PATH:/usr/local/go/bin
以上只能暂时添加 PATH,关闭终端然后下次再登录又没有了。
可以编辑 ~/.bash_profile 或者 /etc/profile,并将以下命令添加该文件的末尾,这样就可以永久生效:
export PATH=$PATH:/usr/local/go/bin
添加后需要执行:
source ~/.bash_profile
或者
source /etc/profile
3、Go语言开发结构与流程
以下程序是在windows系统的VS Code编辑器开发的,安装Go扩展可以方便开发。
Go语言程序的基本结构有以下几个组成:包声明、引入包、函数、变量、语句、表达式、注释。
以下是一个输出Hello, World!的main.go程序:
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}
- 包声明,编写源文件时,必须在非注释的第一行指明这个文件属于哪个包,如package main。
- 引入包,其实就是告诉Go 编译器这个程序需要使用的包,如import "fmt"其实就是引入了fmt包。
- 函数,和c语言相同,即是一个可以实现某一个功能的函数体,每一个可执行程序中必须拥有一个main函数。
- 变量,Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
- 语句/表达式,在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go
编译器自动完成。 - 注释,和c语言中的注释方式相同,可以在任何地方使用以 // 开头的单行注释。以 /* 开头,并以 */
结尾来进行多行注释,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
需要注意的是:标识符是用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母和数字、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。
1. 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);
2. 标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected)。
go run main.go
输出Hello, World!。
可以使用go build 命令来生成二进制文件:
go build main.go
4、Go语言的基础定义
4.1、标识符:
Go语言标识符与其它语言类似,第一个字符必须是字母、下划线,不能是数字。
4.2、关键字
Go语言的25个关键字或者保留字:
var, type, switch, struct, select, return, range, package, map, interface, if, import, goto, go, func, for, fallthrough, else, default, defer, const, case, continue, chan, break.
Go语言的36个预定义标识符:
append, bool, byte, cap, close, complex, complex64, complex128, uint16, copy, false, float32, float64, imag, int, int8, int16, uint32, int32, int64, iota, len, make, new, nil, panic, uint64, print, println, real, recover, string, true, uint, uint8, uintptr.
4.3、注释:
// 单行注释
/*
xxxxx
多行注释
*/
5、Go语言的数据类型
Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。有如下类型:
5.1、数字类型
整型 int 和浮点型 float32、float64,Go 语言支持整型、浮点型数字和支持复数。
5.2、布尔类型
布尔型的值只可以是常量 true 或者 false。例子:var b bool = true。
5.3、字符串类型:
字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的。Go语言的字符串的字节使用UTF-8编码标识Unicode文本。
类型 | 详解 |
---|---|
布尔型 | 布尔型的值只可以是常量 true 或者 false。 |
数字类型 | 整型 int 和浮点型 float。Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
字符串类型 | 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
派生类型 | (a) 指针类型(Pointer)(b) 数组类型© 结构化类型(struct)(d) Channel 类型(e) 函数类型(f) 切片类型(g) 接口类型(interface)(h) Map 类型 |
6、Go语言变量与常量
6.1、声明变量与变量赋值
声明变量的一般形式是使用 var 关键字,具体格式为:var identifier typename
。如下的代码中我们定义了一个类型为int的变量。
package main
import "fmt"
func main() {
var a int = 27
fmt.Println(a);
}
6.1.1 如果变量没有初始化
在go语言中定义了一个变量,指定变量类型,如果没有初始化,则变量默认为零值。零值就是变量没有做初始化时系统默认设置的值。
类型 | 零值 |
---|---|
数值类型 | 0 |
布尔类型 | false |
字符串 | “”(空字符串) |
6.1.2、如果变量没有指定类型
在go语言中如果没有指定变量类型,可以通过变量的初始值来判断变量类型。如下代码
// 声明变量的一般形式是使用 var 关键字:
var variable type
// 也可以一次声明多个变量:
var variable1, variable2 type
// 声明变量时也可以赋值
var temp string = "ssss"
var a, b int = 1, 2
声明了变量并且赋值了,也可以不用写类型。
如果声明了变量没有赋值,那么变量的值为零值。比如var a bool的零值为false。
下面是一些类型数据的零值:
- 数值类型(包括complex64/128)为0
- 布尔类型为 false
- 字符串为 “”
- 以下几种类型零值为nil:
var a *int
var a []int
var a map[string] int
var a chan int
var a func(string) int
var a error // error 是接口
6.1.3、声明变量更加简洁的方式(:=符号):
当我们定义一个变量后又使用该符号初始化变量,就会产生编译错误,因为该符号其实是一个声明语句。
使用格式:typename := value
也就是说intVal := 1
相等于:
a := 1
相当于:
var a int
a = 1
6.1.4、声明全局变量和函数变量
ackage main
var ( // 这种因式分解关键字的写法一般用于声明全局变量
a int
b bool
)
// 这种不带声明格式的只能在函数体中出现
// c, d := 3123, "hello"
func main(){
c, d := 3123, "hello"
}
6.1.5、(多变量声明):
可以同时声明多个类型相同的变量(非全局变量)
var x, y int
var c, d int = 1, 2
g, h := 123, "hello"
关于全局变量的声明如下:
var ( vname1 v_type1 vname2 v_type2 )
具体举例如下:
var (
a int
b bool
)
6.1.6、 匿名变量
匿名变量的特点是一个下画线_
,这本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。
使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。
示例代码如下:
func GetData() (int, int) {
return 10, 20
}
func main(){
a, _ := GetData()
_, b := GetData()
fmt.Println(a, b)
}
需要注意的是匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。
6.2、常量
常量是在程序运行时不会被修改的量。常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式如下:
const variable [type] = value
你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型
const a string = "abc"
const a = "abc"
多个相同类型的声明可以简写为:
const c1, c2 = value1, value2
常量可以用len()等内置函数计算表达式的值,在常量表达式中,函数必须是内置函数,否则编译不通过
ackage main
const (
a = "abc"
b = len(a)
)
func main(){
println(a, b)
}
iota是一种特殊常量,可以认为是一个可以被编译器修改的常量。iota在const关键字出现时将被重置为0,const中每新增一行常量声明,iota计数一次。
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
d = "hh" //独立值,iota += 1
e //"ha" iota += 1
f = 312 //iota +=1
g //312 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i) // => 0 1 2 hh hh 312 312 7 8
}
6.3、变量作用域
作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。
Go语言中变量可以在三个地方声明:
- 函数内定义的变量(局部变量):作用域只在函数体内,参数和返回值变量也是局部变量。这种局部变量的作用域只在函数体内,函数的参数和返回值变量都属于局部变量。这种变量在存在于函数被调用时,销毁于函数调用结束后。
- 函数外定义的变量(全局变量):在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用。全局变量只需要在一个源文件中定义,就可以在所有源文件中使用,甚至可以使用import引入外部包来使用。全局变量声明必须以 var 关键字开头,如果想要在外部包中使用全局变量的首字母必须大写。
- 函数的形式参数:形式参数可以看作函数的局部变量。定义函数时函数名后面括号中的变量叫做形式参数(简称形参)。形式参数只在函数调用时才会生效,函数调用结束后就会被销毁,在函数未被调用时,函数的形参并不占用实际的存储单元,也没有实际值。形式参数会作为函数的局部变量来使用。
7、指针
与C相同,Go语言让程序员决定何时使用指针。变量其实是一种使用方便的占位符,用于引用计算机内存地址。Go 语言中的的取地址符是&
,放到一个变量前使用就会返回相应变量的内存地址。
指针变量其实就是用于存放某一个对象的内存地址。
7.1、指针声明和初始化
和基础类型数据相同,在使用指针变量之前我们首先需要申明指针,声明格式如下:var var_name *var-type
,其中的var-type 为指针类型,var_name 为指针变量名,* 号
用于指定变量是作为一个指针。
代码举例如下:
var ip *int /* 指向整型*/
var fp *float32 /* 指向浮点型 */
指针的初始化就是取出相对应的变量地址对指针进行赋值,具体如下:
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */
ip = &a /* 指针变量的存储地址 */
7.2、空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil
,也称为空指针。它概念上和其它语言的null、NULL一样,都指代零值或空值
8、Go语言运算符
Go语言运算符与java等语言基本一样,均有:算术运算符、关系运算符、逻辑运算符、赋值运算符、位运算符。
比较例外的是,Go语言有与C/C++类似的指针相关运算符:
运算符 | 描述 | 示例 |
---|---|---|
* | 指针变量 | *a是一个指针变量 |
& | 返回变量存储地址 | &a: 变量的存储地址 |
package main
import "fmt"
func main() {
var a int = 22
var ptr *int
// ptr变量存储了a的地址
ptr = &a
fmt.Printf("a的值为 %d\n", a);
fmt.Printf("ptr的值为 %s\n", ptr);
fmt.Printf("*ptr的值为 %d\n", *ptr);
}
输出:
a的值为 22
ptr的值为 %!s(*int=0xc00000e0a8)
*ptr的值为 22
9、Go语言函数
为完成某一功能的程序指令(语句)的集合,称为函数。Go语言程序至少有一个main函数。
9.1 函数分类
在Go语言中,函数是第一类对象,我们可以将函数保持到变量中。函数主要有具名和匿名之分,包级函数一般都是具名函数
,具名函数是匿名函数的一种特例,当匿名函数引用了外部作用域中的变量时就成了闭包函数,闭包函数是函数式编程语言的核心。
举例代码如下:
9.1.1、具名函数:就和c语言中的普通函数意义相同,具有函数名、返回值以及函数参数的函数。
func Add(a, b int) int {
return a+b
}
9.1.2、匿名函数:指不需要定义函数名的一种函数实现方式,它由一个不带函数名的函数声明和函数体组成。
var Add = func(a, b int) int {
return a+b
}
解释几个名词如下:
闭包函数:
返回为函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域。
一级对象:
支持闭包的多数语言都将函数作为第一级对象,就是说函数可以存储到变量中作为参数传递给其他函数,最重要的是能够被函数动态创建和返回。
包:
go的每一个文件都是属于一个包的,也就是说go是以包的形式来管理文件和项目目录结构的。
9.2 函数声明和定义
Go 语言函数定义格式如下:
func fuction_name([parameter list])[return types]{
函数体
}
示例
package main
import "fmt"
func main() {
var a int
a = sum(1, 2)
fmt.Printf("a的值为 %d\n", a);
}
func sum(a, b int) int {
var c int
c = a + b
return c
}
解析 | |
---|---|
func | 函数由func开始声明 |
function_name | 函数名称 |
parameter list | 参数列表 |
return_types | 返回类型 |
函数体 | 函数定义的代码集合 |
9.3 函数传参
Go语言中的函数可以有多个参数和多个返回值,参数和返回值都是以传值的方式和被调用者交换数据。在语法上,函数还支持可变数量的参数,可变数量的参数必须是最后出现的参数,可变数量的参数其实是一个切片类型的参数。
当可变参数是一个空接口类型时,调用者是否解包可变参数会导致不同的结果,我们解释一下解包的含义,代码如下:
func main(){
var a = []int{
1, 2, 3}
Print(a...) // 解包
Print