go 学习

一、程序基础

  • 环境变量

go env 命令可以查看 go 的环境变量。

GOROOT 是 go 的安装路径

GOPATH 是 go 的工作目录:

    GOPATH/src 用于存放项目源码;

    GOPATH/bin 用于存放项目编译后的二进制文件;

    GOPATH/pkg 用于存放项目下载的依赖包文件。

  • 基本知识

(1)一个路径下的所有文件只能声明同一个包名

(2)文件名跟函数名没关系

(3)函数名需要大写开头,推荐大驼峰

  • go命令

go  get  ["github.com/jinzhu/gorm/dialects/mysql"]     // 下载包到GOPATH/pkg/mod

go  build  [main.go]     // 编译,生产main.exe

go  clean  [main.go]    // 清理编译生成的文件

go  run  [main.go]       // 运行

go  vet  [main.go]       // 检查错误

go  fmt  [main.go]       // 格式化文件

二、知识点

1、声明时类型后置;

2、可以并列初始化、赋值;

3、switch 的 case 自带 break 属性;

func test09() {
	var a int
	for true {
		fmt.Scan(&a)
		switch a {
		case 1:
			fmt.Println("the value is ", "111")
		case 2:
			fmt.Println("the value is ", "222")
		case 3, 4, 5:
			fmt.Println("the value is ", "333")
		default:
			fmt.Println("default")
		}
	}
}

4、闭包/匿名,多次调用,可以使局部变量保持静态属性;

匿名函数:

(1)如果某个函数只是用一次,就可以考虑使用匿名函数;

(2)将匿名函数赋值给一个变量,然后通过变量调用;匿名函数作为参数传递,可以使程序解耦合;

闭包:

(1)由 一个匿名函数 与其 引用的外部变量 组成的整体,其中引用的外部变量将随着闭包的调用变化。引用的外部变量 相当于 C的局部静态变量,只初始化一次。或者可以将闭包立即为一个类,其中引用的外部变量相当于类的成员变量,匿名函数相当于类的方法;

(2)与普通函数相比,闭包可以保留开始引用的值,传入一次反复使用,避免反复传递相同的参数值;(可以用类替代)

package main

import (
	"fmt"
	"strings"
)

type suffixMake struct {
	suffix string
}

func (sm *suffixMake) make(name string) string {
	if !strings.Contains(name, sm.suffix) {
		return name + sm.suffix
	}
	return name
}

func main() {

	//累加器
	f := addNumber1()
	fmt.Println(f(1)) //11
	fmt.Println(f(1)) //12

	//补后缀-闭包
	f3 := makeSuffix(".jpg")
	fmt.Println(f3("cat"))     //11
	fmt.Println(f3("dog.jpg")) //12

	//补后缀-用类代替
	f4 := suffixMake{".jpg"}
	fmt.Println(f4.make("cat"))     //11
	fmt.Println(f4.make("dog.jpg")) //12
}

//闭包-累加器
func addNumber1() func(i int) int {
	var n int = 10
	return func(i int) int {
		n += i
		return n
	}
}

//闭包-补后缀
func makeSuffix(suffix string) func(string) string {
	return func(name string) string {
		if !strings.Contains(name, suffix) {
			return name + suffix
		}
		return name
	}
}

Go 语言函数闭包 | 菜鸟教程

5、iota 属性

func test05() {
	const (
		a = iota
		b = iota
		c = iota
		d = 10
		e = iota
		f = iota + 10
	)
	const g = 10
	const (
		h = iota
	)
	fmt.Println(a, b, c, d, e, f)
	fmt.Println(g, h)
}

0 1 2 10 4 15
10 0

6、局部变量可以简单初始化,全局变量不可以这样初始化

a := 2

7、不需要分号结尾

8、类方法的读/写

9、局部变量会被初始化

10、引用传递 与 值传递

(1)传递数组 不是 引用传递,传递数组指针是引用

func main() {
	var arr1 = [2]int{1}
	arrfun(arr1)
	fmt.Println(arr1)
}

func arrfun (arr1 [2]int)  {
	arr1[0] = 10
	arr1[1] = 20
}
[1 0]
func main() {
	var arr1 = [2]int{1}
	arrfun(&arr1)
	fmt.Println(arr1)
}

func arrfun (arr1 *[2]int)  {
	arr1[0] = 10
	arr1[1] = 20
}
[10 20]

(2)引用类型和值类型

引用类型:指针、slice,map、channel、interface

值类型:bool,int簇,float簇,array,string,struct

(3)不管是值传递还是引用传递,传递的都是变量的副本,不同的是,值传递是值的拷贝,引用传递是地址的拷贝。一般来说,传地址效率高,因为数据量小。

(4)内存分配

值类型:变量直接存储值,内存通常在栈中分配;

引用类型:变量存储一个地址,该地址的内存空间才存储真正地数据,内存通常在堆上分配;当没有任何变量引用这个地址时,该地址对应的数据空间就变成一个垃圾,由GC来回收。

11、结构体指针访问成员也是用【.】号,而不是【->】。

12、切片,即动态数组,定义时可不指定大小,也可make大小。通过追加方式添加,可通过主动拷贝和扩容。

(1)reslice

s2 := s1[1:2]

(2)向后扩展slice

reslice 和 append 的时候,只要cap不超过底层数组的大小,新slice就会继承cap的大小,一旦触发扩容,cap为扩容后的新数组的大小。

s[i] 不能超过len的大小

s[i:j] 可以向后超过len的范围,但不能超过cap的范围

13、打印输出

import "fmt"

fmt.Print("中国的首都是\n", capital)                  //输出

