golang基础语法学习

1.函数作为一等公民

2.驼峰命名法/大小写决定是否在包外见

3.包名应该是小写的单个单词命名

4. 包名应为其源码的基础名称,如encoding/base64,包名应为base64而不是encoding_base64

5.获取器:Go不对获取器( getter)或 设置器(setter)支持,开发人员需要编写获取器和设置器的方法 func (o *Object)Owner()string{}/ func (o *Object)SetOwner(user string){}

6.接口名:按照约定:只包含一个方法的接口应该以该方法的名称加er,比如Reader,Writer,Formatter,CloseNotifier等

7.若你的类型实现的方法与一个众所周知的类型的方法拥有相同的含义,那就用相同的命名,比如字符串转换方法命名为String而不是ToString

8.使用for range 遍历 数组或者切片

9.go与众不同的特性之一:函数和方法可以返回多个值。目的:为了改变C的笨习惯,将返回值比如-1表示EOF和通过地址传实参

10.go函数返回值或结果形参可被命名,并作为常规变量使用,就和传入的形参一样。

11.defer(LIFO)先进后出,典型应用:解锁互斥锁和关闭文件。

12. new分配内存会将内存置0,返回指针

13. make 只用于slice,map,channel,并且返回T而不是(*T)的一个已知初始化(而非置0)的值

```

var p *[]int = new([]int) //分配切片结构:*p == nil ;基本没用 返回切片指针

var v []int = make([]int,100) //切片v现在引用一个具有100个int元素的新数组

//没必要的方式

var p *[]int = new([]int)

*p = make([]int,100,100)

//等价于

var p = make([]int,100)

```

14 数值是值,将一个数组赋值给另外一个数组会复制其中所有元素

15 若将数组传入函数,它将接受到数组的值拷贝(副本)而不是指针

16 数组的大小是其类型的一部分,类型【10】int 和【20】int是不同的

17 切片保存了底层数组的引用,若你将某个切片赋予另一个切片,他们将会引用同一个数组。

18 切片函数对内容修改,可以直接传参,若使用append对其添加,则无效,原因是append会将原数组扩容,返回的仍然是原切片而已

19 Map字典,映射 若将map传入函数,并更改此映射的内容,则改修改对调用者可见

20 常量定义的表达式必须是可被编译器求值的常量表达式

21 Go中同时有函数和方法,方法就是一个包含了接收者(receiver)的函数

22 方法的接收着是否必须为结构体?不一定,任何类型都可以

23 go内置函数append

```

func Append(slice,data[]byte)[]byte{

  l := len(slice)

  if l + len(data) > cap(slice) {

    newSlice := make([]byte,(l+len(data)*2))

    copy(newSlice,slice)

    slice = newSlice

  }

  slice = slice[0:l+len(data)]

  for i,c:= range data{

    slice[l+i] = c

  }

  return slice

}

 

//方法实现

type ByteSlice []byte

func (p *ByteSlice) Append(data []byte){

  slice := *p

        *p = slice

}

 

```

24 让ByteSlice实现io.Writer

实现Writer方法

```

type ByteSlice []byte

func (p *ByteSlice)Write(data []byte)(n int,err error){

  slice := *p

  *p = slice

  return len(data),nil

}

```

 

25 接口 为指定对象的行为提供的一种约定:如果事情可以这么做,那么他就可以在这里使用,一个类型可以实现多个接口。

26 类型断言 value interface{} / value.(string)

27 隐藏接口的实现 (duck type)

 ```

type Block interface{

  BlockSize() int

  Encrypt(src,dst []byte)

  Decypt(src, dst []byte)

}

 

type Stream interface{

  XORKeyStream(dst,src []byte)

}

 

func NewCTR(block Block,iv []byte) Stream 

``` 

NewCTR并不只是用于一个特定加密算法和数据源,而是用于对任何Block接口的实现和任何Stream

因为他们返回接口值,所有将CTR加密替换成为其它的加密模式知识一个局部的改变。客户不会感知,符合solid中依赖倒置原则

 

27 接口和方法

go中由于几乎任何事物都可以附加上方法,所有几乎(指针和接口类型除外)任何事物都满足接口的要求。

如下:任何实现了Handler的对象都可以为HTTP请求提供服务

