特有语法 **************************** Goland运行测试问题 ============================ 运行测试显示 ``No tests were run`` .. image:: /_static/tech-stack/language/go/问题SQL.png .. image:: /_static/tech-stack/language/go/NoTestsWereRun.png 花了几小时排除后发现是Gorm的Debug方法导致,去掉之后又正常了 .. image:: /_static/tech-stack/language/go/正常SQL.png .. image:: /_static/tech-stack/language/go/正常测试.png chan(信道) ======================= 信道是带有类型的管道,你可以通过它用信道操作符 <- 来发送或者接收值。 .. code-block:: golang ch <- v // 将 v 发送至信道 ch。 v := <-ch // 从 ch 接收值并赋予 v。 “箭头”就是数据流的方向。 创建chan ----------------------- .. code-block:: golang // 双项通道,支持读写 ch := make(chan int) // 单项通道,只能读 var ch_read <-chan int // 单项通道,只能写 var ch_write chan<- int 定义只读和只写的channel意义不大,一般用于在参数传递中 默认情况下,发送和接收操作在另一端准备好之前都会阻塞。 这使得 Go 程可以在没有显式的锁或竞态变量的情况下进行同步。 遍历chan ------------------------ 原文链接:https://blog.csdn.net/zhaominpro/article/details/77584534 读channel时,第二个值返回为false时表示channel被关闭。 方法一: .. code-block:: golang for{ if value,ok:=<-ch;ok{ //do somthing }else{ break;//表示channel已经被关闭,退出循环 } } 方法二: .. code-block:: golang //range ch:=make(chan int ,3) ch<-1 ch<-2 ch<-3 for value:=range ch{ fmt.Print(value) } //输出:123 //然后会一直阻塞当前协程,如果在其他协程中调用了close(ch),那么就会跳出for range循环。这也就是for range的特别之处 goto =========================== 将控制转移到被标记的语句 .. code-block:: golang func main() { var C, c int //声明变量 C = 1 /*这里不写入for循环是因为for语句执行之初会将C的值变为1,当goto A时for 语句会重新执行(不是重新一轮循环) */ LOOP: for c < 50 { C++ //C=1不能写入for这里就不能写入 for c = 2; c < C; c++ { if C%c == 0 { goto LOOP //若发现因子不是素数 } } fmt.Printf("%d \t", c) } } select ========================== select 语句使一个 Go 程可以等待多个通信操作。 select 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。 .. literalinclude:: select/main.go :language: go interface ========================== .. literalinclude:: interface/main.go :language: go 关于duck typing --------------------------- - duck typing是描述事物的外部行为而非内部结构。 - duck typing关注的不是对象的类型本身,而是它是如何使用的。 空接口 ------------------------- * 空接口中没有任何方法。任意类型都可以实现该接口。空接口这样定义:interface{},也就是包含0个方法(method)的interface。 * 空接口可表示任意数据类型,类似于Java中的object。 空接口常用于以下情形。 - println的参数就是空接口。 - 定义个map:key是string,value是任意数据类型。 - 定义一个切片,其中存储任意类型的数据。 .. code-block:: go package main import ( "fmt" ) type A interface { } type Cat struct { name string age int } type Person struct { name string sex string } func main() { var a1 A = Cat{"Mimi", 1} var a2 A = Person{"Steven", ""} var a3 A = "Learn golang with me!" var a4 A = 100 var a5 A = 3.14 fmt.Println("-----------------") //1.fmt.println参数就是空接口 fmt.Println("println的参数就是空接口,可以是任何数据类型", 100, 3.14, Cat{"旺旺", 2}) //2.定义map,value是任何数据类型 map1 := make(map[string]interface{}) map1["name"] = "Daniel" map1["age"] = 13 map1["height"] = 1.71 fmt.Println(map1) fmt.Println("-----------------") //3.定义一个切片,其中存储任意数据类型 slice1 := make([]interface{}, 0, 10) slice1 = append(slice1, a1, a2, a3, a4, a5) fmt.Println(slice1) } 接口对象类型 ---------------------------- .. code-block:: go /* instance,ok:=接口对象.(实际类型) 如果该接口对象是对应的实际类型,那么instance就是转型之后的对象,ok的值为true,配合 if...else if...语句使用。 接口对象转型第二种方式示例如下。 接口对象.(type) */ //样例:接口对象转型。 package main import ( "fmt" "math" ) //1.定义接口 type Shape interface { perimeter() float64 area() float64 } //2.矩形 type Rectangle struct { a, b float64 } //3.三角形 type Triangle struct { a, b, c float64 } //4.圆形 type Circle struct { radius float64 } //定义实现接口的方法 func (r Rectangle) perimeter() float64 { return (r.a + r.b) * 2 } func (r Rectangle) area() float64 { return r.a * r.b } func (t Triangle) perimeter() float64 { return t.a + t.b + t.c } func (t Triangle) area() float64 { //海伦公式 p := t.perimeter() / 2 //半周长 return math.Sqrt(p * (p - t.a) * (p - t.b) * (p - t.c)) } func (c Circle) perimeter() float64 { return 2 * math.Pi * c.radius } func (c Circle) area() float64 { return math.Pow(c.radius, 2) * math.Pi } //接口对象转型方式1 //instance,ok:=接口对象.(实际类型) func getType(s Shape) { if instance, ok := s.(Rectangle); ok { fmt.Printf("矩形:长度%.2f,宽度%.2f,", instance.a, instance.b) } else if instance, ok := s.(Triangle); ok { fmt.Printf("三角形:三边分别:%.2f,%.2f,%.2f,", instance.a, instance.b, instance.c) } else if instance, ok := s.(Circle); ok { fmt.Printf("圆形:半径%.2f,", instance.radius) } } //接口对象.(type),配合switch和case语句使用 func getType2(s Shape) { switch instance := s.(type) { case Rectangle: fmt.Printf("矩形:长度为%.2f,宽为%.2f,\t", instance.a, instance.b) case Triangle: fmt.Printf("三角形:三边分别为%.2f,%.2f,%.2f,\t", instance.a, instance.b, instance.c) case Circle: fmt.Printf("圆形:半径为%.2f,\t", instance.radius) } } func getResult(s Shape) { getType2(s) fmt.Printf("周长:%.2f,面积:%.2f \n", s.perimeter(), s.area()) } func main() { var s Shape s = Rectangle{3, 4} getResult(s) showInfo(s) s = Triangle{3, 4, 5} getResult(s) showInfo(s) s = Circle{1} getResult(s) showInfo(s) x := Triangle{3, 4, 5} fmt.Println(x) } func (t Triangle) String() string { //实现了系统接口,最后的打印部分会改变 return fmt.Sprintf("Triangle对象,属性分别为:%.2f,%.2f,%.2f", t.a, t.b, t.c) } func showInfo(s Shape) { fmt.Printf("%T,%v \n", s, s) fmt.Println("----------------") } 指针 ============================= * 指针是存储另一个变量的内存地址的变量。 * 例如:变量b的值为156,存储在内存地址0x1040a124。变量a持有b的地址,则a被认为指向b。 * 在Go语言中使用取地址符(&)来获取变量的地址,一个变量前使用&,会返回该变量的内存地址。 * Go语言指针的最大特点是:指针不能运算(不同于C语言) .. literalinclude:: pointer/main.go :language: go new =============================== https://golang.google.cn/ref/spec#Allocation .. code-block:: golang package main import "fmt" type S struct { a int; b float64} func main() { s := new(S) // 指针类型 fmt.Println(s) }