Go编程语言:支持并发、垃圾回收的编译型系统级编程语言!本文主要是按照无闻的《Go 编程基础》开源视频学习并记录笔记。
一、go语言常用命令
go get 获取远程包(需提前安装git)
go run 运行
go build 测试编译(package main 的文件)
go fmt 格式化代码
go install 编译包文件和整个程序
go test 运行测试文件(**_test.go是测试文件,命名自己文件的时候需要注意)
go doc 查看文档(本地官网 godoc -http=:8080 & 后台执行)
我们可以将go的官网放到本地:
godoc -http=:8080
然后我们可以访问本地8080端口:
Go环境变量与工作目录:GOPATH下需要建立 3个目录(bin存放编译后生成的可执行文件;pkg存放编译后生成的包文件;src存放项目源码)
二、go 语法结构
1、Go导入package的格式
package main包含main函数一个程序有且只有一个main包和一个main函数
package 要放在非注释的第一行
package 后加import,import 引入非main包
一般建议package的名称和目录名一致 pacage 名称不要使用驼峰标记法,使用下划线
var 全局变量
type 基本类型 比如:type newtype struct/interface{(内容)}
改包名:
import abc "fmt"/import . "fmt"
调用的时候
abc.Println/Println
不推荐使用.那种,容易混淆
只有
package
为main
的包可以包含main函数,一个可执行程
test.go
package ok
// package main 这里故意将main 包名写错
import "fmt"
func main(){
fmt.Println("Hello, World")
}
如果我们运行的程序里边没有 package main
包,则执行会报错;➜ src go run myfirstgo/test.go go run: cannot run non-main package
- 导入包之后,就可以使用格式<PackageName>.<FuncName>
- 如果导入包之后
未调用
其中的函数
或类型
将会报出编译错误
import and not used:"io";
2、package别名
当使用第三方包时,包名可能会非常接近或相同,此时就可以使用别名来进行区别和调用
import "fmt"
// 使用别名
import std "fmt"
std.Println("Hello,world")
省略调用
// 省略调用
import . "fmt"
Println("Hello,world")
注意点:
1.不建议使用
省略调用,易混淆
2.不可以
和别名同时使用
3.可见性规则
Go语言中,使用 大小写
来决定该常亮、变量、类型、接口、结构或函数,是否可以被外部包所调用:
根据约定,函数名
首字母小写
即为private
func getField(){
// ...
}
函数名
首字母大写
即为public
func Print(){
// ...
}
完整示例:
package main
// 导入其他的包
import "fmt" // 第一种导入方法
/*
import{
"fmt"
"io"
"os"
}
*/
// 常量的定义
const PI = 3.14
// 全局变量的声明与赋值
var name = "gopher"
// 一般类型声明
type newType int
// 结构的声明
type gopher struct{}
// 接口的声明
type golang interface{}
// 由 main 函数作为程序入口点启动
func main(){
fmt.Println("Hello, World")
}
牛刀小试:
既然导入多个包可以进行简写,那么声明多个常亮、全局变量或一般类型(非接口、非结构)是否也可以使用同样的方法呢?
定义常亮:
const(
PI = 3.14
const1 = 1
Radis = 10
)
全局变量的声明与赋值:
var (
name = "corwien"
age = 20
address = "guangzhou"
)
一般类型声明:
type (
newType int
type1 float32
)
三、类型与变量
1.类型
1、布尔类型:bool
长度:1字节
取值范围:true,false
注意事项:不可以用数字代表true或false
2、整型:int/uint
-根据运行平台可能为32或64位
3、8位整型:int8/uint8
-长度:1字节
-取值范围:-128~127/0~255
4、字节型:byte(uint8别名
)
5、16位整型:int16/uint16
-长度:2字节
-取值范围:-32768~32767/0~65565
6、32位整型:int32(rune)/uint32
-长度:4字节
-取值范围:-32768~32767/0~65565
7、浮点型:float32/float64
-长度:4/8字节
-小数位:精确到7/15小数位
8、复数:complex64/complex128
-长度:8/16字节
足够保存指针的32、64位整数型:uintptr
9、其他值类型:
- array、struct、string
10、引用类型:
- slice,map,chan
11、接口类型:interface
12、函数类型:func,可以赋值给变量
类型零值
零值并不等于空值,而是当变量被声明为某种类型后的默认值,通常情况下值类型的默认值为0,bool为false, string为空字符串
类型别名
type (
byte int8
rune int32
文本 string
)
// 使用中文类型,牛逼啊
var b 文本
b = "你好哈亲"
2.变量
2.1 单个变量的声明与赋值
- 变量的声明格式:
var <变量名称><变量类型>
- 变量的赋值格式:
<变量名称> = <表达式>
- 声明的同时赋值:
var <变量名称>[变量类型] = <表达式>
示例:
var a int // 变量的声明
a = 123 // 变量的赋值
// 变量声明的同时赋值
var a int = 123
// 上行的格式可省略变量类型,由系统推断
var c = 123
// 变量声明与赋值的最简写法
d := 678
d := 678 冒号的作用就是代替关键字 var 的
2.2 多个变量的声明与赋值
- 全局变量的声明可以使用
var()
的方式进行简写 - 全局变量的声明不可以省略var,但可以使用
并行
方式 所有变量都可以使用类型推断
局部变量不可以使用var()的简写,只能使用并行方式
举例:
var(
// 使用常规方式
a = 'hello'
// 使用并行方式以及类型推断
b,c = 1,2
// d := 3 // 不可以省略 var
)
多个变量的声明
// 多个变量的声明
var a, b, c int
// 多个变量的赋值
a, b, c = 1, 2, 3
// 多个变量声明的同时赋值
var d, e, f int = 4, 5, 6
// 省略变量类型,有系统推断
var g, h, m = 7, 8, 9
// 多个变量声明与赋值的最简写法
n, o, p := 10, 11, 12
2.3 变量的类型转换
Go中不存在隐式转换,所有类型转换必须显示声明
- 转换
只能发生在两种相互兼容的类型
之间 - 类型转换的格式:
// 当变量未在前边申明过,则需要冒号:,如果已经声明过,则不需要冒号
<ValueA>[:] = <TypeOfValueA>(<ValueB>)
示例:
// 在相互兼容的两种类型之间转换
var a float32 = 1.1
b := int(a)
// 以下表达式无法通过编译
var c bool = true
d := int(c)
全局变量的声明不可以省略var 局部变量不可以使用var()的方式简写
思考
尝试运行下边的代码,开会发生什么,并思考为什么?
// 变量类型转换,整数转为字符,会发生什么呢?
func main() {
var a int = 65
b := string(a)
fmt.Println(b)
}
四、常量与运算符
1、常量的定义
- 常量的值在编译时就已经确定
- 常量的定义格式与变量基本相同
- 等号右侧必须是常量或者常量表达式
// 定义单个常量
const a int = 1
const b = 'A'
const {
text = "123"
length = len(text)
num = b * 20
}
// 同时定义多个变量
const i, j, k = 1, "2", "3"
const(
text2, length2, num2 = "234", len(text2), k * 10
)
2、常量的初始化规则与枚举
- 在定义常量组时,如果不提供初始值,则表示将使用上行的表达式
- 使用相同的表达式不代表具有相同的值
-
iota
是常量的计数器,从0开始,组中每定义1个常量自动递增1 - 通过初始化规则与
iota
可以达到枚举的效果 - 每遇到一个 const 关键字,iota就会重置为0
示例:
const (
// a与b都为"A"
a = "A"
b
c = iota
d // d的值为3
)
const(
e = iota
f // f 的值为1
)
// 星期枚举
const(
// 第一个常量不可省略表达式
Monday = iota
Tuesday
Wednesday
Thursday
Firday
Saturday
Sunday
)
3、运算符
- Go中的运算符均是从左至右结合
优先级(从高到底) - ^ ! (一元运算符)
- * / % << >> & &^
- + – | ^ (二元运算符)
- == != < <= >= >
- <- (专门用于channel)
- &&
- ||
4、指针
Go虽然保留了指针,但与其他编程语言不同的是,在Go当中不支持指针运算以及“->”运算符,而直接采用“.”选择符来操作指针目标对象的成员
- 操作符“&”取变量地址,使用“*”通过指针
间接
访问目标对象 - 默认值为
nil
而非NULL
func main() {
a := 1
var p *int = &a;
// fmt.Println(p) 打印地址:0xc42000e228
fmt.Println(*p) // 打印结果: 1
}
5、递增递减语句
在Go当中, ++ 与 — 是作为语句而并不是作为表达式
五、控制语句
1、判断语句if
-
条件表达式没有括号
,这和其他语言不太一样 - 支持一个初始化表达式(可以是并行方式)
左大括号必须和条件语句或else在同一行
- 支持单行模式
- 初始化语句中的变量为block级别,同时隐藏外部同名变量
- 1.0.3版本中的编译器Bug
示例:
package main
import "fmt"
func main() {
a := true
// 初始化变量,在块里边初始化,作用域只是在该区块中
if a, b, c := 1, 2, 3; a+b+c > 6 {
fmt.Println("大于6")
fmt.Println(a)
} else {
fmt.Println("小于等于6")
fmt.Println(a)
}
fmt.Println(a)
}
打印结果:
➜ myfirstgo go run if.go
小于等于6
1
true
2、循环语句for
- Go 只有
for
一个循环语句关键字,但支持3种形式 - 初始化和步进表达式可以是多个值
- 条件语句每次循环都会被重新检查,因此不建议在条件语句中使用函数,尽量提前计算好条件并以变量或常量代替
左大括号必须和条件语句在同一行
for 循环的三种形式:
1.最简单的
func main() {
a := 1
for {
a++
if a > 3 {
break
}
}
fmt.Println(a)
}
2、条件语句判断
func main() {
a := 1
for a <= 3 {
a++
}
fmt.Println(a)
}
3、最常用的一种方式
func main() {
a := 1
for i := 0; i < 3; i++ {
a++
}
fmt.Println(a)
}
3、选择语句switch
- 可以使用
任何类型
或表达式
作为条件语句 不需要写break,一旦条件符合自动终止
- 如希望继续执行下一个case,需使用
fallthrough
语句 - 支持一个初始化表达式(可以是并行方式),右侧需跟分号
- 左大括号必须和条件语句在同一行
示例:
// switch
func main() {
a := 1
switch a {
case 0:
fmt.Println("a=0")
case 1:
fmt.Println("a=1")
default:
fmt.Println("Nothing")
}
fmt.Println(a)
}
func main() {
a := 1
switch {
case a >= 0:
fmt.Println("a=0")
fallthrough
case a >= 1:
fmt.Println("a=1")
}
fmt.Println(a)
}
func main() {
switch a := 1; {
case a >= 0:
fmt.Println("a=0")
fallthrough
case a >= 1:
fmt.Println("a=1")
}
}
4、跳转语句goto,break,continue
- 三个语法都可以配合
标签
使用 标签名区分大小写,若不使用会造成编译错误
- Break与continue配合标签可用于多层循环的跳出
- Goto 是调整执行位置,与其他2个语句配合标签的结果并不相同
示例:
func main() {
// 标签
LABLE:
for {
for i := 0; i < 10; i++ {
if i > 3 {
break LABLE
}
}
}
fmt.Println("ok")
}
func main() {
LABLE:
for i := 0; i < 10; i++ {
for {
continue LABLE
fmt.Println(i)
}
}
fmt.Println("ok")
}
六、语言范围 Range
Go 语言中 range
关键字用于for循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引值,在集合中返回 key-value 对的 key 值。
示例:
package main
import "fmt"
func main() {
//这是我们使用range去求一个slice的和。使用数组跟这个很类似
nums := []int{2, 3, 4}
sum := 0
for _, num := range nums {
sum += num
}
fmt.Println("sum:", sum)
//在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。
for i, num := range nums {
if num == 3 {
fmt.Println("index:", i)
}
}
//range也可以用在map的键值对上。
kvs := map[string]string{"a": "apple", "b": "banana"}
for k, v := range kvs {
fmt.Printf("%s -> %s\n", k, v)
}
//range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
for i, c := range "go" {
fmt.Println(i, c)
}
}
运行结果:
sum: 9
index: 1
a -> apple
b -> banana
0 103
1 111