```

type Handler interface{

  ServeHTTP(ResponseWriter,*Request)

}

 

type Counter struct{

  n int

}

 

func (ctr *Counter)ServeHTTP(w http.ResponseWriter,req *http.Request){

  ctr.n++

  fmt.Fprintf(w,"counter = %d\n",ctr.n)

}

 

//或者

 

type Counter int 

 

func (ctr *Counter)ServeHTTP(w http.ResponseWriter,req *http.Request){

  *ctr++

  fmt.Fprintf(w,"counter = %d\n",*ctr)

}

 

```

 28 如果一个类型只是用来实现接口,并且除了该接口以外没有其他被导入的方法,那就不需要导出这个类型,只导出接口,

清楚的表明其重要的是行为,而不是实现。

 

29 空白标志符的定义

  1.忽略左值 (_),和Unix系统中向 /dev/null 文件中写入数据:它认为那些需要出现但值其实可以

忽略的变量提供一个只写的占位符 

  2. 未使用的左值或者import

  3. 接口检测

30 内嵌的定义 embedding

  1. 接口和结构体内嵌(接口只能内嵌接口类型)

  2.结构体内嵌和继承的区别

    2.1 结构体内嵌一个类型时,该类型所有的方法会变成外部类型的方法,但是当这些方法被调用时,其接受的参数仍然是内部类型,而不是外部类型

    2.2 但其他语言的继承关系的话,方法变成子类的方法,并且方法调用时,接受的参数也是子类型的参数

    ```

    type Logger struct{}

    func(I Logger)Log(){

      fmt.Printf("type of Log's recevier %T\n",I)

    }

    type Job struct{

      name string

      Logger

    }

    func (j Job)Work(){

      fmt.Printf("type of work's recevier: %T\n",j)

    }

    func main(){

      job := job{}

      job.Log()

      job.Work()

    }

      

    ```

    输出:

    type of log's receiver : main.Logger

    type of Work's receiver: main Job

   

30  并发

  1 以通信实现共享并发理念

   要考虑很多细节保证对共享变量访问的正确性,并发编程变得异常复杂

  Go 采用了一种不同的方法,即共享变量通过Channel相互传递的方法解决上述问题

  著名的口号

  Do not Communicate by sharing memory; instead,share memory by communicating(不用通过共享内存来通信,而是通过通信来共享内存)

  Go的并发模型源自CSP模型(communicating Sequential Processes 通信顺序进程)

  在此并发模型下,任何时刻,仅有一个Goroutine可以访问到某个变量,这样数据竞争问题在设计上就被规避了。

  2 Groutine的定义

  每个Groutine都对应一个非常简单的模型:它是一个并发的函数执行线索,并且在多个并发的Goroutine间,资源

  是共享的。Groutine很轻,初始化栈很小

  添加一个Go关键词创建并调用方法,当函数执行结束后,Goroutine也会隐形退去

  单独的goroutine并不实用,因为执行函数无法发布其完成信号,因此需要一个Channel结构

  3 channel是Goroutine间进行通信的数据类型,与map类似,channel也是通过make分配的

  其返回值实际上是一个执向相关数据结构的引用。

  有两种类型:无缓冲channel,也称为同步channel

  有缓冲channel

  区别是make时,整形参数有无取值

  ci := make(chan int);

  cs: = make(chan *os.File,100)

  无缓冲channel使通信 有值的交换 和 同步机制组合 共同保证两个执行Goroutines运行可控。(同步)

  有缓冲channel则是让两个Goroutine可以并行处理自己的任务而不相互等待(异步)

  假如主线程有3个任务要执行,其中两个任务没有依赖,可以并行,但是第三个任务必须要等前两个完成我们就可以使用无buffer的channel

  ```

  func task1(done chan int){

    fmt.Println("task1 done")

    done <- 1

  }

  func task2(){

    fmt.Prinln("task2 done")

  }

  func task3(){

    fmt.Println("task3 done")

  }

  ```

  主线程启动了一个goroutine执行task1,然后执行task2,在task2完成后等待,task1的done信号完成后

  再执行 task3

  ```

    func main(){

      done := make(chan int)

      go task1(done)

      task2()

      <- done

      task3()

    } 

  ```

  出现的结果:task1和task2完成的时间不确定,但是,task3一定会等待1和2完成才开始执行

  task1 done  ==>  task2 done ==> task3 done

  task2 done ==> task1 done ==> task3 done

  

 

posted @ 2020-08-12 01:03  易先讯  阅读(170)  评论(0编辑  收藏  举报