本篇本来是接着上一篇的,本篇是 go 的关于方法、接口以及并发的一些例子;上一篇是关于数据结构以及指针的,上上一篇是关于基本语法以及结构的,基本类型、函数、for、if 等等。
- 方法:
- package main
- import (
- "fmt"
- "math"
- )
- type Vertex struct {
- X, Y float64
- }
- func (v *Vertex) Abs() float64 {
- return math.Sqrt(v.X*v.X + v.Y*v.Y)
- }
- func main() {
- v := &Vertex{3, 4}
- fmt.Println(v.Abs())
- //fmt.Println((*v).Abs()) // ok,2
- }
1). 所谓的“方法”(method) —— 就是特殊的函数,只不过与某个类型绑定起来了而已,比如 Abs 绑定到了 *Vertex 类型; 2). Abs 绑定到了 *Vertex 上之后,就可以使用 *Vertex 值去调用这个方法,比如 v.Abs() ; 3). Abs 绑定到了 *Vertex 上之后,函数体内部可以使用 *Vertex 可以访问到的东西,比如 v.X ;
- 在基本类型上定义方法:
- package main
- import (
- "fmt"
- "math"
- )
- type MyFloat float64
- func (f MyFloat) Abs() float64 {
- if f < 0 {
- return float64(-f)
- }
- return float64(f)
- }
- func main() {
- f := MyFloat(-math.Sqrt2)
- fmt.Println(f.Abs())
- }
1). 实际上不能在基本类型,比如 int 上定义方法,但是简单重重命名一下,便可以了; 2). 可以在自己包内的类型上定义方法,不能对包含的其他包和基本类型定义方法; 3). 使用 float64 定义 MyFloat,可以将 float64 直接传给 MyFLoat,但是不能反过来直接传递,可以 float64(f) ;
- 在指针上定义方法:
- package main
- import (
- "fmt"
- "math"
- )
- type Vertex struct {
- X, Y float64
- }
- func (v *Vertex) Scale(f float64) {
- v.X = v.X * f
- v.Y = v.Y * f
- }
- func (v *Vertex) Abs() float64 {
- return math.Sqrt(v.X*v.X + v.Y*v.Y)
- }
- func main() {
- v := &Vertex{3, 4}
- v.Scale(5)
- fmt.Println(v, v.Abs())
- }
1). 使用指针定义方法,一是为了避免大量数据拷贝;而是可以通过指针修改域外的东西;
- 接口:
- package main
- import (
- "fmt"
- "math"
- )
- type Abser interface {
- Abs() float64
- }
- func main() {
- var a Abser
- f := MyFloat(-math.Sqrt2)
- v := Vertex{3, 4}
- a = f // a MyFloat implements Abser
- fmt.Println(a.Abs())
- a = &v // a *Vertex implements Abser
- //a = v // a Vertex, does NOT
- // implement Abser
- fmt.Println(a.Abs())
- }
- type MyFloat float64
- func (f MyFloat) Abs() float64 {
- if f < 0 {
- return float64(-f)
- }
- return float64(f)
- }
- type Vertex struct {
- X, Y float64
- }
- func (v *Vertex) Abs() float64 {
- return math.Sqrt(v.X*v.X + v.Y*v.Y)
- }
1). 接口是一种类型,里面装的是一系列方法声明,也就是多个方法声明; 2). 一个接口值可以用任一个实现了该接口的实例/对象赋值; 3). 实现接口的如果是指针类型 *T,那么,只能使用 *T 变量赋值给接口,而不能是 T 。
- 接口继承:
- package main
- import (
- "fmt"
- "os"
- )
- type Reader interface {
- Read(b []byte) (n int, err error)
- }
- type Writer interface {
- Write(b []byte) (n int, err error)
- }
- type ReadWriter interface {
- Reader
- Writer
- }
- func main() {
- var w Writer
- // os.Stdout implements Writer
- w = os.Stdout
- fmt.Fprintf(w, "hello, writer\n")
- }
1). 接口继承; 2). os 包;os.Stdout ; fmt.Fprintf ;标准输出流、流格式化输出;os.Stdout 实现了 Writer 接口,可以使用 fmt.Fprintf 向 os.Stdout 写内容;
- error 错误处理:
- package main
- import (
- "fmt"
- "time"
- )
- type MyError struct {
- When time.Time
- What string
- }
- func (e *MyError) Error() string {
- return fmt.Sprintf("at %v, %s",
- e.When, e.What)
- }
- func run() error {
- return &MyError{
- time.Now(),
- "it didn't work",
- }
- }
- func main() {
- if err := run(); err != nil {
- fmt.Println(err)
- }
- }
1). error 是一个接口; 2). fmt.Sprintf 格式化字符串输出; 3). 只要是能够用 Error 函数描述自己错误信息的类型,都可以作为 error ;
type error interface { Error() string }
- web server —— web服务器:
- package main
- import (
- "fmt"
- "net/http"
- )
- type Hello struct{}
- func (h Hello) ServeHTTP(
- w http.ResponseWriter,
- fmt.Fprintf(w, "Hello")
- }
- func main() {
- var h Hello
- http.ListenAndServe("localhost:4000",h)
- }
1). net/http 包; 2). http 包使用 Handler 接口处理请求;
package http type Handler interface { ServeHTTP(w ResponseWriter, r *Request) }
- image 处理:
- package main
- import (
- "fmt"
- "image"
- )
- func main() {
- m := image.NewRGBA(image.Rect(0, 0, 100, 100))
- fmt.Println(m.Bounds())
- fmt.Println(m.At(0, 0).RGBA())
- }
1). image 包;Image 接口;
package image type Image interface { ColorModel() color.Model Bounds() Rectangle At(x, y int) color.Color }
- 并发 / 多线程:
- package main
- import (
- "fmt"
- "runtime"
- )
- func say(s string) {
- for i := 0; i < 5; i++ {
- runtime.Gosched()
- fmt.Println(s)
- }
- }
- func main() {
- go say("world")
- say("hello")
- }
1). 并发、多线程;一个 goroutine 是个轻量级线程; 2). 这个程序赶脚也有点诡异!
- 通道 channels:
- package main
- import "fmt"
- func sum(a []int, c chan int) {
- sum := 0
- for _, v := range a {
- sum += v
- }
- c <- sum // send sum to c
- }
- func main() {
- a := []int{7, 2, 8, -9, 4, 0}
- c := make(chan int)
- //c := make(chan int,10)
- go sum(a[:len(a)/2], c)
- go sum(a[len(a)/2:], c)
- x, y := <-c, <-c // receive from c
- fmt.Println(x, y, x + y)
- }
1). 通道;有点管道 pipeline 的味道,一端写东西,从另外一端读东西; 2). 默认具有同步操作; 3). channel 使用前必须 make 一下,带不带第二个参数结果情况是有区别的;
ch := make(chan int) ch <- v // Send v to channel ch. v := <-ch // Receive from ch, and assign value to v.
- 带缓冲 buffered 通道 channels:
- package main
- import "fmt"
- func main() {
- c := make(chan int, 2)
- c <- 1
- c <- 2
- // c <- 3 // deadlock !
- fmt.Println(<-c)
- fmt.Println(<-c)
- }
1). 带缓冲通道;full 阻塞写;empty 阻塞读; 2). 带缓冲和不带缓冲情况是有差异的,不仅仅是效率的问题;
- 主动关闭通道:
- 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)
- }
- }
1). 发送者可以主动关闭通道,只是在需要的地方,比如为了结束 range 循环;
- select:
- 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)
- }
1). select 只要有一个 case 可以执行便执行,否则便阻塞;例子实现了同步操作; 2). 如果有多个可以选择,select 是随机选择一个执行;
- 带默认 default 的select:
- package main
- import (
- "fmt"
- "time"
- )
- func main() {
- tick := time.Tick(1e8)
- boom := time.After(5e8)
- for {
- select {
- case <-tick:
- fmt.Println("tick.")
- case <-boom:
- fmt.Println("BOOM!")
- return
- default:
- fmt.Println(" .")
- time.Sleep(5e7)
- }
- }
- }
1). select default 选择没有可以执行的 case 情况下的默认动作;
由于篇幅所限,也为了看着方便,前面的见上一篇文章。
有疑问加站长微信联系(非本文作者)