The Go programming language is an open source project to make programmers more productive.
Go is expressive, concise, clean, and efficient. Its concurrency mechanisms make it easy to write programs that get the most out of multicore and networked machines, while its novel type system enables flexible and modular program construction. Go compiles quickly to machine code yet has the convenience of garbage collection and the power of run-time reflection. It's a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language.
var
定义,常量由const
定义
type
指定:=
简化版赋值,但变量不得再被var或const修饰,全局变量不可使用_
,代表不接收值,会被丢弃var variableName type = value // 变量
const Pi float32 = 3.1415926 // 常量
// 简化版
vname1, vname2, vname3 := v1, v2, v3
var (
home = os.Getenv("HOME")
user = os.Getenv("USER")
gopath = os.Getenv("GOPATH")
)
bool
,true或false,默认falseint
有符号 和 uint
无符号 ,分为8,16,32,64字节类型
rune
= int32, byte
= uint32float32
和 float64
complex128
(64位实数+64位虚数)string
error
用于处理异常信息,Go的package里面还专门有一个包errors来处理错误
err := errors.New("emit macho dwarf: elf header corrupted")
if err != nil {
fmt.Print(err)
}
// 方式一
arr := [10]int{}
arr[0] = 1
// 方式二
arr := [10]int{5,2,3,4}
格式map[keyType]valueType
// 声明一个key是字符串,值为int的字典,这种方式的声明需要在使用之前使用make初始化
numbers := make(map[string]int)
// 另一种map的声明方式
var numbers map[string]int
numbers["one"] = 1 //赋值
numbers["tow"] = 2 //赋值
numbers["three"] = 3 //赋值
map
是无序的,每次打印出来的map
都会不一样,它不能通过index
获取,而必须通过key
获取map
的长度是不固定的,也就是和slice
一样,也是一种引用类型len
函数同样适用于map
,返回map
拥有的key
的数量map
的值可以很方便的修改,通过numbers["one"]=11
可以很容易的把key为one
的字典值改为11
map
和其他基本型别不同,它不是thread-safe,在多个go-routine存取时,必须使用mutex lock机制// 初始化一个字典
rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map有两个返回值,第二个返回值,如果不存在key,那么ok为false,如果存在ok为true
csharpRating, ok := rating["C#"]
if ok {
fmt.Println("C# is in the map and its rating is ", csharpRating)
} else {
fmt.Println("We have no rating associated with C# in the map")
}
delete(rating, "C") // 删除key为C的元素
if x > 10 {
fmt.Println("x is greater than 10")
} else {
fmt.Println("x is less than 10")
}
// 计算获取值x,然后根据x返回的大小,判断是否大于10。
if x := computedValue(); x > 10 {
fmt.Println("x is greater than 10")
} else {
fmt.Println("x is less than 10")
}
if integer == 3 {
fmt.Println("The integer is equal to 3")
} else if integer < 3 {
fmt.Println("The integer is less than 3")
} else {
fmt.Println("The integer is greater than 3")
}
用goto跳转到必须在当前函数内定义的标签
func myFunc() {
i := 0
Here: //这行的第一个词,以冒号结束作为标签
println(i)
i++
goto Here //跳转到Here去
}
for expression1; expression2; expression3 {
//...
}
sum := 1
for ; sum < 1000; {
sum += sum
}
for sum < 1000 {
sum += sum
}
for index := 10; index>0; index-- {
if index == 5{
break // 或者continue
}
fmt.Println(index)
}
// range获取
for k,v:=range map {
fmt.Println("map's key:",k)
fmt.Println("map's val:",v)
}
替代多个if-else
switch sExpr {
case expr1:
some instructions
case expr2:
some other instructions
case expr3:
some other instructions
default:
other code
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kEFq1O8S-1681642454907)(./function-syntax.png)]
func
用来声明一个函数funcName
,
分隔// 多个返回值
func SumAndProduct(A, B int) (int, int) {
return A+B, A*B
}
Go函数支持变参...type
func myfunc(arg ...int) {}
*type
channel
,slice
,map
这三种类型的实现机制类似指针,所以可以直接传递,而不用取地址后传递指针。(注:若函数需改变slice
的长度,则仍需要取地址传递指针)// 传值参数
func add1(a int) int {
a = a+1 // 我们改变了a的值
return a //返回一个新值
}
// 传指针参数
func add1(a *int) int { // 请注意,
*a = *a+1 // 修改了a的值
return *a // 返回新值
}
func ReadWrite() bool {
file.Open("file")
defer file.Close()
if failureX {
return false
}
if failureY {
return false
}
return true
}
函数也是一种变量,可以通过type定义,它的类型就是所有拥有相同的参数,相同的返回值的一种类型
type typeName func(input1 inputType1 , input2 inputType2 [, ...]) (result1 resultType1 [, ...])
// 声明了一个函数类型
type testInt func(int) bool
func isOdd(integer int) bool {
if integer%2 == 0 {
return false
}
return true
}
func isEven(integer int) bool {
if integer%2 == 0 {
return true
}
return false
}
// 声明的函数类型在这个地方当做了一个参数
func filter(slice []int, f testInt) []int {
var result []int
for _, value := range slice {
if f(value) {
result = append(result, value)
}
}
return result
}
Init
init
能够应用于所有的package,main函数只能应用于package maininit
,但对于可读性和可维护性而言,建议每个文件只写一个init
函数init()
和 main() ,不需要任何地方调用这两个函数init
函数都是可选的,但package main就必须包含一个main函数Struct
类型声明新类型type,作为其他类型的属性或字段的容器
type person struct {
name string
age int
}
// 声明方式
var P person
// 按照顺序提供初始化值
P := person{"Tom", 25}
// 通过field:value的方式初始化,这样可以任意顺序
P := person{age:24, name:"Tom"}
// 也可以通过new函数分配一个指针,此处P的类型为*person
P := new(person)
实际上Go支持只提供类型,而不写字段名的方式,也就是匿名字段,也称为嵌入字段
type Human struct {
name string
age int
weight int
}
type Student struct {
Human // 匿名字段,那么默认Student就包含了Human的所有字段
speciality string
}
对象的函数
type Color byte
type Box struct {
width, height, depth float64
color Color
}
type BoxList []Box //a slice of boxes
// func (b Box) 代表函数属于哪个对象或struct的方法,即定义函数所属
func (b Box) Volume() float64 {
return b.width * b.height * b.depth
}
func (b *Box) SetColor(c Color) {
b.color = c
}
匿名字段实现了一个method,则包含匿名字段的struct
也能调用该method
type Human struct {
name string
age int
phone string
}
type Employee struct {
Human //匿名字段
company string
}
//Human定义method
func (h *Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
//Employee的method重写Human的method
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
//Employee的method重写Human的method
func (e *Employee) SayHi() {
fmt.Printf("Hi, I am %s, I work at %s. Call me on %s\n", e.name,
e.company, e.phone) //Yes you can split into 2 lines here.
}
interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口
// 定义interface
type Men interface {
SayHi()
Sing(lyrics string)
}
若一个struct
类型的方法都实现了interface的接口,则interface可存储该struct
类型的对象
//Human实现SayHi方法
func (h Human) SayHi() {
fmt.Printf("Hi, I am %s you can call me on %s\n", h.name, h.phone)
}
//Human实现Sing方法
func (h Human) Sing(lyrics string) {
fmt.Println("La la la la...", lyrics)
}
mike := Human{"Mike", 25, "222-222-XXX"}
sam := Employee{Human{"Sam", 36, "444-222-XXX"}, "Golang Inc.", 1000}
//定义Men类型的变量i
var i Men
//i能存储Employee、Human
i = sam
i = mike
不包含任何方法,故所有类型都实现了空interface,对于描述起不到任何作用,但可以存储任意类型数值
// 定义a为空接口
var a interface{}
var i int = 5
s := "Hello world"
// a可以存储任意类型的数值
a = i
a = s
interface的变量可以持有任意实现该interface类型的对象,如fmt.Stringer
type Stringer interface {
String() string
}
反向获取变量类型
switch value := element.(type) {
case int:
fmt.Printf("list[%d] is an int and its value is %d\n", index, value)
case string:
fmt.Printf("list[%d] is a string and its value is %s\n", index, value)
case Person:
fmt.Printf("list[%d] is a Person and its value is %s\n", index, value)
default:
fmt.Println("list[%d] is of a different type", index)
}
如果一个interface1作为interface2的一个嵌入字段,那么interface2隐式的包含了interface1里面的method
type Interface interface {
sort.Interface //嵌入字段sort.Interface,把sort.Interface的所有method给隐式的包含进来了
Push(x interface{}) //a Push method to push elements into the heap
Pop() interface{} //a Pop elements that pops elements from the heap
}
// sort 的 Interface
type Interface interface {
// Len is the number of elements in the collection.
Len() int
// Less returns whether the element with index i should sort
// before the element with index j.
Less(i, j int) bool
// Swap swaps the elements with indexes i and j.
Swap(i, j int)
}
Go语言实现了反射,所谓反射就是能检查程序在运行时的状态,一般用到的包是reflect包
reflect.Type
或者reflect.Value
,根据不同的情况调用不同的函数)t := reflect.TypeOf(i) //得到类型的元数据,通过t我们能获取类型定义里面的所有元素
v := reflect.ValueOf(i) //得到实际的值,通过v我们获取存储在里面的值,还可以去改变值
tag := t.Elem().Field(0).Tag //获取定义在struct里面的标签
name := v.Elem().Field(0).String() //获取存储在第一个字段里面的值
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("kind is float64:", v.Kind() == reflect.Float64)
fmt.Println("value:", v.Float())
// 以下修改字段值是错误的
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)
// 以下修改字段值是正确的,反射时需要使用 & 修饰变量才能对反射后的变量进行修改
var x float64 = 3.4
p := reflect.ValueOf(&x)
v := p.Elem()
v.SetFloat(7.1)
import (
"math"
"fmt"
)
go test
测试例如kk_test.go:
package main
import (
"fmt"
"testing"
)
// 测试函数必需以Test开头,并且参数为(t *testing.T),固定模式!
func TestKK(t *testing.T) {
fmt.Println("PASS TEST")
}
D:\src>go test
PASS TEST
PASS
ok demo 0.230s
测试文件中定义Fuzz{Name}的函数,该函数代表模糊测试,用于go test -fuzz=Fuzz
做模糊测试
func FuzzReverse(f *testing.F) {
testcases := []string {"Hello, world", " ", "!12345"}
for _, tc := range testcases {
f.Add(tc) // Use f.Add to provide a seed corpus
}
f.Fuzz(func(t *testing.T, orig string) {
// Reverse 是程序的一个函数,无需关心,下面的逻辑是模糊测试
rev, err1 := Reverse(orig)
if err1 != nil {
return
}
doubleRev, err2 := Reverse(rev)
if err2 != nil {
return
}
if orig != doubleRev {
t.Errorf("Before: %q, after: %q", orig, doubleRev)
}
if utf8.ValidString(orig) && !utf8.ValidString(rev) {
t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
}
})
}
D:\src>go test -fuzz=Fuzz
PASS TEST
fuzz: elapsed: 0s, gathering baseline coverage: 0/10 completed
fuzz: minimizing 38-byte failing input file
fuzz: elapsed: 0s, gathering baseline coverage: 4/10 completed
--- FAIL: FuzzReverse (0.16s)
--- FAIL: FuzzReverse (0.00s)
reverse_test.go:43: Number of runes: orig=1, rev=1, doubleRev=1
reverse_test.go:45: Before: "\xb7", after: "�"
Failing input written to testdata\fuzz\FuzzReverse\72a3fe19fabb4e967f79fa5802976a563b6502c1ee5a29dca84ad5a146de8634
To re-run:
go test -run=FuzzReverse/72a3fe19fabb4e967f79fa5802976a563b6502c1ee5a29dca84ad5a146de8634
FAIL
exit status 1
FAIL demo 0.362s
D:\src>go test -run=FuzzReverse/72a3fe19fabb4e967f79fa5802976a563b6502c1ee5a29dca84ad5a146de8634