GO语言学习笔记
环境准备
Windows安装GO语言环境安装
安装包下载地址:https://golang.org/dl/,推荐选择Windows版本的msi格式安装包,简单些。
32位的电脑选择:go1.11.5.windows-386.msi
64位电脑选择:go1.11.5.windows-amd64.msi (111M)
GO语言中国社区:https://studygolang.com/dl
IDE我是使用jetbrain的GoLand,下载地址:https://www.jetbrains.com/go/whatsnew/ (220M),激活码生成方法:http://idea.lanyus.com/
优点是自动补全很好用,功能强大,但是很重。
比较轻量级的可以考虑VSCode,有相应的GO插件可以用
大概100M左右,下载完之后,双击安装即可,安装过程中可以选一个比较好的安装目录。
GO语言IDE选择
IDE我是使用jetbrain的GoLand,下载地址:https://www.jetbrains.com/go/whatsnew/ (220M),激活码生成方法:http://idea.lanyus.com/
GoLang的优点是自动补全很好用,功能强大,但是很重。
比较轻量级的可以考虑VSCode,有相应的GO插件可以用。
另外需要配置一下GoLang,进入GoLang,打开一个文件夹,比如Go_Projects。
- 修改主题颜色为黑色的方法:File -> Setting -> Appearance & Behavior -> Theme -> 默认是Intellij,黑色是Darcula
- 修改字体大小方法:File -> Setting -> Editor -> Font,在右边选项框中选择字体即可,推荐Console,18号
- 配置Go语言执行环境方法:File -> Setting -> Go -> GOROOT -> 选择GO语言的安装目录即可。
- 配置Go语言GOPATH:File -> Setting -> Go -> GOPATH -> 选择当前项目所在的路径即可。
测试环境:在GoLang LED中新建一个文件:hello.go,加入下面内容:
package main
import "fmt"
func main() {
/* 这是我的第一个简单的程序 */
fmt.Println("Hello, World!")
}
右键运行,如果在控制台打印出 hello,world!说明IDE和Go执行环境都已经准备完毕。
另外如果将Go的安装目录下的bin添加到系统环境变量(msi安装文件默认添加),可以执行下面命令运行go语言文件:
go run helll.go
基本语法
该部分介绍Go语言的一些基础用法,适用于有一定编程语言的同学。
Go程序结构
根据上面的hello.go文件,我们可以看出,一个Go语言执行文件包含下面几个部分:
package main
:指定当前文件所属的包名,package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。import "fmt"
: 表示导入fmt
的包,导入包后可以使用该包中的函数,fmt 包实现了格式化 IO(输入/输出)的函数func main()
:声明main()函数,表示该文件的执行入口,main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)
Go语言基础语法规则
- 语句分割:一行代表一个语句结束,不需要额外的分号,如果一行包含多个语句,则必须使用分号隔开,这种方式不推荐
- 注释:使用
//
进行单行注释,使用/* */
进行多行注释 - 标识符:字母(A~Z和a~z)数字(0~9)、下划线_组成,第一个字符必须是字母或下划线而不能是数字。
- 空格:空格不影响语句解析,变量声明必须使用空格隔开
fmt.Println("Hello") // 单行单条语句不需要分号
fmt.Println("Hello");fmt.Println(" world") // 单行多条语句需要分号分割
// 单行注释
/*
多行注释
多行注释
*/
// 定义变量
var age int
Go语言变量使用
Go语言变量定义语法:var identifier type
,定义方式包含三种:
- 指定变量类型,声明后若不赋值,使用默认值
- 根据值自行判定变量类型
- 省略var,使用 := 定义函数内变量
需注意:函数内变量定义后未使用会报编译错误,全局变量则不会报错
// 指定变量类型,声明后若不赋值,使用默认值。
var v_name v_type
v_name = value
// 根据值自行判定变量类型。
var v_name = value
// 省略var,使用 := ,此时变量不应该是已经声明过的,否则会导致编译错误
v_name := value
Go语言支持变量结构,即多个变量声明:
package main
var x, y int
var (
a int
b bool
) // 这种因式分解关键字的写法一般用于声明全局变量
var c, d int = 1, 2
var e, f = 123, "hello"
e += 12 // 使用变量
//这种不带声明格式的只能在函数体中出现
// g, h := 123, "hello"
func main() {
// 使用 := 定义函数内变量
// 如果定义var 1 int = 4, 不使用的话会报编译错误
g, h := 123, "hello"
println(x, y, a, b, c, d, e, f, g, h)
}
另外说明一下Go语言中变量的作用域,Go语言中变量的作用域为函数级。
- 函数内定义的变量称为局部变量:函数内作用域
- 函数外定义的变量称为全局变量:全局作用域
- 函数定义中的变量称为形式参数:函数内作用域
如果定义的变量没有初始化,则使用默认值
int
类型的初始值为:0float32
类型的初始值为:0string
类型的初始值为:””pointer
类型的初始值为:nil
Go语言常量使用
Go语言常量定义的语法:const identifier [type] = value
,一般常量所有字母大写,定义方式如下:
- 显式类型定义:const b string = “abc”
- 隐式类型定义:const b = “abc”
- 多个常量定义:const c_name1, c_name2 = value1, value2
- 枚举定义方式:const ( Unknown = 0; Female = 1; Male = 2)
常量定义时可以使用函数,函数值应该是编译是能够确认的值。
package main
import "unsafe"
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
)
func main(){
println(a, b, c)
}
这里不得不说一个特殊常量iota
,这个常量由编译器来进行管理。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
// 第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1
const (
a = iota // 0
b // 1
c // 2
d = "ha" // 独立值,iota += 1
e // "ha" iota += 1
f = 100 // iota +=1
g // 100 iota +=1
h = iota // 7,恢复计数
i // 8
)
fmt.Println(a,b,c,d,e,f,g,h,i) // 常量可以直接使用
Go语言运算符
Go语言的运算符和PHP等语言类似,包括:
- 算术运算符: +、-、*、/、%、++、–
- 关系运算符: ==、!=、>、<、>=、<=
- 逻辑运算符: &&、||、!
- 位运算符: &、|、^、<<、>>
- 赋值运算符: =、+=、-=、*=、/=、%=、<<=、>>=、&=、^=、!=
- 指针运算符: &(返回变量存储地址)、*(指针变量)
Go语言中有指针的概念,这里特别说明两个指针操作运算符
var a int = 4
var ptr *int
ptr = &a // 取a变量的地址并复制给指针ptr
*ptr = 20 // 修改ptr指针地址所指向的内存空间,相当于修改a=20,此时a=20
Go语言流程控制: if else
Go语言中的流程控制条件不需要括号,条件语句语法:
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
} else {
/* 在布尔表达式为 false 时执行 */
}
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
} else if 布尔表达式 {
/* 在布尔表达式为 false 时执行 */
} else {
/* 在布尔表达式为 false 时执行 */
}
if 布尔表达式 1 {
/* 在布尔表达式 1 为 true 时执行 */
if 布尔表达式 2 {
/* 在布尔表达式 2 为 true 时执行 */
}
}
条件语句实例如下:
package main
import "fmt"
func main() {
var age int = 23
if age == 25 {
fmt.Println("true")
} else if age < 25 {
if age == 0 {
fmt.Println("zero")
} else {
fmt.Println("too small")
}
} else {
fmt.Println("too big")
}
}
Go语言流程控制: switch
switch语法中,区别于其他如PHP等语言,需注意以下几点
- 默认每个case后面的case不执行,除非使用
fallthrough
关键字强制执行后面的 case 语句 - 使用
fallthrough
关键字执行下一条匹配的case - 使用case val1, val2, val3的方式多个case执行相同代码
package main
import "fmt"
func main() {
/* 定义局部变量 */
var grade string = "B"
var marks int = 90
var x interface{}
switch marks {
case 90: grade = "A"
case 80: grade = "B"
case 50,60,70 : grade = "C"
default: grade = "D"
}
// 可以省略switch后面的判断变量
switch {
case marks == 90 : grade = "A"
case marks == 80 : grade = "B"; fallthrough // 强制执行下一个case,即当marks=80时,grade="C"
case 50,60,70 : grade = "C"
default: grade = "D"
}
// 另外switch可以用来进行类型处理
var x_type = ""
switch i := x.(type) {
case nil: x_type = "nil"
case int: x_type = "int"
case float64: x_type = "float64"
case func(int) float64: x_type = "func(int) float64"
case bool, string: x_type = "bool or string"
default: x_type = "unknown"
}
}
另外Go语言中还有特有的类似于用于通信的switch语句,select
语法,此次不做介绍。
Go语言流程控制: for
Go语言中循环语句值包括for
,另外还有break
,continue
,goto
循环控制。
Go语言中for
循环有三种形式:
- 带初始条件:for init; condition; post { }
- 只有循环终止条件:for condition { }
- 永久循环:for { }
其中:
- init: 一般为赋值表达式,给控制变量赋初值;
- condition: 关系表达式或逻辑表达式,循环控制条件;
- post: 一般为赋值表达式,给控制变量增量或减量。
下面是一个示例:
package main
import "fmt"
func main() {
var b int = 15
var a int
numbers := [6]int{1, 2, 3, 5}
/* for 循环 */
for a := 0; a < 10; a++ {
fmt.Printf("a 的值为: %d\n", a)
}
for a < b {
a++
fmt.Printf("a 的值为: %d\n", a)
}
for i,x:= range numbers {
fmt.Printf("第 %d 位 x 的值 = %d\n", i,x)
}
}
Go语言数据类型
概述
在 Go 编程语言中,数据类型用于声明函数和变量,Go语言是一门强类型语言。Go语言的数据类型可分为以下几个大类。
类型 | 描述 |
---|---|
布尔型 | 常量 true 或者 false |
数字类型 | 整型,浮点型,Go语言支持复数,采用补码运算 |
字符串类型 | 单个字节连接,使用 UTF-8 编码标识 Unicode 文本,字符串只能使用双引号表示 |
派生类型 | 包括指针类型,数组类型等 |
数字类型
数字类型包括整数和浮点数,如下:
类型标识 | 描述 |
---|---|
int | 32或64位整型(根据平台觉得是32位还是64位) |
uint | 和int一样 |
uintptr | 无符号整型,用于存放一个指针 |
byte | 8个字节 |
rune | 32个字节 |
uint8 | 无符号8位整型 |
uint16 | 无符号16位整型 |
uint32 | 无符号32位整型 |
uint64 | 无符号64位整型 |
int8 | 有符号8位整型 |
int16 | 有符号16位整型 |
int32 | 有符号32位整型 |
int64 | 有符号64位整型 |
float32 | EEE-754 32位浮点型数 |
float64 | IEEE-754 64位浮点型数 |
complex64 | 32位实数和虚数 |
complex128 | 64位实数和虚数 |
派生类型
派生类型包括下面几种:
- 指针类型
- 数组类型
- 结构化类型
- Channel类型
- 函数类型
- 切片类型
- 接口类型
- Map类型
Go语言类型转换
Go语言类型转换很简单:type_name(expression)
比如将float32转换为int:var f int = int(10.65)
值类型和引用类型
所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值。
当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝
可以通过 &i 来获取变量 i 的内存地址,一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。
当使用赋值语句 r2 = r1 时,只有引用(地址)被复制(浅拷贝),如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。
Go语言函数
Go语言函数定义
每个Go语言文件最少有个 main() 函数。Go 语言函数定义格式如下:
func function_name( [parameter list] ) [return_types] {
函数体
}
- func:函数由 func 开始声明
- function_name:函数名称
- parameter list:参数列表,可选,Go语言中默认使用值传递参数
- return_types:返回类型,没有return时可以不用写该项
- 函数体:函数定义的代码集合
实例如下
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 声明局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
函数调用
传入参数即可调用函数。
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int = 200
var ret int
/* 调用函数并返回最大值 */
ret = max(a, b)
fmt.Printf( "最大值是 : %d\n", ret )
}
/* 函数返回两个数的最大值 */
func max(num1, num2 int) int {
/* 定义局部变量 */
var result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
函数可返回多个值
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Mahesh", "Kumar")
fmt.Println(a, b)
}
函数的附加说明
Go语言中的函数功能非常强大,包括以下几点:
- 可以使用函数作为参数传递,用于回调函数等场景
- 可以返回匿名函数,用作函数闭包
- 可以和对象绑定,用作方法
Go语言指针
和C语言一样,Go语言还支持指针。一个指针变量指向了一个值的内存地址。
Go语言指针声明
指针的声明很简单,只需要在你声明的类型前面加上*
即可:var var_name *var-type
// 声明一个整数类型的指针:该地址指向一个整型
var ip *int
// 声明一个整数类型的指针:该地址指向一个浮点型
var fp *float32
如果只是声明指针没有赋值,则指针的值为空指针nil
。
Go语言指针使用
指针的操作主要有取地址操作符:&
和取值操作符:.
package main
import "fmt"
func main() {
var num int = 20 // 声明实际变量
var numPtr *int // 声明指针变量
numPtr = &num // 指针变量的存储地址
fmt.Println("num: ", num) // 20
fmt.Println("numPtr: ", numPtr) // 内存地址:0xc00000a0e0
*numPtr = 1 // 修改指针指向的内存区域的值,相当于修改 num=1
fmt.Println("numPtr: ", *numPtr) // 1
}