1.基础类型

  1. 打印字符必须使用printf,因为println会输出字符的ascii码

    1
    fmt.Printf("ch1 = %c, ch2 = %c, %d\n", ch1, ch2, ch3)
  2. 字符串是双引号,字符是单引号

  3. 自动推导类型有两种写法

    1. var a = 1
    2. a := 1
  4. %v自动匹配格式输出,但是并不是很只能比如字符型会输出其ascii码

  5. _为匿名变量,一般用于丢弃某个函数返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package main //每个程序都要有一个main包,没有main包则会导致build无法生成exe文件

import "fmt"

func main() {
fmt.Println("hello go!!!")
var a = 10
//a := 10
b := 20
c := 30
fmt.Println("a=", a)
fmt.Printf("the type of a is %T\n", a)
fmt.Printf("a=%d,b=%d,c=%d", a, b, c)
const i = 10
fmt.Printf("the type of i is %T \n", i)
var ch1, ch2, ch3 byte
ch1 = 'a' //字符赋值
ch2 = 97 //字符的ascii码赋值
ch3 = '\n' //转义字符
fmt.Printf("ch1 = %c, ch2 = %c, %d\n", ch1, ch2, ch3)
fmt.Println("ch1 = ",ch1)
}

2.流程控制

  1. if和左大括号要在同一行

  2. 支持一个初始化表达式, 初始化字句和条件表达式直接需要用分号分隔

    1
    2
    3
    if b := 3; b == 3 {
    fmt.Println("b==3")
    }
  3. switch不用写break来跳出循环,go语言自动包含

  4. 如果不想在执行了switch的case语句后跳出switch,则用fallthrough关键字

  5. switch也支持一个初始化语句

  6. switch有三种写法,见word

  7. for 初始化条件;判断条件;条件变化{}

  8. go语言只有i++没有++i

  9. 1
    2
    3
    for{
    代码块....
    }//这种没有条件的则会无限循环
  10. range迭代函数返回值为两个,一个是下标,另一个是下标所对应元素

3.函数


  1. 自定义函数放在main前main后都可以

  2. 函数的参数不需要写var关键字

  3. 如果形参为同类型则可以有简化写法,eg:a,b int (不推荐这种写法,会很乱)

  4. 如果不知道函数传入参数个数,则用不定参数 eg:(args …int),其中args由切片实现,要访问传入的参数可以用arg[0],arg[1]…….

  5. 不定参数要写在形参的最后面

  6. 注意函数定义最常用写法,word22页

  7. 函数定义分类:

    6.1有参无返回值型

    ​ 6.1.1固定参数

    ​ 6.1.2不定参数

    6.2无参有返回值型

    ​ 6.2.1有一个返回值

    ​ 6.2.2有n个返回值

    6.3有参有返回值型

  8. 如何函数的返回值是int型,则可以直接可以用+运算

  9. 函数其实也是一种数据类型即函数类型

  10. 利用函数类型实现计算器函数,即实现多态(一个接口,但其有多种形态)课p52

  11. 匿名函数是写在main内的,其可以调用main中的全局变量

  12. 匿名函数内部改外部变量,则外部变量相应改变

  13. 多个defer时从后向前执行

  14. 注意局部变量的作用范围:

    1
    2
    3
    4
    5
    6
    7
    if flag := 3;flag == 3{

    fmt.println("flag = ",flag)

    }

    flag = 4//err,因为flag的作用范围只有if语句里面,是一个局部变量
  15. 执行到定义局部变量那句话时才会给其分配空间,当程序执行到离开作用域时,空间就被释放

  16. 当使用变量时,采用就近原则,即采用当前作用域变量,如果未定义就用全局的那个

4.工程管理

  1. 一个包是由很多go文件组成,比如go/src/fmt
  2. import . “fmt”这样导入则后续代码不需要写fmt前缀
  3. 同一个目录包名必须一样,参照src/fmt,注:fmt就是一个包
  4. 同一个目录,调用别的文件的函数,直接调用即可,无需包名引用
  5. 一个包即一个工程即一个文件夹
  6. 一个包内的不同go文件就是实现这个工程的不同功能模块
  7. 当在进行一个工程之前,先设置GOPATH为该工程的src路径
  8. 函数想被其他包调用则要将函数名首字母大写

5.复合类型

  1. &a与%p对应,不是%d

  2. p=new(T),new函数就是在内存中拿出一个T类型的内存空间,然后将该内存空间的地址赋给p

  3. 注意数组的初始化形式

  4. 如果种子参数一样,则每次运行程序产生的随机数都一样

  5. 可以将种子参数设置为当地时间,这样每次执行程序种子不同从而随机数也会 不同

  6. 数组传入到函数只是传值,普通类型变量也一样都只是传值,函数内对该变量的操作不会影响函数外的该变量。要想改变则必须传地址

  7. s := a[0:3:5],s为切片,长度为3,容量为5

  8. 切片的初始化定义方式有如下几种:

    1
    2
    s1 := []int{}//len=0,cap=0 也可以是s1:=[]int{1,2,3,4}
    s4 := make([]int, 0, 0)//第一个0是切片长度,第二个0为容量,若没指定容量则容量与长度相等
  9. 通过append函数给slice加元素

  10. s := a[0:3],在进行切片时如果没有指定max则max默认为len(5),注意不要和8混淆

  11. 切片并不是数组或数组指针,它通过内部指针和相关属性引⽤数组⽚段

  12. 改变切片值原数组值也会改变,改变数组值那么切片值也会相应改变

  13. 不管是哪一种切片,最终切片的指针都指向底层数组!!!

  14. 总结:对切片的操作会影响底层数组

  15. 切片传入到函数是传引用,函数内对其进行改变,函数外的相应也会改变

  16. 在使用多个scanf时,前面的scanf要加\n

  17. map作为函数参数传递的是引用,即函数内部对其改变,函数外部该值也会做出相应改变

  18. 结构体的定义写在main函数外部

  19. 在定义了结构体指针p1后可以通过.运算符直接操作成员。eg:p1.sex即不用*p1.sex

  20. 如果想使用其他包的函数,结构体类型,结构体成员,函数名,类型名,结构体成员变量名,首字母必须大写。如果首字母是小写,只能在同一个包里使用

6. 面向对象

  1. 方法:给自定义类型定义一种方法

  2. 不同类型可以拥有同名方法

  3. 接收者可以类比为对象中的类,即给类添加方法

  4. 继承某一个结构体,其方法也能够被继承

  5. string,*p,struct都属于接口类型,可以定义一个接口类型的切片将这三个存入

  6. 如果用户定义的类型实现了某个接口类型声明的一组方法,那么这个用户定义的类型的值就可以赋给这个接口类型的值

  7. 空接口为万能类型,可以保存任意类型的值

    1
    2
    3
    var i interface{} = 1

    var i interface{} = "abc"

7.异常处理

  1. 接口类型即万能类型,什么都可以存

  2. 1
    fmt.println(recover())//功能是出现panic时并不会让程序中断,并打印panic错误信息
  3. error是报错但不崩溃,panic是报错并崩溃程序

  4. recover()返回值为panic的错误信息

