特有语法
Goland运行测试问题
运行测试显示 No tests were run


花了几小时排除后发现是Gorm的Debug方法导致,去掉之后又正常了


chan(信道)
信道是带有类型的管道,你可以通过它用信道操作符 <- 来发送或者接收值。
ch <- v // 将 v 发送至信道 ch。
v := <-ch // 从 ch 接收值并赋予 v。
“箭头”就是数据流的方向。
创建chan
// 双项通道,支持读写
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被关闭。
方法一:
for{
if value,ok:=<-ch;ok{
//do somthing
}else{
break;//表示channel已经被关闭,退出循环
}
}
方法二:
//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
将控制转移到被标记的语句
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 会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。
/* 摘自: https://gobyexample.com/select */
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
c1 <- "one"
}()
go func() {
time.Sleep(2 * time.Second)
c2 <- "two"
}()
// 使用select同时等待值,当到达值打印起值
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}
interface
package main
import (
"fmt"
)
type Phone interface {
call()
}
type AndroidPhone struct {
}
type IPhone struct {
}
func (a AndroidPhone) call() {
fmt.Println("我是安卓手机,可以打电话了")
}
func (i IPhone) call() {
fmt.Println("我是苹果手机,可以打电话了")
}
func main() {
//定义接口类型的变量
var phone Phone
phone = new(AndroidPhone)
fmt.Printf("%T,%v,%p \n", phone, phone, &phone)
phone.call()
phone = AndroidPhone{}
fmt.Printf("%T,%v,%p \n", phone, phone, &phone)
phone.call()
phone = new(IPhone)
fmt.Printf("%T,%v,%p \n", phone, phone, &phone)
phone.call()
phone = IPhone{}
fmt.Printf("%T,%v,%p \n", phone, phone, &phone)
phone.call()
}
关于duck typing
duck typing是描述事物的外部行为而非内部结构。
duck typing关注的不是对象的类型本身,而是它是如何使用的。
空接口
空接口中没有任何方法。任意类型都可以实现该接口。空接口这样定义:interface{},也就是包含0个方法(method)的interface。
空接口可表示任意数据类型,类似于Java中的object。
空接口常用于以下情形。
println的参数就是空接口。
定义个map:key是string,value是任意数据类型。
定义一个切片,其中存储任意类型的数据。
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)
}
接口对象类型
/*
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语言)
package main
import "fmt"
// 声明指针
var ip *int //指向整型的指针
var fp *float32 //指向浮点型的指针
func main() {
//声明实际变量
var a int = 120
//声明指针变量
var ip *int
//给指针变量赋值,将变量a的地址赋给ip
ip = &a
//打印ip的类型和值
fmt.Printf("ip的类型是%T,值是%v \n", ip, ip)
//打印变量*ip的类型和值
fmt.Printf("*ip变量的类型是%T,值是%v \n", *ip, *ip)
// 空指针
// 当一个指针被定义后没有分配到任何变量时,它的值为nil。nil指针也成为空指针。nil在概念上和其他语言的 null、None、NULL一样,都指代零值或空值。
// 通过指针修改变量的值
b := 3158
c := &b
*c++
fmt.Println("b的新值:", b)
// 指针的指针
/*
如果一个指针变量存放的又是另一个指针变量的地址,则称这个指针变量为指向指针的指针变量。当定义一个指向指针的指针变量
时,第一个指针存放第二个指针的地址,第二个指针存放变量的地址。
*/
var d int
var ptr *int
var pptr **int
a = 1234
/* 指针ptr地址 */
ptr = &d
fmt.Println("ptr", ptr)
/*指向指针ptr的地址*/
pptr = &ptr
fmt.Println("pptr", ptr)
/*获取pptr的值*/
fmt.Printf("变量a=%d\n", d)
fmt.Printf("指针变量 *ptr=%d\n", *ptr)
fmt.Printf("指向指针的指针变量**ptr=%d\n", **pptr)
}
new
https://golang.google.cn/ref/spec#Allocation
package main
import "fmt"
type S struct { a int; b float64}
func main() {
s := new(S)
// 指针类型
fmt.Println(s)
}