简单例子
package main //当前所在的文件名
import ( //引进包
"fmt"
"time"
)
//声明函数 函数名字 传入变量以及类型 结果的类型(有几个就写几个)
func add(x, y int) (int) {
return x + y
}
func main(){
fmt.Println("Welcom to Go!",time.Now())
fmt.Println(add(42,13))
}
包,变量,函数
- 包:“fmt”,“time”
- 变量:
- 声明方式:
var x float32 =42
x := float32(42)
- 常见数据型:
- bool,
- string
- int int8 int16 int32 int64
- uint uint8 uint16 uint32 uint64 uintptr
- float32/float64
- complex64/complex128
- 函数
//函数写法1:声明 传入变量 函数名字 结果类型 func (v Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y) } //函数写法2:声明 函数名字 传入变量 结果(有几个就写几个类型) func add(x, y int) (int) { return x + y } // 函数写法3(内部方法):类名 函数名字 传入变量 结果类型 //内部方法和非内部方法区别:非内部方法默认导出,内部方法不导出 //调用区别:非内部方法导出后可直接调用,内部方法导出后调用只能用 srv.add(); func(srv Service) add(x, y int) (int) { return x + y }
流程控制语句
- for
//go的for语句没有括号
for i := 0; i < 10; i++ {
sum += i
}
//类似于其他语言的while写法
for sum < 1000 {
sum += sum
}
//无限循环
for {}
- if else
-
//简单的判断
if x<0 {}
//可在里面正常生成一个
if v := math.Pow(x, n); v < lim {
return v
}
- switch
switch i {
case 0:xxx,
case f()
}
- defer
//defer定义的函数会立即求值,但要到外层结束后才会被调用
//其实质是压入一个栈
defer fmt.Println("world")
更多类型:指针,struct,数组,slice和映射
- 指针,指针的零值为nil
//定义p的值
var p *int
//&操作符生成一个指向其操作数的指针
i := 42
p = &i
//*操作符表示指针指向的底层值
fmt.Println(*p) //通过指针p读取i
*p = 21 //通过指针p设置成i
package main
import "fmt"
func main() {
i, j := 42, 2701
p := &i // 指向 i
fmt.Println(*p) // 通过指针读取 i 的值
*p = 21 // 通过指针设置 i 的值
fmt.Println(i) // 查看 i 的值
p = &j // 指向 j
*p = *p / 37 // 通过指针对 j 进行除法运算
fmt.Println(j) // 查看 j 的值
}
- struct
//define the struct
type Vertex struct {
X int
Y int
}
//usage
v := Vertex{1, 2}
//结构体文法,通过直接列出字段的值来新分配一个结构体
//使用Name:语法可以仅列出部分字段
//特殊前缀&返回一个指向结构体的指针
var (
v1 = Vertex{1, 2} // 创建一个 Vertex 类型的结构体
v2 = Vertex{X: 1} // Y:0 被隐式地赋予
v3 = Vertex{} // X:0 Y:0
p = &Vertex{1, 2} // 创建一个 *Vertex 类型的结构体(指针)
)
func main() {
fmt.Println(v1, p, v2, v3)
}
- slice,make
- 数组和切片的区别:数组一般是定长的,数组切片可以构建成slice
- slice切片
//define array
var a [10]int
var a [10]int={2,3,5,7,11,13} //a :=[10]int{2,3,5,7,11,13}
//slice the array
a[low:high]
//slice length and capacity
// 截取切片使其长度为 0
s = s[:0]
printSlice(s)
// 拓展其长度
s = s[:4]
printSlice(s)
// 舍弃前两个值
s = s[2:]
printSlice(s)
- make切片
//切片的cap是总容量,len是里面存在的具体个数
a := make([]int, 5) // len(a)=5
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:] // len(b)=4, cap(b)=4
- 切片定义任何类型
// 创建一个井字板(经典游戏)
board := [][]string{
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
[]string{"_", "_", "_"},
}
// 两个玩家轮流打上 X 和 O
board[0][0] = "X"
board[2][2] = "O"
board[1][2] = "X"
board[1][0] = "O"
board[0][2] = "X"
- 切片扩展
// 添加一个空切片
s = append(s, 0)
printSlice(s)
// 这个切片会按需增长
s = append(s, 1)
printSlice(s)
// 可以一次性添加多个元素
s = append(s, 2, 3, 4)
printSlice(s)
- range
for i, _ := range pow{}
for _, value := range pow{}
for i := range pow{}
- 映射 有点类似于js的json
//define the map
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google": {37.42202, -122.08408},
}
//插入或者修改
m[key] = elem
//获取元素
elem = m[key]
//删除元素
delete(m,key)
//通过双赋值检查某个键是否存在
value,ok=m[key]
方法
- golang的方法可以作为值进行传递
- 方法只是个带接收者参数的函数
//写法1
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
//写法2
func Abs(v Vertex) float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
- 和C语言类似的指针调用方法
*p定义为指针函数,*p指向的地址才是实际的地址,使用&p传入形参 - 指针为接受者的方法调用时,接收者即可以为值也可以为指针
var v Vertex
v.Scale(5) // OK
Scale(5) //错误
p := &v
p.Scale(10) // OK
Scale(p)//Ok
- 同样发生在传入赋值时
var v Vertex
fmt.Println(v.Abs()) // OK
fmt.Println(Abs(&v)) // 编译错误!
p := &v
fmt.Println(p.Abs()) // OK 这种状态下被自动解释为(*p).Abs()
- 选择指针作为接收者的规律
- 方法能够修改其接收者指向的值
- 这样可以避免在每次调用方法时复制该值。若值的类型为大型结构体时,这样做会更加高效。
接口:一组方法定义签名定义的组合
-
定义和使用:
//例子,interface里面的方法 type I interface { M() }
-
隐式实现
package main import "fmt" //相当于函数里面的方法 type I interface { M() } //相当于类 type T struct { S string } // 此方法表示类型 T 实现了接口 I,但我们无需显式声明此事。 func (t T) M() { fmt.Println(t.S) } func main() { var i I = T{"hello"} i.M() }
-
接口值:接口可作为值返回,使用标准符号 %T
-
nil值:未定义的接口值默认为nil,但不从逻辑上排除会报错
-
空接口
var i interface{}
-
类型断言和类型选择
//类型断言 t,ok :=i,(Type) //example t,ok := i.(int) //类型选择 switch v := i.(type) { case T: // v 的类型为 T case S: // v 的类型为 S default: // 没有匹配,v 与 i 的类型相同 }
-
Stringer:fmt包中函数,可以重写fmt.Println()函数
//interface type Stringer interface { String() string } //usage type Person struct { Name string Age int } func (p Person) String() string { return fmt.Sprintf("%v (%v years)", p.Name, p.Age) } func main() { a := Person{"Arthur Dent", 42} z := Person{"Zaphod Beeblebrox", 9001} fmt.Println(a, z) }
-
错误:与Stringer相似可以处理相应的值
func (e *MyError) Error() string { return fmt.Sprintf("at %v, %s", e.When, e.What) }
-
Reader
//使用例子 func main() { r := strings.NewReader("Hello, Reader!") //切片创造空间 b := make([]byte, 8) for { n, err := r.Read(b) fmt.Printf("n = %v err = %v b = %v\n", n, err, b) fmt.Printf("b[:n] = %q\n", b[:n]) if err == io.EOF { break } } }
-
图像
//底层定义 type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color } //使用例子 func main() { m := image.NewRGBA(image.Rect(0, 0, 100, 100)) fmt.Println(m.Bounds()) fmt.Println(m.At(0, 0).RGBA()) }
并发
-
goroutine go程:Go 运行时管理的轻量级线程
//command go f(x,y,z)
-
channel 信道:带有类型的管道,类似于携程
//创建,信道在使用前必须创建 ch := make(chan int);//默认只有一个数据 //使用方法 ch <- v // 将 v 发送至信道 ch。 v := <-ch // 从 ch 接收值并赋予 v。 //例子 package main import "fmt" func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // 将和送入 c } func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // 从 c 中接收 fmt.Println(x, y, x+y) }
-
带缓冲的channel:
//初始化带缓存的信道 ch := make(chan int, 100) //当信道填满后会阻塞 package main import "fmt" func main() { ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<-ch) fmt.Println(<-ch) }
-
range和close
-
发送者关闭close,只能由发送者关闭。如 close©.
-
接收者通过表达式检测信道是否关闭。如 v,ok := <-ch.
-
信道与文件不同,只有告诉发送者不再接受时才能不关闭。
package main import ( "fmt" ) func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c) } func main() { c := make(chan int, 10) go fibonacci(cap(c), c) for i := range c { fmt.Println(i) } }
-
-
select 语句:
-
使得go等待多个通信操作。多个分支都准备好时随机选择一个执行;
package main import "fmt" func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y case <-quit: fmt.Println("quit") return } } } func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) }
-
当其他分支都没准备好时,default分支就会执行
//model select { case i := <-c: // 使用 i default: // 从 c 中接收会阻塞时执行 } //example package main import ( "fmt" "time" ) func main() { tick := time.Tick(100 * time.Millisecond) boom := time.After(500 * time.Millisecond) for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } } }
-
-
asycn.Mutex互斥锁:
-
比如:想保证每次只有一个Go程能够访问一个共享的变量
-
Lock
-
Unlock
package main import ( "fmt" "sync" "time" ) // SafeCounter 的并发使用是安全的。 type SafeCounter struct { v map[string]int mux sync.Mutex } // Inc 增加给定 key 的计数器的值。 func (c *SafeCounter) Inc(key string) { c.mux.Lock() // Lock 之后同一时刻只有一个 goroutine 能访问 c.v c.v[key]++ c.mux.Unlock() } // Value 返回给定 key 的计数器的当前值。 func (c *SafeCounter) Value(key string) int { c.mux.Lock() // Lock 之后同一时刻只有一个 goroutine 能访问 c.v defer c.mux.Unlock() return c.v[key] } func main() { c := SafeCounter{v: make(map[string]int)} for i := 0; i < 1000; i++ { go c.Inc("somekey") } time.Sleep(time.Second) fmt.Println(c.Value("somekey")) }
-
-
Reference: