- 是一门编译型语言,运行效率高,开发高效,部署简单;语言层面支持并发,易于利用多核实现并发;内置runtime(作用:性能监控,GC等);简单易学,丰富的标准库,强大的网络库;内置强大的工具(gofmt),跨平台编译,内嵌C支持。
- 应用:服务器编程,分布式系统 ,网络编程,云平台(docker)
- 命令行工具:
1、go build
:用于编译源码文件、代码包、依赖包;
2、go run
:可以编译并运行Go源码文件;
3、go get
:命令主要用动态获取远程代码包。
// 单行注释
/* 多行注释 */
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样
// 导入依赖包
import (
"fmt"
// "time" // 导入的包要使用,否则会产生编译错误
)
// 定义全局常量
const NAME string = "global const var"
// 定义全局变量
var tmp string = "global var"
// 一般类型声明
type countInt int
// 结构体的声明
type Learn struct {
}
// 声明接口
type Ilearn interface {
}
// 函数定义
func learnGo() {
fmt.Println("learn go!")
}
// 主函数
func main() {
fmt.Println("Hello World!!!")
fmt.Println(NAME)
fmt.Println(tmp)
learnGo()
}
- package是基本的分发单位和工程管理中依赖关系的体现。
- 每个Go语言源代码文件的开头都拥有一个
package声明
,表示源码文件所属代码包
;要生成Go语言可执行程序
,必须要有main的package包
,且必须在该包下有main()函数
; - 同一个路径下只能存在一个
package
,一个package可以拆分成多个源文件组成。 - import语句可以
导入源代码文件所依赖的package包
! - 导入的包要使用,否则会产生编译错误!
- 若导入的包中依赖其他包(包B),会首先导入包B,然后初始化包B中的常量和变量,最后若包B中有
init()函数
,会自动执行init()函数
,所有包导入完成后才会对main中的常量和变量进行初始化,然后执行main中的init()函数
(如果存在),最后才执行main函数。 - 若有一个包被导入多次,则该包只会被导入一次。
- 函数名
首字母要大写
! - 转义字符:
\r
表示回车,从当前行的最前面开始输出,覆盖掉旧的内容
。 -
格式化go源代码文件
:执行命令:gofmt -w 文件名.go
;或者先按组合键shift+tab
将代码整体移动到最左边,再选择某些代码块按tab
键适当缩进即可。 -
行长约定
:一行最长不超过80个字符,超过则用逗号隔开并换行展示!
错误记录
- 作为一个go的初学者,首先必须了解一个go项目的目录结构,例如我的一个测试目录如下:
workspace
|--bin
|--pkg
|--src
|---learn
|----learn.go
|---show
|----show.go
|--main.go
-
bin目录
存放编译后的可执行文件
;pkg目录
存放编译后的包文件(名称以.a为后缀的文件)
;src目录
存放项目源文件
。 - 一般地,bin和pkg目录可以不创建,go命令会自动创建(如
go install
),只需要创建src目录即可。 - 注意:某个.go文件import导入包的过程是先去
$GOROOT
下的src目录
找对应的包,找到则导入;若找不到,则去存放Go的项目路径$GOPATH
下的src目录找对应的包,找到则导入;若还找不到,则报错! - 刚开始还不知道go项目的目录结构时,我在main.go文件中导入learn和show包是这样的:
import (
"fmt"
"src/learn"
"src/show"
)
结果报如下错误,细细观察,通过查找和对比,可以发现上面讲的原理;
于是,把main.go文件中import包的代码改一下(去掉"src"):
import (
"fmt"
"learn"
"show"
)
再次键入执行命令:go run main.go
,结果就成功执行了!
learn.go的代码如下:
package learn
import (
"fmt"
"show"
)
func init() {
show.Show()
fmt.Println("learn init...")
}
func Learn() {
fmt.Println("learn 学习...")
}
show.go的代码如下:
package show
import (
"fmt"
)
func init() {
fmt.Println("show init...")
}
func Show() {
fmt.Println("show 展示...")
}
main.go的代码如下:
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样
// 导入依赖包
import (
"fmt"
// "time" // 导入的包要使用,否则会产生编译错误
"learn"
"show"
)
// 函数定义
func learnGo() {
fmt.Println("learn go!")
}
// init函数
func init() {
fmt.Println("main init...")
}
// 主函数
func main() {
show.Show()
learn.Learn()
learnGo()
}
- 键入
go env
可以查看go的配置:例如我的环境配置如下:
- import别名、
.
、_
1、import别名:
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样
// 导入依赖包
import (
zzz "fmt" // 包"fmt"的别名定义为zzz
// "time" // 导入的包要使用,否则会产生编译错误
"learn"
"show"
)
// 函数定义
func learnGo() {
zzz.Println("learn go!")
}
// init函数
func init() {
zzz.Println("main init...")
}
// 主函数
func main() {
show.Show()
learn.Learn()
learnGo()
}
2、点(.
)操作的含义是:点(.
)表示的包导入后,调用该包中函数时可以省略前缀包名;
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样
// 导入依赖包
import (
. "fmt" // 使用 . 操作,调用包"fmt"中的函数时,省略包名.
// "time" // 导入的包要使用,否则会产生编译错误
"learn"
"show"
)
// 函数定义
func learnGo() {
Println("learn go!") // 省略包名fmt.
}
// init函数
func init() {
Println("main init...")
}
// 主函数
func main() {
show.Show()
learn.Learn()
learnGo()
}
3、下划线(_
)操作的含义是:导入该包,但不导入整个包,而是执行该包的init函数
,因此无法通过包名来调用包中的其他函数。使用下划线(_
)操作往往是为了注册包里的引擎,让外部可以方便地使用。
- 代码包初始化函数即无参数声明和结果声明的init函数。
- init函数可以被声明在任何文件中,且可以有多个。
以上报错说明无法调用learn.go文件中的Learn()函数
,我们将main()函数中的代码learn.Learn()
注释掉,再用命令行执行main.go可以发现执行了learn.go中的init()函数
,说明_
操作只调用init()函数
,当然在执行init()函数
之前,会先初始化learn.go文件中的常量和变量。
- init函数的执行时机:单一代码包内,对所有全局变量进行求值,然后执行所有init函数,同一个代码包中多个init函数的执行顺序是不确定。
Go语言的基本数据类型
1、布尔型:只可以是常量 true 或者 false
,不能是其他类型。一个简单的例子:var b bool = true。
2、整数类型:整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。
1.uint8
:无符号 8 位整型 (0 到 255)
2.uint16
:无符号 16 位整型 (0 到 65535)
3.uint32
:无符号 32 位整型 (0 到 4294967295)
4.uint64
:无符号 64 位整型 (0 到 18446744073709551615)
5.int8
:有符号 8 位整型 (-128 到 127)
6.int16
:有符号 16 位整型 (-32768 到 32767)
7.int32
:有符号 32 位整型 (-2147483648 到 2147483647)
8.int64
:有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)
3、浮点数类型
1.float32
:IEEE-754 32位浮点型数
2.float64
:IEEE-754 64位浮点型数
3.complex64
:32 位实数和虚数
4.complex128
:64 位实数和虚数
4、字符串类型string
,编码统一为"UTF-8"。
5、其他数字类型
1.byte
:类似 uint8,无符号类型。
2.rune
:类似 int32,表示一个Unicode码。
3.uint
:32 或 64 位,其取决于操作系统的位数
4.int
:与 uint 一样大小,其取决于操作系统的位数
5.uintptr
:无符号整型,用于存放一个指针
- 查看某个变量所占的字节数:
unsafe.Sizeof(变量)
,导入import "unsafe"
包!
- 派生类型
1、指针类型(Pointer)
2、数组类型
3、结构化类型(struct)
4、Channel类型(chan)
5、函数类型(func)
6、切片类型(slice)
7、接口类型(interface)
8、Map类型(map)
- 类型零值和类型别名
1、类型零值不是空值,而是某个变量被声明后的默认值,一般情况下,值类型的默认值为0,布尔类型默认值为false,string默认值为空字符串。
2、类型别名:type 别名 = 数据类型
,例如:type D = int
3、类型声明:type 别名 数据类型
,例如:type d int
4、区别:类型声明将d定义为一个新的数据类型
,该类型拥有和int
一样的特性,但是两者是不同类型
,不能进行算术等运算
!而类型别名则将D定义为int整型的一个别名
,使用D和int相同。二者可以当作同一种运算。别名只在源码中存在,编译完成后,不会有别名类型
。 - 查看某个变量的类型:
reflect.TypeOf(变量名)
,导入import "reflect"
包。
变量与常量
- 单个变量的声明和赋值
1、变量的声明格式:var <变量名称> [变量类型]
2、变量的赋值格式:<变量名称> = <值,表达式,函数等>
3、声明和赋值同时进行:var <变量名称> [变量类型] = <值,表达式,函数等>
4、分组声明格式:
var(
a int
b float32
name string
)
5、同一行声明多个变量和赋值:var a, b, c int = 1, 2, 3
或者a, b := 1, 2
。注意::=
是声明并赋值;系统会自动推断类型;不需要var关键字;只能使用在局部变量。
6、全局变量必须使用var
关键字,局部变量则可以省略。
7、特殊变量下划线_
:相当于一个垃圾桶,把值赋值给下划线
,相当于把值丢进一个垃圾桶,以后都不再使用。
8、Go语言中不存在隐式转换,类型转换必须是显式转换。类型转换只能发生在两种兼容类型之间;类型转换格式:<变量名称> [:]= <目标类型>(<需要转换的变量>)
。
- 变量的可见性规则
1、大写字母开头的变量
是可导出的
,也就是其他包可以读取的,是公共变量
;
2、小写字母开头的变量
就是不可导出的
,是私有变量
。即导入的某个包,只能使用该包中的公有变量,不能使用私有变量。
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样
// 导入依赖包
import (
. "fmt"
"reflect"
)
var(
a uint
b float32
c bool
d string = "你好"
)
func main() {
var v1, v2, v3 = 4, 5, 1.222 // 可以声明每个变量的类型,省略的话--->系统自动推断类型
Println(reflect.TypeOf(v3)) // float64
Println(v1, v2, v3) // 4 5 1.222
Println(a, b, c, d) // 0 0 false 你好
c1, c2, c3 := 33, "string", 1.222 // 简写方式,系统自动推断类型,只能用在函数体内
Println(reflect.TypeOf(c2)) // string
Println(c1, c2, c3) // 33 string 1.222
// 下划线的使用
// var a1, _, a2 = 9, 8, 7
// Println(a1, _, a2) // 报错:
// # command-line-arguments
// .\main.go:31:9: cannot use _ as value
// 类型转换测试: 只能用在相同类型之间进行转换
var h float64 = 2.6555555
k := int64(h) // 括号不能省
Println(k) // 2,直接向下取整
Println(reflect.TypeOf(k)) // int64
}
- 常量定义从形式上分为显式和隐式:
1、显式:const identifier [type] = value
2、隐式:const identifier = value
(通常叫无类型常量) - 常量可以使用内置表达式定义:例如:
len()
、unsafe.Sizeof()
3、常量范围目前只支持布尔型,数字型(整数型、浮点型和复数)和字符串型。
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样
// 导入依赖包
import (
. "fmt"
"reflect"
)
const(
name string = "cat" // 显式定义
age = 12 // 隐式定义
)
const a, b, c = 1, 2, "我是中文"
const lenc = len(c) // 一个中文占三个字节,长度为12
func main() {
Println(name, age) // cat 12
Println(a, b, c) // 1 2 我是中文
Println(reflect.TypeOf(c)) // string
Println(lenc) // 12
}
- 特殊常量
iota
的使用
1、iota在const关键字出现时将被重置为0;
2、const中每新增一行常量声明将使iota计数一次。
3、iota常见用法:
1.跳值使用法;
2.插队使用法;
3.表达式隐式使用法;
4.单行使用法。
// 程序所属包,每个.go文件必须要有package关键字
package main // 包名尽量与文件目录名一样
// 导入依赖包
import (
. "fmt"
)
// iota在关键字出现时被重置为0
const a = iota // 0
const b = iota // 0
const(
c = iota
m = 2014 // 插入使用法,iota依然计数
d = iota
_ // 跳值使用法,下划线单独占用一行,iota依然计数
e = iota
)
const(
b1 = iota * 3
b2 // 自动使用向上第一个非空表达式,此时每增加一行常量const,iota计数加1,即b2 = 1 * 2
b3 // b3 = 2 * 3 = 6
)
const(
c1, c2 = iota + 1, iota * 3
c3, c4 // 沿用上面第一个非空的表达式,注意格式要一样,且const新增一行,iota计数器才会加1
c5 = iota // 否则就得赋予值
)
func main() {
Println(a, b) // 0 0
Println(c, d, e) // 0 2 4
Println(b1, b2, b3) // 0 3 6
Println(c1, c2, c3, c4, c5) // 1 0 2 3 2
}