8.文本文件处理

  1. 常用字符串处理函数

  2. 结构体想要生成json,成员变量名首字母必须大写

  3. 如果想要json中的值首字母小写,要在比如结构体定义的后面加上’ json:”company” ‘其中’为反引号

  4. 利用’ json:”-“ ‘表示此字段不会输出到屏幕

  5. 1
    2
    3
    //用make创建切片和map
    slice=make([]string,0,100)//len=0,cap=100
    map1=make(map[string]interface{},4)//创建一个键为字符串型,值为任何类型且长度为4的字典
  6. json后返回的结果为字符列表,要通过string(result)转换为json格式

  7. json需要先转换成[]byte,才能反转为结构体或者map

  8. 空接口类型值如何赋值给string等类型,看(中)p56

  9. json解析到结构体更加简单,因为解析完之后每个字段什么类型很清楚的被定义了。而对于解析到map类型,其值全是空接口,要通过类型断言判断每个值的类型以及赋值给字符串

  10. fmt.println()相当于os.stdout.WriteString()

  11. Sprintf() 是把格式化字符串输出到指定的字符串中,可以用一个变量来接受,然后在打印

  12. 当文件读取完时err=io.EOF

  13. 注意file.read返回值有两个一个是读取长度n,一个是err

9.并发编程

  1. 主协程退出,其他子协程也要跟着退出

  2. main函数即主协程

  3. 设置核数越多,时间片就越短

  4. 1
    2
    3
    4
    //1.无缓冲型channel特点:
    写入的时候对方未读就会阻塞任务
    读的时候没有数据也会阻塞
    即要保证数据放入时被立即取出
  5. 可通过range遍历channel内容

  6. channel传参,是引用传递

  7. 双向管道传参可以变为单向

  8. 1
    2
    3
    4
    5
    timer1 := time.NewTimer(time.Second * 2)//timer1为定时器,三秒后定时器就会向自己的C字节管道发送一个time.Time类型的元素值
    //timer1.Stop()停止计时器
    //ok := timer1.Reset(1*time.Second)重置计时器1s后写入
    t2 := <-timer1.C //定时器可实现延时功能,类似于sleep

10.HTTP编程

  1. socket编程中write和read函数的传参都是字节切片,比如发送hello,即conn.write([]byte(“hello”))

  2. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    n, err :=conn.read(buf)//接受来自对方发送的内容
    if err != nil{
    if err == io.EOF{
    fmt.println("文件接收完毕")
    }else{
    fmt.println("err =",err)
    }
    return
    }
    f.write(buf[:n])//往文件写入内容
  3. 定义在main函数外部的为全局变量

  4. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package main //服务器端代码示例

    import (
    "fmt"
    "net/http"
    )

    //服务端编写的业务逻辑处理程序myhandler
    //w, 给客户端回复数据
    //req为结构体, 读取客户端发送的数据,参数就是这两不能变
    func myHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello world")
    }

    func main() {
    http.HandleFunc("/go", myHandler)// 请求127.0.0.1/go时调用myhandler

    //在指定的地址进行监听,开启一个HTTP
    http.ListenAndServe("127.0.0.1:8000", nil)
    }
  5. 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    package main //客户端代码

    import (
    "fmt"
    "io"
    "log"
    "net/http"
    )

    func main() {

    //get方式请求一个资源
    //resp, err := http.Get("http://www.baidu.com")
    //resp, err := http.Get("http://www.neihan8.com/article/index.html")
    resp, err := http.Get("http://127.0.0.1:8000/go")
    if err != nil {
    log.Println(err)
    return
    }

    defer resp.Body.Close() //关闭

    fmt.Println("header = ", resp.Header)
    fmt.Printf("resp status %s\nstatusCode %d\n", resp.Status, resp.StatusCode)
    fmt.Printf("body type = %T\n", resp.Body)


    //以下为读取服务器回应的主体数据部分,因为回应的主体数据部分是用resp.Body来获取,该接口是一个流所以要通过read的方式将其通过for循环一点一点的读入buf里面
    buf := make([]byte, 2048) //切片缓冲区
    var tmp string

    for {
    n, err := resp.Body.Read(buf) //读取body包内容
    if err != nil && err != io.EOF {
    fmt.Println(err)
    return
    }

    if n == 0 {
    fmt.Println("读取内容结束")
    break
    }
    tmp += string(buf[:n]) //累加读取的内容
    }

    fmt.Println("buf = ", string(tmp))
    }