项目组织结构
/GOPATH
/src #源代码
main.go #主函数
/myPackage #自己的包
myfile.go
/pkg #引入的包
/bin #可执行文件
变量声明
iota的使用:
iota 比较特殊,可以被认为是一个可被编译器修改的常量.
在每一个 const 关键字出现时被重置为0,
然后在下一个 const 出现之前,每出现一次 iota ,其所代表的数字会自动增1.
const后面跟括号
表明其余的变量都重复第一个变量的表达式!
枚举的定义
下面是星期的定义,最后一个代表日期数,而且是包内私有的.
示例代码:
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays
)
数组
package main
import (
"fmt"
)
func main() {
array := [5]int{1, 1, 1, 1, 1}
modifyArray(array)
fmt.Println("修改后的数组是:", array)
}
func modifyArray(array [5]int) {
array[0] = 10 //试图修改第一个元素的值
fmt.Println("在修改函数中,数组的值是:", array)
}
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays
)
slice 数组切片
从数组中创建
var mArray [5]int = [5]int{5, 5, 5, 5, 5}
var mSlice []int = mArray[:3]
fmt.Println("mArray is:")
for _, v := range mArray {
fmt.Print(v, " ")
}
fmt.Println("\nmSlice is:")
for _, v := range mSlice {
fmt.Print(v, " ")
}
mSlice2 := []int{1, 2, 3, 4, 5, 6}
for i, v := range mSlice2 {
fmt.Println("slice:", i, "is:", v)
}
数组切片的CRUD
mslice := make([]int, 5, 10)
fmt.Println("len(mint):", len(mslice))
fmt.Println("cap(mint):", cap(mslice))
mslice = append(mslice, 1, 2, 3)
fmt.Println("mint:", mslice)
mslice = mslice[:len(mslice) - 1]
fmt.Println("切片移除最后一位:", mslice)
mslice = append(mslice[:5 - 1], mslice[5 + 1:]...)
fmt.Println("删除索引5:", mslice)
map的使用
map简单例子
package main
import (
"fmt"
)
/**
类型: 个人信息
*/
type PersonInfo struct {
ID string
Name string
Address string
}
func main() {
//定义一个map
var personDB map[string]PersonInfo
//用make函数创建一个map
personDB = make(map[string]PersonInfo)
//插入几条数据
personDB["123"] = PersonInfo{"123", "xiaoming", "aaa strt"}
personDB["124"] = PersonInfo{"124", "xiaoli", "fsdfsdfs"}
//查找map
fmt.Println("ID为:123的人是:", personDB["123"])
// ok是一个返回的bool型,返回true表示找到了对应的数据
person, ok := personDB["124"]
if ok {
fmt.Println("找到了人:", person)
} else {
fmt.Println("没找到")
}
}
map 例子
package main
import (
"fmt"
)
/**
类型: 个人信息
*/
type PersonInfo struct {
ID string
Name string
Address string
}
func main() {
//定义一个map
var personDB map[string]PersonInfo
//用make函数创建一个map
personDB = make(map[string]PersonInfo)
//插入几条数据
personDB["123"] = PersonInfo{"123", "xiaoming", "aaa strt"}
personDB["124"] = PersonInfo{"124", "xiaoli", "fsdfsdfs"}
//查找map
fmt.Println("ID为:123的人是:", personDB["123"])
// ok是一个返回的bool型,返回true表示找到了对应的数据
person, ok := personDB["124"]
if ok {
fmt.Println("找到了人:", person)
} else {
fmt.Println("没找到")
}
//删除元素
fmt.Println("删除前:", personDB)
delete(personDB, "123")
fmt.Println("删除后:", personDB)
}
goto跳转
跳转到指定的标记位置
package main
import "fmt"
func main() {
i := 0
//一个标记位
FLAG:
fmt.Println(i)
i++
if i < 10 {
//跳转到标记位置
goto FLAG
}
}
func函数
创建函数
package mymath
import (
"errors"
)
func Add(a int, b int) (result int, err error) {
if a < 0 || b < 0 {
//假设只支持正数
err = errors.New("只能输入正数!")
return
}
result = a + b
return
}
任意类型不定参数
package main
import "fmt"
/**
任意类型的不定参数
*/
/**
interface{}: 任意类型的参数
... 代表不定长度
*/
func MyPrintf(args ...interface{}) {
for _, arg := range args {
switch arg.(type){
case int:
fmt.Println(arg, "is an int value")
case int8:
fmt.Println(arg, "is an int8 value")
case string:
fmt.Println(arg, "is string value")
default:
fmt.Println(arg, "is an unknow type")
}
}
}
func main() {
var a int = 1
var b int8 = 121
var c string = "fsdfsf"
var d map[string]string = make(map[string]string)
MyPrintf(a, b, c, d)
}
匿名函数与闭包
package main
import (
"fmt"
)
func main() {
var j int = 5
//a是一个返回函数的函数立即执行的结果,所以a最终是一个函数.
a := func() (func()) {
var i int = 10
return func() {
fmt.Printf("i,j : %d,%d \n", i, j)
}
}()
a()
j *= 2
a()
}
执行结果:
i,j : 10,5
i,j : 10,10
错误处理
defer
func copyFile(dst, src string) (w int64, err error) {
srcFile, err := os.Open(src)
if err != nil {
return
}
//这个收尾工作,一定会执行,而且是从后往前执行!
defer srcFile.Close()
dstFile, err := os.Create(dst)
if err != nil {
return
}
defer dstFile.Close()
return io.Copy(dstFile, srcFile)
}
panic recover
调用panic后程序就真的挂了,再也无法执行了!
所以尽量还是用return error 的形式处理错误.
package main
import (
"fmt"
"log"
)
func foo() {
fmt.Println("a")
panic("我报错啦!")
fmt.Println("b")
}
func main() {
defer func() {
if r := recover(); r != nil {
log.Printf("Runtime Error caught: %v", r)
}
}()
foo()
fmt.Println("c")
}
输出结果:
a
2016/11/26 21:31:58 Runtime Error caught: 我报错啦!
Process finished with exit code 0
flag 用户参数解析
在执行二进制文件时, 输入 -o -i 等参数.
package main
import (
"flag"
"fmt"
)
// 第二个参数是默认值
var infile *string = flag.String("i", "infile", "File contais values for sorting")
var outfile *string = flag.String("o", "outfile", "File to receive sorted values")
var algorithm *string = flag.String("a", "qsort", "Sort algorithm")
func main() {
flag.Parse()
if infile != nil {
fmt.Println("infile:", *infile, " outfile:", *outfile, " algorithm:", *algorithm)
}
}
面向对象编程
简单例子
package main
import "fmt"
type Hero struct {
name string
mana int
skill string
skillCost int
horse *Horse
}
//receiver使用指针类型,可以改变实例的属性,否则不能.
func (hero *Hero) useSkill() {
fmt.Println(hero.name + "发动技能:" + hero.skill)
hero.mana -= hero.skillCost
}
func (hero *Hero) sayMana() {
fmt.Printf("%s还有%d%%的魔法值!", hero.name, hero.mana)
}
/**
玩家的马
*/
type Horse struct {
name string //名字
hungry int //饥饿度
speed int //跑速
}
func (horse *Horse) run() {
fmt.Println(horse.name, "跑起来了,当前饥饿度:", horse.hungry)
horse.hungry -= 1
}
func main() {
gailun := &Hero{name:"盖伦", mana:500, skill:"大保剑", skillCost:60}
chitu := &Horse{"赤兔", 100, 5}
gailun.horse = chitu
for i := 0; i < 5; i++ {
gailun.useSkill()
gailun.sayMana()
gailun.horse.run()
}
}
接口
接口查询
判断一个实例对象是不是某一种类型(接口或对象的指针)
if file5, ok := file1.(two.IStream); ok {
...
}
var file1 Writer = ...
if file6, ok := file1.(*File); ok {
...
}
类型查询
var v1 interface{} = ...
switch v := v1.(type) {
case int:
// 现在v的类型是int
case string: // 现在v的类型是string
...
}
goroutin 并发编程
chan通信
核心理念: 不要通过共享内存来通信,而应该通过通信来共享内存
无缓冲channel: 任何读写操作都会使当前goroutin立即阻塞,直到该channel被读取或被写入为止.
package main
import "fmt"
func count(ch chan int) {
ch <- 1
fmt.Println("counting")
}
func main() {
//定义一个存放channel的数组
chs := make([]chan int, 10)
//初始化并启动chan数组
for i, _ := range chs {
chs[i] = make(chan int)
go count(chs[i])
}
//读取chan数组
for i, _ := range chs {
<-chs[i]
}
}
带缓冲的channel
第2个参数如果不加就是不带缓冲的.
c := make(chan int, 1024)
select机制
case 语句里必须是一个IO操作.(chan操作?)
case 是同时随机判断的,没有判断顺序.
当一个case满足条件时,整个select退出.
当所有case都不满足时,走default.
package main
import "fmt"
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go func() {
ch2 <- 2
ch1 <- 1
}()
select {
case <-ch1:
fmt.Println("ch1")
case <-ch2:
fmt.Println("ch2")
}
}
超时机制
// 首先,我们实现并执行一个匿名的超时等待函数
timeout := make(chan bool, 1)
go func() {
time.Sleep(1e9) // 等待1秒钟
timeout <- true
}()
// 然后我们把timeout这个channel利用起来
select {
case <-ch:
// 从ch中读取到数据
case <-timeout:
// 一直没有从ch中读取到数据,但从timeout中读取到了数据
}
多核并行
真正意义上的并发,直接使用多个CPU.
runtime.GOMAXPROCS(runtime.NumCPU())
fmt.Println(runtime.NumCPU())
sync.Once 全局唯一性操作
once 的 Do() 方法可以保证在全局范围内只调用指定的函数一次(这里指匿名函数),
而且所有其他goroutine在调用到此语句时,将会先被阻塞,直至全局唯一的once.Do() 调用结束后才继续。
once := sync.Once{}
once.Do(func() {
fmt.Println("aaa")
})
sync.Mutex 加锁机制
采用加锁的机制,即通过共享内存来实现通信,不推荐这种方式
package main
import (
"fmt"
"sync"
"time"
)
//申明一个锁,同时锁住读写,
var mutex sync.Mutex
var count int = 0
/**
采用加锁的机制,即通过共享内存来实现通信,不推荐这种方式
*/
func main() {
for i := 0; i < 5; i++ {
go visit()
}
time.Sleep(10 * time.Second)
}
func visit() {
//加锁
mutex.Lock()
//释放锁
defer mutex.Unlock()
count += 1
fmt.Println(count)
}