fmt.Printf("中国的首都是", %s\n", capital)      //格式化输出

fmt.Println("中国的首都是", capital)               //换行输出,自动判断类型

14、不支持隐式类型转换,需要强制类型转换

var a int = 10

var b float32

b = float(a)

15、map,为无序 map

16、全局变量不需要前置声明,函数也不需要前置声明

17、只有后置++/--,并且只能单独使用,不能参与其他运算

func test06() {
	var a int
	a++
	fmt.Println(a)
	a--
	fmt.Println(a)
}

18、条件语句中,必须使用 bool 表达式;数值不能被认为是 true 或者 false,数值要使用逻辑表达式,产出 bool 结果;

func test07() {
	a, b := 1, 2
	if a == 1 && b == 2 {
		fmt.Println(a, b)
	}
	if a > 0 {
		fmt.Println(a)
	}
	if a != 0 {
		fmt.Println(a)
	}
}

19、输入输出

func test08() {
	var (
		a int
		b int
	)
	fmt.Scan(&a, &b)    //输入
	fmt.Println(a, b)   //输出
}

20、外部包要使用内部函数,需要将包内函数的首字母大写,否则会报错

Unexported function 'get' usage

21、通道 chan 是有类型的

var c chan int

22、通道阻塞

(1)通道的 生产者一直不能放入数据 或 消费者一直不能消费数据,都会使程序死锁 !

(2)通道是用于协程之间通信的,其 生产者 和 消费者 需要位于不同的协程中。

(3)通道可以设置大小(带缓冲区),带缓冲区可以解耦生产者,生产由同步变为异步,但 通道满 依然会造成 生产者 等待甚至死锁,通道空 依然会造成 消费者 等待甚至死锁。

all goroutines are asleep - deadlock!

(4)通道是阻塞型的,因此下面的用法和不加ok一样,因为要么可以从通道获取值ok=true,要么通道会阻塞

x, ok := <-c

23、通道可以使主程序等待。

24、初始化

(1)切片初始化

       不指定容量的切片,后续append即可(超过容量自动翻倍扩容),相当于容器。

s:=[]int{}         //相当于  make([]int, 0, 0)

       指定大小和容量的切片,不超过大小使用下标即可,相当于数组,超过大小使用append(超过容量自动翻倍扩容)。

s := make([]int, 1, 2)

(2)其他

# 数组初始化
var arr [2]int = [2]int{}
var arr [2]int = [2]int{1}
var arr [2]int = [2]int{1,2}
var arr = [2]int{1,2}

arr := [2]int{1,2}

# 切片初始化,切片应该
var arr []int = []int{}      // len=cap=0,空切片,不能用
var arr []int = []int{1,2}   // len=cap=2
var arr []int = make([]int, 0, 4)    // len=0(从0开始填内容), cap=4
var arr = make([]int, 0, 4)  // len=0, cap=4

arr := make([]int, 0, 4)  // len=2, cap=4

arr := []int{}
arr = append(arr, 1,2,3,4)


# map 初始化
m2 := map[string]string{"k":"l"}
fmt.Println(m2)
m3 := make(map[string]string)
m3["kk"] = "ll"
fmt.Println(m3)

25、不支持重载

26、面向接口编程,interface 接口类 相当于 指针类型

       接口类 作为 形参,把它看做是 指针,实参 传递 实现类 的地址。

type human interface {
}

type man struct {
}

func test1 (human *human) {       //错误
}

func test2 (human human) {        //正确
}

func main() {
    man1 := &man{}
    test1(man1)           //错误

    man2 := man{}
    test2(man2)           //错误

    man3 := &man{}
    test2(man3)           //正确
}

27、select 多路复用

(1)用于通信,每一个case代表一个通信操作,即在某个channel上进行发送或者接收

(2)类似 switch,执行满足条件的分支中的一个分支,且只执行一次

(3)有阻塞性质,如果没有一个分支满足,会阻塞等待,甚至死锁

(4)如果需要循环,外层需要加 for,并且需要注意避免死锁:

         加 default 和 sleep,如果条件不满足,可能会不停执行 default,因此需要加延时

         加 tick,每隔一段时间执行一次 tick 分支

(5)一个没有任何 case 的 select 语句写作 select{},表示永久阻塞

package main

import "fmt"

func main() {

	var ch1 = make(chan int, 1)
	var ch2 = make(chan int, 1)

    ch1 <- 1
    ch2 <- 2

	select {
	case x := <-ch1:
		fmt.Println("ch1 x = ", x)
	case x := <-ch2:
		fmt.Println("ch2 x = ", x)
    default:
        fmt.Println("default")
	}

}
package main

import "fmt"

func main() {

	var ch1 = make(chan int, 1)
	var ch2 = make(chan int, 1)

    ch1 <- 1
    ch2 <- 2

    for {
    	select {
    	case x := <-ch1:
    		fmt.Println("ch1 x = ", x)
    	case x := <-ch2:
    		fmt.Println("ch2 x = ", x)
        default:
            fmt.Println("default")
            time.Sleep(1*time.Second)
	    }
    }

}
package main

import "fmt"

func main() {

	var ch1 = make(chan int, 1)
	var ch2 = make(chan int, 1)

    ch1 <- 1
    ch2 <- 2
    tick := time.Tick(1*time.Second)

    for {
    	select {
    	case x := <-ch1:
    		fmt.Println("ch1 x = ", x)
    	case x := <-ch2:
    		fmt.Println("ch2 x = ", x)
        case <-tick
            fmt.Println("tick")
	    }
    }

}

28、init函数

(1)执行顺序

执行被引入包内的全局变量定义

执行被引入包内的init

执行main包内的全局变量定义

执行main包内的init

(2)一个包内可以有多个init函数

(3)主要场景,初始化变量,初始化链接

29、defer

(1)defer栈,先进后出

(2)在调用的地方进栈,进栈的同时,使用的变量也会被压入栈中。

       如下,因此两次打印的结果 n 不一致,defer 压入的 n 值是10,并不是11

package main

import "fmt"

func main() {
	var n int = 10
	test(n)
}

func test(n int) {
	defer fmt.Println("n1", n) // 10
	n++
	fmt.Println("n2", n)  // 11
}

(3)defer 的使用主要是用来关闭一些打开的资源,如 文件句柄、数据库链接 等。

30、len

       对于string,len计算的是字节数,因为string本质上是[]byte;

       go是uft-8编码,字母和数字等(ascii)都是一个字节,中文为3个字节,因此

       len("hello世界")=5+3*2=11

       存在中文时,for range 是按字符个数遍历的,因此仍然可以正常遍历,但 for index 则不行,需要将 string 转化为 []rune类型,然后才能 for index 遍历;

       rune类型占用4个字节;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值