前言
1.查看语言版本
代码语言:txt复制go version
2.下载编辑器,Atom在github上是开源的,官网:https://github.com/atom
3.第一个Go程序
代码语言:go复制package main
import "fmt"
func main(){
fmt.Println("hello,world")
}
基本程序结构
1.测试脚本,执行脚本命令go test -v xxx_test.go
package try_test
import "testing"
func TestFirstTry( t*testing.T){
t.Log("My Frist Try");
}
执行结果:
代码语言:txt复制➜ test go test -v zc_test.go
=== RUN TestFirstTry
zc_test.go:6: My Frist Try
--- PASS: TestFirstTry (0.00s)
PASS
ok command-line-arguments 0.006s
2.声明常量
代码语言:go复制package main
import "fmt"
func main() {
const LENGTH int = 10
const WIDTH int = 5
var area int
const a, b, c = 1, false, "str" //多重赋值
area = LENGTH * WIDTH
fmt.Printf("面积为 : %d", area)
println()
println(a, b, c)
}
常量还可以用作枚举:
代码语言:go复制package const_test
import "testing"
func TestConst(t *testing.T) {
const (
Unknown = 0
Female = 1
Male = 2
)
t.Log(Unknown,Female,Male);
}
3.数据类型
- Go语言不支持隐式转换
- 字符串初始化是空字符串
- 指针类型不可以赋值
package type_test
import "testing"
func TestType(t *testing.T) {
var a int = 1;
var b int64
b = int64(a)
t.Log(a,b);
}
func TestPoint(t *testing.T) {
var a int = 1;
aPtr := &a;
t.Log(a,aPtr);
t.Logf("%T %T",a,aPtr);
}
func TestString(t *testing.T) {
var string string
t.Log("*" string "*");
}
4.条件和循环
4.1 for循环
代码语言:go复制package loop_test
import "testing"
func TestLoopNumber(t *testing.T){
var number int = 0
for number < 5 {
t.Log(number);
number ;
}
}
4.2 if条件语句
代码语言:txt复制if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
}
if 布尔表达式 {
/* 在布尔表达式为 true 时执行 */
} else {
/* 在布尔表达式为 false 时执行 */
}
常用集合
1.数组声明和遍历
声明数组 var variable_name [SIZE] variable_type
package array_test
import "testing"
func TestArrayInt(t *testing.T) {
var arr [3]int
arr1 := [4]int{1,2,3,4}
arr3 := [...]int{1,3,4,5}
arr1[1] = 5
t.Log(arr[1],arr[2]);
t.Log(arr1,arr3);
}
func TestArrayLoop( t *testing.T ) {
arr1 := [4]int{1,2,3,4}
var count int = len(arr1);
for index := 0; index < count; index {
t.Log(index,arr1[index]);
}
for index,value := range arr1 {
t.Log(index,value)
}
for _,value := range arr1 {
t.Log(value)
}
}
2.数组截取
代码语言:go复制a[开始索引(包含),结束索引(不包含)]
a := [...]int {1,2,3,4,5}
a[1:2] // 2
a[1:3] // 2,3
a[1:len(a)] // 2,3,4,5
a[1:] //2,3,4,5
a[:3] // 1,2,3
代码语言:go复制func TestArraySlice(t *testing.T) {
arr := [5]int{0,2,3,4,5}
t.Log(arr[0:2])
}
3.切片类型
3.1切片声明:
代码语言:go复制package slice_test
import "testing"
func TestSliceInit(t *testing.T) {
var s0 []int
s0 = append(s0,1)
s := []int{}
s1 := []int{1,2,3}
s2 := make([]int, 2,4)
t.Log(len(s0),cap(s0));
t.Log(len(s),cap(s));
t.Log(len(s1),cap(s1));
t.Log(len(s2),cap(s2));
}
4.数组和切片的异同点:
- 容量是否可伸缩
- 是否可进行比较
5.Map声明和遍历
代码语言:go复制package map_test
import "testing"
func TestMapInit(t *testing.T) {
m := map[string]int{"one":1,"two":2,"three":3}
m1 := map[string]int{}
m2 := make(map[string]int, 10)
t.Log(m,m1,m2);
}
func TestMapLoop(t *testing.T) {
m := map[string]int{"one":1,"two":2,"three":3}
for key,value := range m {
t.Log(key,value);
}
}
与其他语言的差异:在访问的key不存在时,扔会返回0值,不能通过返回nil来判断元素是否存在。
6.字符串(与其他主要编程语言的差异)
- string是引用类型,不是指针类型
- string是只读的byte slice,len函数可以所包含的byte
- string的byte数组可以存放任何数据
函数
与其他主要编程语言的差异
- 可以有多个返回值
- 所有参数都是值传递:slice,map,channel会有传引用的错觉
- 函数可以作为变量的值
- 函数可以作为参数和返回值
package func_test
import "testing"
import "math/rand"
func returnParams() (int ,int){
return rand.Intn(10),rand.Intn(20)
}
func TestFuncParams(t *testing.T) {
a,b := returnParams();
t.Log(a,b);
}
可变参数:
代码语言:txt复制package func_test
import "testing"
func Sum(ops ...int) int{
ret := 0
for _,op := range ops {
ret = op
}
return ret;
}
func TestSumParams(t *testing.T) {
t.Log(Sum(1,2,3))
t.Log(Sum(1,2,3,4,5))
}
defer延迟执行特性:
代码语言:txt复制package func_test
import "testing"
import "fmt"
func Clear() {
fmt.Println("clear....");
}
func TestDeferFunc(t *testing.T) {
defer Clear()
fmt.Println("start");
}
面向对象编程
1.数据的封装(结构体)
代码语言:go复制type Employee struct {
Id string
Name string
Age int
}
代码实例:
代码语言:txt复制package struct_test
import (
"testing"
)
type Employee struct {
Id string
Name string
Age int
}
func TestNewObject(t *testing.T) {
e := Employee{"0","Bob",20}
e1 := Employee{Name:"Mike",Age:30}
e2 := new(Employee) // 返回指针
t.Log(e)
t.Log(e1)
t.Log(e2)
}
与其他主要编程语言的差异:
代码语言:txt复制package struct_test
import (
"testing"
"fmt"
"unsafe"
)
func TestVarAddr(t *testing.T) {
e := Employee{"0","Bob",20}
fmt.Printf("Address is %x n",unsafe.Pointer(&e.Name))
t.Log(e.getString());
}
//第一种定义方式在对应方法被调用时,实例的成员会进行赋值
func (e *Employee) getString() string {
return fmt.Sprintf("ID:%s-Name:%s-Age:%d",e.Id,e.Name,e.Age);
}
//通常情况下为了避免内存拷贝我们使用第二种方式定义
func (e *Employee) getAddrString() string {
return fmt.Sprintf("ID:%s-Name:%s-Age:%d",e.Id,e.Name,e.Age);
}
2.Go语言的接口与依赖
接口:与其他主要编程语言的差异
- 接口为非入侵性,实现不依赖于接口定义
- 所有接口的定义可以包含在接口使用者包内
package interface_test
import (
"testing"
)
type params interface {
HelloWorld() string
}
type GoParams struct {
}
func (g *GoParams ) HelloWorld() string {
return "fmt.Println("Hello,world")";
}
func TestClient(t *testing.T){
var p params
p = new(GoParams)
t.Log(p.HelloWorld());
}
3.扩展与复用
代码语言:txt复制package extension_test
import (
"testing"
"fmt"
)
type Pet struct {
}
func (p *Pet) Speak() {
fmt.Println("...");
}
func (p *Pet) SpeakTo(host string) {
//p.Speak();
fmt.Println(" ",host)
}
type Dog struct {
Pet
}
func TestExtens(t *testing.T){
dog := new(Dog)
dog.GetDoger();
}
func (d *Dog) GetDoger() {
d.SpeakTo("host");
p := new(Pet);
p.SpeakTo("ssss");
}
4.空接口与断言
- 空接口可以表示任何类型
- 通过断言来将空接口转换为制定类型
v,ok := p.(int)
代码语言:txt复制package empty_interface
import (
"testing"
"fmt"
)
func DoSomething(p interface{}){
if i,ok := p.(int); ok {
fmt.Println("Intager",i);
return
}
if s,ok := p.(string); ok {
fmt.Println("String",s);
return
}
fmt.Println("Unknow Type")
}
func TestEmptyInterface(t *testing.T) {
DoSomething(10)
DoSomething("stark")
}
Go接口最佳实践:
倾向于使用小的接口定义,很多接口只包含一个方法。
代码语言:go复制type Reader interface {
Read(p []byte)(n int,err error)
}
type Writer interface {
Writer(p []byte)(n int,err error)
}
较大的接口定义,可以由多个小接口定义组合而成。
代码语言:Go复制type ReadWrite interface {
Read(p []byte)(n int,err error)
Writer(p []byte)(n int,err error)
}
只依赖于必要功能的最小接口。
错误处理
与其他主要编程语言的差异
- 没有异常机制
- error类型实现了error接口
- 可以通过errors.New来快速创建错误实例
type error interface {
Error() string
}
errors.New("n must be in the range [0,10]");
2.panic
- panic用于不可以恢复的错误
- panic退出前会执行defer指定的内容
3.recover
在不确定的情况下,可以重启来恢复程序
包和依赖管理
1.package
- 基本复用模块单元(以首字母大写来表明可被包外代码访问)
- 代码的package可以和所在的目录不一致
- 同一目录里的Go代码的package要保持一致
2.init方法
- 在main被执行前,所有依赖的package的init方法都会被执行
- 不同包的init函数按照包导入的依赖关系决定执行顺序
- 每个包可以有多个init函数
- 包的每个源文件也可以有多个init函数,这点比较特殊
package
1.通过 go get 来获取远程依赖
- go get -u 强制从网络更新远程依赖 2.注意代码在GitHub上的组织形式,以适应go get
- 直接以代码路径开始,不要有src
依赖管理
并发编程
1.协程
代码语言:Go复制package groutine_test
import (
"fmt"
"time"
"testing"
)
func TestGroutine(t *testing.T) {
for i:= 0;i< 10;i {
go func (i int) {
fmt.Println(i)
}(i)
time.Sleep(time.Millisecond * 50)
}
}
2.协程锁 sync.Mutex
3.CSP并发机制
Csp Vs Actor
- 和Actor的直接通讯不同,Csp模式则是通过Channel进行通讯的,更松耦合一些。
- Go中channel是有容量限制并且独立于处理Groutine,Actor模式中的mailbox容量是无限的,接收的进程也总是被动的处理消息。
package channel_test
import (
"testing"
"fmt"
"time"
)
func service () string {
time.Sleep(time.Millisecond * 50)
return "Deno"
}
func otherTask(){
fmt.Println("working on something else")
time.Sleep(time.Millisecond * 100)
fmt.Println("Task is done")
}
func TestService(t *testing.T) {
t.Log(service ())
otherTask()
}
Channel异步执行
代码语言:Go复制package channel_test
import (
"testing"
"fmt"
"time"
)
func service () string {
time.Sleep(time.Millisecond * 50)
return "Deno"
}
func otherTask(){
fmt.Println("working on something else")
time.Sleep(time.Millisecond * 100)
fmt.Println("Task is done")
}
func AsynService() chan string {
retCh := make(chan string,1)
go func(){
ret := service()
fmt.Println("returned result")
retCh <- ret
fmt.Println("service exited")
}()
return retCh
}
func TestAsynService(t *testing.T) {
retCh := AsynService()
otherTask()
fmt.Println(<-retCh)
time.Sleep(time.Second * 1)
}
多路选择和超时
代码语言:Go复制select {
case ret := <- retChannel
}
4.Channel的关闭和广播
- 向关闭的channel发送数据,会导致panic
- v,ok <- ch;ok为bool值,true表示正常接收,false表示通道关闭
- 所有的channel接收者都会在channel关闭时,立刻从阻塞等待中返回且上述ok值为false。这个广播机制常被利用,进行向多个订阅者同时发送信号。比如退出信号。
sync.Pool 对象获取
- 尝试从私有对象获取
- 私有对象不存在,尝试从当前Processor的共享池获取
- 如果当前Processor共享池也是空的,那么就尝试去其他Processor的共享池获取
- 如果所有子池都是空的,最后就用用户指定的New函数产生一个新的对象返回
- sync.Pool 对象的生命周期
- GC会清除sync.pool缓存的对象
- 对象的缓存有效期为下一次GC之前
sync.Pool 总结
- 适用于通过复用,降低复杂对象的创建和GC代价
- 协程安全,会有锁的开销
- 生命周期受GC影响,不适合于做连接池等,需自己管理生命周期的资源的池化
测试
1.单元测试
内置单元测试框架
- Fail,Error: 该测试失败,该测试继续,其他测试继续执行
- FailNow,Fatal:该测试失败,该测试中止,其他测试继续执行
-v:查看测试结果
-cover:测试代码覆盖率
代码语言:Go复制go test -v -cover type_test.go
2.第三方 BDD框架
项目网站
https://github.com/smartystreets/goconvey
安装
代码语言:txt复制go get -u github.com/smartystreets/goconvey
启动 web Ui
代码语言:txt复制$GOPATH/bin/goconvey
反射和UnSafe
代码语言:txt复制reflect.TypeOf Vs reflect.ValueOf
- reflect.TypeOf 返回类型(reflect.TypeOf)
- reflect.ValueOf 返回类型(reflect.Value)
- 可以从reflect.Value获得类型
- 通过kind来判断类型
常用架构模式实现
Pipe-Filter模式
- 非常适合与数据处理及数据分析系统
- Filter封装数据处理的功能
- 松耦合:Filter只跟数据(格式)耦合
- Pipe用于连接Filter传递数据或者在异步处理过程中缓冲数据流进程内同步调用时,pipe演变为数据在方法调用间的传递。
Micro Kernel
特点:
易于扩展、错误隔离、保持架构一致性
要点:
- 内核包含公共流程和通用逻辑
- 将可变或可扩展部分规划为扩展点
- 抽象扩展点行为,定义接口
- 利用插件进行扩展
常见任务
内置Json解析
代码语言:Go复制type BasicInfo struct {
Name string `json:"name"`
Age int `json:"age"`
}