0
点赞
收藏
分享

微信扫一扫

go入门到精通

灵魂跑者 04-02 20:00 阅读 2
golang笔记

安装


由于官网被墙,不用科学上网的话是访问不了的,不过可以从Go下载 - Go语言中文网 - Golang中文社区 (studygolang.com)下载最新的go安装包

  • windows

    直接下载安装包安装即可,安装完后在命令行执行go version查看go版本检查是否安装成功

  • Linux环境

    参考另一篇文章Ubuntu下go语言环境搭建_g++ : executable file not found in $path-CSDN博客

编辑器


用vs code或者goland都可以,看个人习惯

goland的话可以在Other Versions - GoLand (jetbrains.com)下载2022.1.4及更老的版本,这些版本下载下来是免激活的,有效期直接到2099年

运行和编译


运行:go run main.go

编译:go build main.go

编译成功后会生成可执行文件

go语法


数据类型

  1. 基本类型

    整型:int, int8, int16, int32, int64,uint, uint8, uint16, uint32, uint64等

    浮点:float32,float64

    布尔(bool):true,false

    字符串

  2. **聚合类型:**数组和结构体

  3. **引用类型:**指针,切片,map集合,函数和Channel

  4. 接口类型:interface

变量声明

有以下三种方式。

# 1) a的默认值为0
var a int

# 2) 声明并初始化a,a自动赋值为int
var a = 1

# 3) 简写声明
message := "hello world"

还可以在一行声明多个变量

var b, c int = 2, 3

//不指定任何类型
var nhooo4, nhooo5, nhooo6 = 100, "GFG", 7896.46

var(
     nhooo1 = 100
     nhooo2 = 200.57
     nhooo3 bool
     nhooo4 string = "www"
)

数组

	//一维数组声明、赋值和遍历
	var a = [5]int{11, 22, 33, 44}
	a[4] = 55
	for k, v := range a {
		fmt.Println(k, v)
	}

	//二维数组声明和遍历
	var b = [3][2]int{{11, 22}, {33, 44}, {55, 66}}
	for k, v := range b {
		for ki, vi := range v {
			fmt.Println(k, ki, vi)
		}
	}

	//自动识别数组长度
	var c = [...]int{11, 22, 33, 44}
	fmt.Println(c)

数组有一定限制,比如不能修改数组长度、不能添加元素、不能获取子数组

如果不给数组赋值,那么数组的默认值是0或空值

字符串

声明

package main

import "fmt"

func main() {

    //创建并初始化
    //使用双引号,支持转义字符
    My_value_1 := "Welcome to nhooo"

    //添加转义字符
    My_value_2 := "Welcome!\nnhooo"

    //使用反引号,不支持转义字符
    My_value_3 := `Hello!nhooo`

    //添加转义字符
    //原始文本
    My_value_4 := `Hello!\nnhooo`

    //显示
    fmt.Println("String 1: ", My_value_1)
    fmt.Println("String 2: ", My_value_2)
    fmt.Println("String 3: ", My_value_3)
    fmt.Println("String 4: ", My_value_4)
    
    //创建和初始化一个字节片
    myslice1 := []byte{0x47, 0x65, 0x65, 0x6b, 0x73} 
  
    //从切片创建字符串
    mystring1 := string(myslice1) 
    fmt.Println("String 5: ", mystring1)
    
    //输出字符串长度
	fmt.Println("String 5.len: ", len(mystring1))
}

输出结果:
String 1:  Welcome to nhooo
String 2:  Welcome!     
nhooo                   
String 3:  Hello!nhooo  
String 4:  Hello!\nnhooo
String 5:  Geeks        
String 5.len:  5  

常用函数

package main

import (
	"fmt"
	"strings"
)

func main() {
	//比较字符串使用比较函数
	fmt.Println(strings.Compare("gfg", "Geeks"))

	//使用+运算符连接字符串
	str3 := "hello"
	str4 := "world"
	result := str3 + " " + str4
	fmt.Println(result)
	//或者这样
	result = fmt.Sprintf("%s %s", str3, str4)
	fmt.Println(result)

	//使用join()函数将切片连接成字符串
	myslice := []string{"Welcome", "To", "nhooo", "Portal"}
	result = strings.Join(myslice, "-")
	fmt.Println(result)

	//字符串拆分成切片
	res4 := strings.Split("nhooo,geeks", ",")
	fmt.Println(res4)

	//包含
	str1 := "Welcome to Nhooo for Nhooo "
	res1 := strings.Contains(str1, "Nhooo")
	fmt.Println(res1)

	//索引
	res2 := strings.Index(str1, "Nhooo")
	fmt.Println(res2)
}

输出结果:
1
hello world            
hello world            
Welcome-To-nhooo-Portal
[nhooo geeks]          
true                   
11     

切片

  • 声明
package main

import "fmt"

func main() {
	//使用var关键字,创建切片
	var my_slice_1 = []string{"nhooos", "for", "nhooos"}
	fmt.Println("My Slice 1:", my_slice_1)

	//使用简写声明
	my_slice_2 := []int{12, 45, 67, 56, 43, 34, 45}
	fmt.Println("My Slice 2:", my_slice_2)

	//创建一个数组
	arr := [7]string{"一", "二", "三", "四", "五", "六", "七"}

	//用make创建切片,长度为4,容量为7
	var my_slice_3 = make([]int, 4, 7)
	my_slice_3[0] = 1//赋值
	fmt.Printf("Slice 3 = %v, \nlength = %d, \ncapacity = %d\n", my_slice_3, len(my_slice_3), cap(my_slice_3))

	//显示数组
	fmt.Println("数组:", arr)

	//通过截取数组或切片创建切片,从下标1(包括)到6(不包括)共5个元素
	myslice := arr[1:6]

	//显示切片
	fmt.Println("切片:", myslice)

	//显示切片的长度
	fmt.Printf("切片长度: %d", len(myslice))

	//显示切片的容量
	fmt.Printf("\n切片容量: %d", cap(myslice))
}


输出结果:
My Slice 1: [nhooos for nhooos]
My Slice 2: [12 45 67 56 43 34 45]
Slice 3 = [1 0 0 0],              
length = 4,                       
capacity = 7                      
数组: [一 二 三 四 五 六 七]      
切片: [二 三 四 五 六]            
切片长度: 5                       
切片容量: 6 

在上面的实例中,我们从给定的数组中创建一个切片。这里,片的指针指向索引1,因为片的下界被设置为1,所以它开始访问来自索引1的元素。切片的长度为5,表示切片中元素的总数为5,而切片6的容量表示最多可以存储6个元素,计算方式为len(arr[切片第一个元素位置:]),即不管切片截取数组的多少,直接用原数组容量-截取原数组的第一个位置下标。

  • 切片扩容
package main

import "fmt"

func main() {
	// 创建一个切片
	slice := []int{1, 2, 3, 4, 5}

	// 获取切片长度和容量
	length := len(slice)
	capacity := cap(slice)

	fmt.Printf("Length: %d, Capacity: %d\n", length, capacity)
    //结果:Length: 5, Capacity: 5

	// 追加元素
	slice = append(slice, 6)

	// 再次获取长度和容量
	length = len(slice)
	capacity = cap(slice)

	fmt.Printf("Length: %d, Capacity: %d\n", length, capacity)
    //结果:Length: 6, Capacity: 10
	// 2次追加元素
	slice = append(slice, 7)

	// 再次获取长度和容量
	length = len(slice)
	capacity = cap(slice)

	fmt.Printf("Length: %d, Capacity: %d\n", length, capacity)
    //结果:Length: 7, Capacity: 10
}

扩容策略:如果切片的容量小于 1024 个元素,于是扩容的时候就翻倍增加容量。总容量从原来的1个翻倍到现在的2个。

一旦元素个数超过 1024 个元素,那么增长因子就变成 1.25 ,即每次增加原来容量的四分之一。

  • 切片复制

    func copy(dst, src []Type) int
    

    此处,dst表示目标切片,而src表示源切片,它将返回要复制的元素数量

    package main
    
    import "fmt"
    
    func main() {
    
        //创建切片
        slc1 := []int{58, 69, 40, 45, 11, 56, 67, 21, 65}
        var slc2 []int
        slc3 := make([]int, 5)
        slc4 := []int{78, 50, 67, 77}
    
        //复制之前
        fmt.Println("Slice_1:", slc1)
        fmt.Println("Slice_2:", slc2)
        fmt.Println("Slice_3:", slc3)
        fmt.Println("Slice_4:", slc4)
    
        //复制切片
        copy_1 := copy(slc2, slc1)
        fmt.Println("\nSlice:", slc2)
        fmt.Println("复制的元素总数:", copy_1)
    
        copy_2 := copy(slc3, slc1)
        fmt.Println("\nSlice:", slc3)
        fmt.Println("复制的元素总数:", copy_2)
    
        copy_3 := copy(slc4, slc1)
        fmt.Println("\nSlice:", slc4)
        fmt.Println("复制的元素总数:", copy_3)
    
        //这里不要混淆,因为在上面
        //复制了slc4的代码行
        //并因此进行永久修改,即
        // slc 4包含[58 69 40 45]
        copy_4 := copy(slc1, slc4)
        fmt.Println("\nSlice:", slc1)
        fmt.Println("复制的元素总数:", copy_4)
    }
    输出:
    
    Slice_1: [58 69 40 45 11 56 67 21 65]
    Slice_2: []
    Slice_3: [0 0 0 0 0]
    Slice_4: [78 50 67 77]
    
    Slice: []
    复制的元素总数: 0
    
    Slice: [58 69 40 45 11]
    复制的元素总数: 5
    
    Slice: [58 69 40 45]
    复制的元素总数: 4
    
    Slice: [58 69 40 45 11 56 67 21 65]
    复制的元素总数: 4
    

控制语句

// for 循环
for i := 0; i < 4; i++{ 
  fmt.Printf(i)   
} 

// 无限循环 
for { 
  fmt.Printf("nhooo\n")   
} 

//for range循环
for i, j:= range rvariable{
   // 语句..
}

// for遍历 channel
chnl := make(chan int)
go func() {
    chnl <- 100
    chnl <- 1000
    chnl <- 10000
    chnl <- 100000
    close(chnl)
}()
for i := range chnl {
    fmt.Println(i)
}

//select
c := make(chan int) 
select { 
case <-c: 
default: 
    fmt.Println("!.. Default case..!") 
} 

函数

package main

import (
	"fmt"
	"strings"
)

// 普通函数
func area(length, width int) int {
	Ar := length * width
	return Ar
}

// 可变参数函数联接字符串
func joinstr(element ...string) string {
	return strings.Join(element, "-")
}

func main() {
	//调用普通函数
	Ar := area(10, 20)
	fmt.Println(Ar)

	//调用可变参数函数
	fmt.Println(joinstr("hello", "world", "!!"))

	//在匿名函数
	func(ele string) {
		fmt.Println(ele)
	}("hi")
}
  • main函数

在Go语言中,main包是一个特殊的软件包,与可执行程序一起使用,并且该package包含*main()函数。在main()函数是一种特殊类型的函数,它是可执行程序的入口点。它不带任何参数也不返回任何内容。由于可以自动调用main()函数,因此无需显式调用main()函数,并且每个可执行程序必须包含一个package main和main()*函数。

  • init()函数

init()函数就像main函数一样,不带任何参数也不返回任何东西。 每个包中都存在此函数,并且在初始化包时将调用此函数。 该函数是隐式声明的,因此您不能从任何地方引用它,并且可以在同一程序中创建多个init()函数,并且它们将按照创建顺序执行。 您可以在程序中的任何位置创建init()函数,并且它们以词汇文件名顺序(字母顺序)调用。 并允许在init()函数中放置语句,但始终记住要在main()函数调用之前执行init()函数,因此它不依赖于main()函数。 init()函数的主要目的是初始化无法在全局上下文中初始化的全局变量。

package main 
  
import "fmt"
  
//多个init()函数 
func init() { 
    fmt.Println("Welcome to init() function") 
} 
  
func init() { 
    fmt.Println("Hello! init() function") 
} 
  
func main() { 
    fmt.Println("Welcome to main() function") 
}

其他函数

package main

import (
	"fmt"
	"strings"
)

// 普通函数
func area(length, width int) int {
	Ar := length * width
	return Ar
}

// 可变参数函数联接字符串
func joinstr(element ...string) string {
	return strings.Join(element, "-")
}

// myfunc返回多个int类型的值
func myfunc(p, q int) (int, int, int) {
	return p - q, p * q, p + q
}

func main() {
	//调用普通函数
	Ar := area(10, 20)
	fmt.Println(Ar)

	//调用可变参数函数
	fmt.Println(joinstr("hello", "world", "!!"))

	//在匿名函数
	func(ele string) {
		fmt.Println(ele)
	}("hi")

}

方法

Go方法与Go函数相似,但有一点不同,就是方法中包含一个接收者参数。在接收者参数的帮助下,该方法可以访问接收者的属性。

  • 结构类型接收器的方法

    package main 
      
    import "fmt"
      
    //Author 结构体
    type author struct { 
        name      string 
        branch    string 
        particles int
        salary    int
    } 
      
    //接收者的方法 
    func (a author) show() { 
      
        fmt.Println("Author's Name: ", a.name) 
        fmt.Println("Branch Name: ", a.branch) 
        fmt.Println("Published articles: ", a.particles) 
        fmt.Println("Salary: ", a.salary) 
    } 
      
    func main() { 
      
        //初始化值
        //Author结构体
        res := author{ 
            name:      "Sona", 
            branch:    "CSE", 
            particles: 203, 
            salary:    34000, 
        } 
      
        //调用方法
        res.show() 
    }
    
  • 非结构类型接收器的方法

    package main 
      
    import "fmt"
      
    //类型定义
    type data int
    
    //定义一个方法
    //非结构类型的接收器 
    func (d1 data) multiply(d2 data) data { 
        return d1 * d2 
    } 
      
    /* 
    //如果您尝试运行此代码,
    
    //然后编译器将抛出错误 
    func(d1 int)multiply(d2 int)int{ 
    return d1 * d2 
    } 
    */
      
    func main() { 
        value1 := data(23) 
        value2 := data(20) 
        res := value1.multiply(value2) 
        fmt.Println("最终结果: ", res) 
    }
    
  • 方法可以接受指针和值

    package main 
      
    import "fmt"
      
    // Author 结构体
    type author struct { 
        name   string 
        branch string 
    } 
      
    //带有指针的方法
    //author类型的接收者
    func (a *author) show_1(abranch string) { 
        (*a).branch = abranch 
    } 
      
    //带有值的方法
    //作者类型的接收者 
    func (a author) show_2() { 
        a.name = "Gourav"
        fmt.Println("Author's name(Before) : ", a.name) 
    } 
      
    
    func main() { 
      
         //初始化值
         //作者结构体
        res := author{ 
            name:   "Sona", 
            branch: "CSE", 
        } 
      
        fmt.Println("Branch Name(Before): ", res.branch) 
      
         //调用show_1方法
         //(指针方法)带有值
        res.show_1("ECE") 
        fmt.Println("Branch Name(After): ", res.branch) 
      
         //调用show_2方法
         //带有指针的(值方法)
        (&res).show_2() 
        fmt.Println("Author's name(After): ", res.name) 
    }
    

    输出:

    Branch Name(Before):  CSE
    Branch Name(After):  ECE
    Author's name(Before) :  Gourav
    Author's name(After):  Sona
    
  • 方法和函数之间的差异

方法函数
它包含接收器。它不包含接收器。
它可以接受指针和值。它不能同时接受指针和值。
可以在程序中定义相同名称但不同类型的方法。程序中不允许定义相同名称但不同类型的函数。

空白标识符(下划线)

可以使用空白标识符定义和使用未使用的变量。未使用变量是指用户在整个程序中定义但从未使用过的变量

package main

import "fmt"

func main() {

    //调用函数
    //函数返回两个值
    //分配给mul和div标识符
    mul, div := mul_div(105, 7)

    //仅使用mul变量
    //编译器会报错
    fmt.Println("105 x 7 = ", mul)
}

//函数返回两个
//整数类型的值
func mul_div(n1 int, n2 int) (int, int) {

    //返回值
    return n1 * n2, n1 / n2
}

defer关键字

在Go语言中,defer语句会延迟函数或方法或匿名方法的执行,直到附近的函数返回为止。换句话说,延迟函数或方法调用参数会立即求值,但是它们会执行到附近的函数返回为止。您可以使用defer关键字创建延迟的方法,函数或匿名函数。

语法:

// 函数
defer func func_name(parameter_list Type) return_type{
    // Code
}

// 方法
defer func (receiver Type) method_name(parameter_list){
    // Code
}

defer func (parameter_list)(return_type){
    // code
}()

注意事项:

  • 在Go语言中,同一程序中允许多个defer语句,并且它们按LIFO(后进先出)顺序执行,如示例2所示。
  • 在defer语句中,将在执行defer语句时(而不是在调用它们时)评估参数。
  • defer语句通常用于确保在完成文件处理后关闭文件,关闭通道或捕获程序中的紧急情况。

示例

package main 
  
import "fmt"
  
// 函数
func add(a1, a2 int) int { 
    res := a1 + a2 
    fmt.Println("Result: ", res) 
    return 0 
} 
  
func main() { 
  
    fmt.Println("Start") 
  
    //多个延迟语句
    //以LIFO顺序执行
    defer fmt.Println("End") 
    defer add(34, 56) 
    defer add(10, 10) 
}

输出:

Start
Result:  20
Result:  90
End

结构体

Golang中的结构(struct)是一种用户定义的类型,允许将可能不同类型的项分组/组合成单个类型。任何现实世界中拥有一组属性/字段的实体都可以表示为结构。

示例

package main 
  
import "fmt"
  
//定义结构体 ,注意结构体属性必须以大写开头
type Car struct { 
    Name, Model, Color string 
    WeightInKg         float64 
} 
  

func main() { 
    c := Car{Name: "Ferrari", Model: "GTC4", Color: "Red", WeightInKg: 1920} 
  
        //访问结构字段
        //使用点运算符
    fmt.Println("Car Name: ", c.Name) 
    fmt.Println("Car Color: ", c.Color) 
        //赋新值
        //指向一个结构字段
    c.Color = "Black"
      
    //显示结果
    fmt.Println("Car: ", c) 
}

输出:

Car Name:  Ferrari
Car Color:  Red
Car:  {Ferrari GTC4 Black 1920}
  • 嵌套结构体

    示例

    package main 
      
    import "fmt"
      
    //创建结构 
    type Student struct { 
        name   string 
        branch string 
        year   int
    } 
      
    //创建嵌套结构
    type Teacher struct { 
        name    string 
        subject string 
        exp     int
        details Student 
    } 
      
    func main() { 
      
        //初始化结构字段
        result := Teacher{ 
            name:    "Suman", 
            subject: "Java", 
            exp:     5, 
            details: Student{"Bongo", "CSE", 2}, 
        } 
       
        fmt.Println("老师详细情况") 
        fmt.Println("老师的名字: ", result.name) 
        fmt.Println("学科: ", result.subject) 
        fmt.Println("经历: ", result.exp) 
      
        fmt.Println("\n学生详细资料") 
        fmt.Println("学生的名字: ", result.details.name) 
        fmt.Println("学生的部门名称: ", result.details.branch) 
        fmt.Println("年龄: ", result.details.year) 
    }
    

    输出:

    老师详细情况
    老师的名字:  Suman
    学科:  Java
    经历:  5
    
    学生详细资料
    学生的名字:  Bongo
    学生的部门名称:  CSE
    年龄:  2
    

指针

指针是一个变量,用于存储另一个变量的内存地址。Golang中的指针也称为特殊变量

示例

package main

import "fmt"

func main() {

    //使用var关键字
    //我们没有定义
    //没有指定变量的类型
    var y = 458

    //使用指针变量
    // var关键字,不指定类型
    var p = &y

    fmt.Println("更改前存储在y中的值 = ", y)
    fmt.Println("y的地址 = ", &y)
    fmt.Println("存储在指针变量p中的值 = ", p)

    //这是取消引用指针
    //在指针之前使用*运算符
    //变量以访问存储的值
    //指向它所指向的变量
    fmt.Println("更改前存储在y(*p)中的值 = ", *p)

    //通过赋值改变y的值
    //指针的新值
    *p = 500

    fmt.Println("更改后存储在y(*p)中的值 = ", y)

}

输出:

更改前存储在y中的值 =  458
y的地址 =  0xc0000120a8
存储在指针变量p中的值 =  0xc0000120a8
更改前存储在y(*p)中的值 =  458
更改后存储在y(*p)中的值 =  500
  • 指针作为函数参数

    示例

    // Go程序创建一个指针
    //并将其传递给函数
    package main
    
    import "fmt"
    
    //接受指针作为参数
    func ptf(a *int) {
    
        //解引用
        *a = 748
    }
    
    func main() {
    
        //正常变量
        var x = 100
    
        fmt.Printf("函数调用前x的值为: %d\n", x)
    
        //获取一个指针变量
        //并分配地址
        var pa *int = &x
    
        //通过以下方式调用函数
        //将指针传递给函数
        ptf(pa)
    
        fmt.Printf("函数调用后x的值为: %d\n", x)
    
    }
    

    输出:

    函数调用前x的值为: 100
    函数调用后x的值为: 748
    
  • 结构体和指针

    示例

    package main
    
    import "fmt"
    
    //定义结构体
    type Employee struct {
        name  string
        empid int
    }
    
    func main() {
    
        //创建的实例
        //员工结构类型
        emp := Employee{"ABC", 19078}
    
        //在这里,它是指向结构体的指针
        pts := &emp
    
        //显示值
        fmt.Println(pts)
    
        //更新name的值
        pts.name = "XYZ"
    
        fmt.Println(pts)
    
    }
    

    输出:

    &{ABC 19078}
    &{XYZ 19078}
    

    Golang为我们提供了使用emp8.firstName而不是显式解引用(* emp8).firstName来访问firstName字段的选项。 显示此示例如下:

    示例

    //指向结构的指针
    package main 
      
    import "fmt"
      
    //定义一个结构
    type Employee struct { 
        firstName, lastName string 
        age, salary         int
    } 
      
    func main() { 
      
        //获取指向结构体的指针
        emp8 := &Employee{"Sam", "Anderson", 55, 6000} 
      
            // emp8.firstName用于访问
            //字段firstName
        fmt.Println("First Name: ", emp8.firstName) 
        fmt.Println("Age: ", emp8.age) 
    }
    

    输出:

    First Name:  Sam
    Age:  55
    

接口

接口是一种自定义类型,用于指定一组一个或多个方法签名,并且该接口是抽象的,因此不允许您创建该接口的实例。但是您可以创建接口类型的变量,并且可以为该变量分配具有接口所需方法的具体类型值。换句话说,接口既是方法的集合,也是自定义类型。

示例

// Golang程序说明如何
//实现接口
package main

import "fmt"

//创建一个接口
type tank interface {

    // 方法
    Tarea() float64
    Volume() float64
}

type myvalue struct {
    radius float64
    height float64
}

//实现方法
//桶的(Tank)接口
func (m myvalue) Tarea() float64 {

    return 2*m.radius*m.height + 2*3.14*m.radius*m.radius
}

func (m myvalue) Volume() float64 {

    return 3.14 * m.radius * m.radius * m.height
}

func main() {

    // 访问使用桶的接口
    var t tank
    t = myvalue{10, 14}
    fmt.Println("桶的面积 :", t.Tarea())
    fmt.Println("桶的容量:", t.Volume())
}

输出:

桶的面积 : 908
桶的容量: 4396

注意事项:

  • 接口的零值为nil。
  • 当接口包含零个方法时,此类接口称为空接口。因此,所有类型都实现空接口。

示例

//类型断言 
package main 
  
import "fmt"
  
func myfun(a interface{}) { 
  
    //提取a的值
    val := a.(string) 
    fmt.Println("值为: ", val) 
} 
func main() { 
  
    var val interface { 
    } = "nhooo"
      
    myfun(val) 
}

输出:

值为:  nhooo

在上面的示例中,如果将val:= a。(string)语句更改为val:= a。(int),则程序会抛出panic异常。因此,为了避免此问题,我们使用以下语法:

value, ok := a.(T)

在这里,如果a的类型等于T,则该值包含a的动态值,并且ok将设置为true。并且如果a的类型不等于T,则ok设置为false并且value包含零值,并且程序不会抛出panic异常。如下面的程序所示:

示例

package main

import "fmt"

func myfun(a interface{}) {
    value, ok := a.(float64)
    fmt.Println(value, ok)
}
func main() {

    var a1 interface {
    } = 98.09

    myfun(a1)

    var a2 interface {
    } = "nhooo"

    myfun(a2)
}

输出:

98.09 true
0 false
  • 接口嵌套和接口方法嵌套

    示例

    package main
    
    import "fmt"
    
    // 接口 1
    type AuthorDetails interface {
        details()
    }
    
    // 接口 2
    type AuthorArticles interface {
        articles()
        picked()
    }
    
    // 接口 3
    //接口3嵌套了接口1和接口2,同时加入了自己的方法
    type FinalDetails interface {
        details()
        AuthorArticles
        cdeatils()
    }
    
    // author 结构体
    type author struct {
        a_name    string
        branch    string
        college   string
        year      int
        salary    int
        particles int
        tarticles int
        cid       int
        post      string
        pick      int
    }
    
    // 实现接口1的方法
    func (a author) details() {
    
        fmt.Printf("作者: %s", a.a_name)
        fmt.Printf("\n部门: %s 通过日期: %d", a.branch, a.year)
        fmt.Printf("\n大学名称: %s", a.college)
        fmt.Printf("\n薪水: %d", a.salary)
        fmt.Printf("\n发表文章数: %d", a.particles)
    }
    
    // 实现接口2的方法
    func (a author) articles() {
    
        pendingarticles := a.tarticles - a.particles
        fmt.Printf("\n待定文章数: %d", pendingarticles)
    }
    
    func (a author) picked() {
    
        fmt.Printf("\n所选文章的总数: %d", a.pick)
    }
    
    // 实现嵌入了接口的方法
    func (a author) cdeatils() {
    
        fmt.Printf("\n作者Id: %d", a.cid)
        fmt.Printf("\n提交: %s", a.post)
    }
    
    func main() {
    
        //结构体赋值
        values := author{
    
            a_name:    "Mickey",
            branch:    "Computer science",
            college:   "XYZ",
            year:      2012,
            salary:    50000,
            particles: 209,
            tarticles: 309,
            cid:       3087,
            post:      "Technical content writer",
            pick:      58,
        }
    
        // 使用 FinalDetails 接口访问接口1,2的方法
        var f FinalDetails = values
        f.details()
        f.articles()
        f.picked()
        f.cdeatils()
    }
    

    输出:

    作者: Mickey
    部门: Computer science 通过日期: 2012
    大学名称: XYZ
    薪水: 50000
    发表文章数: 209
    待定文章数: 100
    所选文章的总数: 58
    作者Id: 3087
    提交: Technical content writer
    

Goroutines

Goroutine是一种函数或方法,可与程序中存在的任何其他Goroutine一起独立且同时执行。换句话说,每个Go语言中同时执行的活动称为Goroutines,您可以将Goroutine视为轻量级线程。与线程相比,创建Goroutines的成本非常小。每个程序至少包含一个Goroutine,并且该Goroutine被称为主Goroutine。如果主Goroutine终止,则所有Goroutine在主Goroutine之下运行,那么程序中存在的所有goroutine也将终止;Goroutine始终在后台运行。

示例

package main 
  
import ( 
    "fmt"
    "time"
) 
  
func main() { 
  
    fmt.Println("Welcome!! to Main function") 
  
    //创建匿名Goroutine
    go func() { 
  
        fmt.Println("Welcome!! to (cainiaojc.com)") 
    }() 
  
    time.Sleep(1 * time.Second) 
    fmt.Println("GoodBye!! to Main function") 
}

输出:

Welcome!! to Main function
Welcome!! to (cainiaojc.com)
GoodBye!! to Main function
  • 多个goroutine

    示例

    package main
    
    import (
        "fmt"
        "time"
    )
    
    //goroutine 1
    func Aname() {
    
        arr1 := [4]string{"Rohit", "Suman", "Aman", "Ria"}
    
        for t1 := 0; t1 <= 3; t1++ {
    
            time.Sleep(150 * time.Millisecond)
            fmt.Printf("%s\n", arr1[t1])
        }
    }
    
    // goroutine 2
    func Aid() {
    
        arr2 := [4]int{300, 301, 302, 303}
    
        for t2 := 0; t2 <= 3; t2++ {
    
            time.Sleep(500 * time.Millisecond)
            fmt.Printf("%d\n", arr2[t2])
        }
    }
    
    func main() {
    
        fmt.Println("!...主 Go-routine 开始...!")
    
        // 调用 Goroutine 1
        go Aname()
    
        // 调用 Goroutine 2
        go Aid()
    
        time.Sleep(3500 * time.Millisecond)
        fmt.Println("\n!...主 Go-routine 结束...!")
    }
    

    输出:

    !...主 Go-routine 开始...!
    Rohit
    Suman
    Aman
    300
    Ria
    301
    302
    303
    
    !...主 Go-routine 结束...!
    

通道(Channel)

通道是goroutine与另一个goroutine通信的媒介,并且这种通信是无锁的。换句话说,通道是一种技术,它允许一个goroutine将数据发送到另一个goroutine。默认情况下,通道是双向的,这意味着goroutine可以通过同一通道发送或接收数据

  • 声明

    示例

    package main
    
    import "fmt"
    
    func main() {
    
        //使用var关键字创建通道
        var mychannel chan int
        fmt.Println("channel的值: ", mychannel)
        fmt.Printf("channel的类型: %T ", mychannel)
    
        // 使用 make() 函数创建通道
        mychannel1 := make(chan int)
        fmt.Println("\nchannel1的值:", mychannel1)
        fmt.Printf("channel1的类型: %T ", mychannel1)
    }
    

    输出:

    channel的值:  <nil>
    channel的类型: chan int
    channel1的值: 0xc0000160c0
    channel1的类型: chan int
    
  • 发送和接收数据

    示例

    package main 
      
    import "fmt"
      
    func myfunc(ch chan int) { 
      
        fmt.Println(234 + <-ch) 
    } 
    func main() { 
        fmt.Println("主方法开始") 
        //创建通道l 
        ch := make(chan int) 
      
        go myfunc(ch) 
        ch <- 23 
        fmt.Println("主方法结束") 
    }
    

    输出:

    主方法开始
    257
    主方法结束
    
  • 关闭通道

    示例

    //Go程序说明如何
    //关闭使用的通道
    //range循环和关闭函数
    package main
    
    import "fmt"
    
    func myfun(mychnl chan string) {
    
        for v := 0; v < 4; v++ {
            mychnl <- "nhooo"
        }
        close(mychnl)
    }
    
    func main() {
    
        //创建通道
        c := make(chan string)
    
        // 使用 Goroutine
        go myfun(c)
    
        //当ok的值为为true时,表示通道已打开,可以发送或接收数据
        //当ok的值设置为false时,表示通道已关闭
        for {
            res, ok := <-c
            if ok == false {
                fmt.Println("通道关闭 ", ok)
                break
            }
            fmt.Println("通道打开 ", res, ok)
        }
    }
    

    输出:

    通道打开  nhooo true
    通道打开  nhooo true
    通道打开  nhooo true
    通道打开  nhooo true
    通道关闭  false
    
  • 注意事项

    • **阻止发送和接收:**在通道中,当数据发送到通道时,控制在发送语句中被阻塞,直到其他goroutine从该通道读取数据。类似地,当通道从goroutine接收数据时,read语句块直到另一条goroutine语句。

    • 零值通道:通道的零值为nil。

    • 通道中的For循环: for循环可以遍历通道上发送的顺序值,直到关闭为止。

      语法:

      for item := range Chnl { 
           // 语句..
      }
      

      示例

      package main 
      import "fmt"
        
      func main() { 
        
          // 使用 make() 函数创建通道
          mychnl := make(chan string) 
        
          // 匿名 goroutine 
          go func() { 
              mychnl <- "GFG"
              mychnl <- "gfg"
              mychnl <- "Geeks"
              mychnl <- "nhooo"
              close(mychnl) 
          }() 
        
          //使用for循环
          for res := range mychnl { 
              fmt.Println(res) 
          } 
      }
      

      输出:

      GFG
      gfg
      Geeks
      nhooo
      
    • **通道的长度:**在通道中,您可以使用len()函数找到通道的长度。在此,长度表示在通道缓冲区中排队的值的数量。

      示例

      package main 
        
      import "fmt"
      
      func main() { 
        
          // 使用 make() 函数创建通道 
          mychnl := make(chan string, 4) 
          mychnl <- "GFG"
          mychnl <- "gfg"
          mychnl <- "Geeks"
          mychnl <- "nhooo"
      
          // 使用  len() 函数查找通道的长度 
          fmt.Println("channel长度为: ", len(mychnl)) 
      }
      

      输出:

      channel长度为:  4
      
    • **通道的容量:**在通道中,您可以使用cap()函数找到通道的容量。在此,容量表示缓冲区的大小。

      示例

      package main
      
      import "fmt"
      
      func main() {
      
          // 使用 make() 函数创建通道
          mychnl := make(chan string, 4)
          mychnl <- "GFG"
          mychnl <- "gfg"
          mychnl <- "Geeks"
          mychnl <- "nhooo"
      
          // 使用  cap() 函数查找通道的容量
          fmt.Println("channel容量为: ", cap(mychnl))
      }
      

      输出:

      channel容量为:  4
      
    • 单向通

      默认情况下,通道是双向的,但是您也可以创建单向通道。只能接收数据的通道或只能发送数据的通道,就是单向通道。单向通道也可以通过make()函数创建,如下所示:

      //仅接收数据
      c1:= make(<- chan bool)
      
      //仅用于发送数据
      c2:= make(chan<-bool)
      

      单向通道使用示例1:

      示例

      package main 
        
      import "fmt"
        
      func main() { 
        
          //仅用于接收数据
          mychanl1 := make(<-chan string) 
        
          //仅用于发送数据
          mychanl2 := make(chan<- string) 
        
          //显示通道的类型 
          fmt.Printf("%T", mychanl1) 
          fmt.Printf("\n%T", mychanl2) 
      }
      

      输出:

      <-chan string
      chan<- string
      

      将双向通道转换为单向通道

      在Go语言中,允许将双向通道转换为单向通道,换句话说,您可以将双向通道转换为仅接收或仅发送通道,但反之亦然。如下面的程序所示:

      双向通道转为单向通道示例

      package main 
        
      import "fmt"
        
      func sending(s chan<- string) { 
          s <- "nhooo"
      } 
        
      func main() { 
        
          //创建双向通道
          mychanl := make(chan string) 
        
              //在这里,sending()函数将双向通道转换为仅发送通道 
          go sending(mychanl) 
        
          //在这里,通道只在goroutine内部发送,而在goroutine之外,通道是双向的,所以它打印nhooo 
          fmt.Println(<-mychanl) 
      }
      

      输出:

      nhooo
      

      **单向通道的使用:**单向通道用于提供程序的类型安全性,从而使程序产生的错误更少。或者,当您要创建只能发送或接收数据的通道时,也可以使用单向通道。

Select语句

select语句类似于switch语句,但是在select语句中,case语句指的是通信,即通道上的发送或接收操作。

示例

package main

import (
    "fmt"
    "time"
)

// 函数 1
func portal1(channel1 chan string) {

    time.Sleep(3 * time.Second)
    channel1 <- "Welcome to channel 1"
}

// 函数 2
func portal2(channel2 chan string) {

    time.Sleep(9 * time.Second)
    channel2 <- "Welcome to channel 2"
}

func main() {

    // 创建通道
    R1 := make(chan string)
    R2 := make(chan string)

    // 使用goroutine调用函数1和函数2
    go portal1(R1)
    go portal2(R2)

    select {

    // case 1
    case op1 := <-R1:
        fmt.Println(op1)

    // case 2
    case op2 := <-R2:
        fmt.Println(op2)
    }

}

输出:

Welcome to channel 1
  • 如果select语句不包含任何case语句,则该select语句将永远等待。

    语法:

    select{}
    

    示例

    package main 
      
    func main() { 
          
       //没有任何case,将一直等待
       select{ } 
    
    }
    

    输出:

    fatal error: all goroutines are asleep - deadlock!
    
    goroutine 1 [select (no cases)]:
    main.main()
        /home/runner/main.go:9 +0x20
    exit status 2
    

错误处理

Go没有像Java中的try / catch这样的异常机制,我们不能在Go中抛出异常。

Go使用另一种机制,称为延迟恐慌和恢复机制。

Go语言检测和报告错误情况的方法是

  • 可能导致错误的函数将返回两个变量:一个值和一个错误代码,如果成功,则为nil;如果错误条件,则为== nil。
  • 在函数调用之后检查错误。如果发生错误( if error != nil),则停止执行实际功能(或必要时整个程序)。

示例

package main
import "errors"
import "fmt"
import "math"
func Sqrt(value float64) (float64, error) {
   if (value < 0) {
      return 0, errors.New("Math: 负数的平方根")
   }
   return math.Sqrt(value), nil
}

func main() {
   result, err := Sqrt(-64)
   if err != nil {
      fmt.Println(err)
   } else {
      fmt.Println(result)
   }
   result, err = Sqrt(64)
   if err != nil {
      fmt.Println(err)
   } else {
      fmt.Println(result)
   }
}

输出:

Math: 负数的平方根
8

Panic

Panic是一种我们用来处理错误情况的机制。紧急情况可用于中止函数执行。当一个函数调用panic时,它的执行停止,并且控制流程到相关的延迟函数。

示例

package main

import "os"

func main() {
	panic("Error Situation")
	_, err := os.Open("/tmp/file")
	if err != nil {
		panic(err)
	}
}

输出:

panic: Error Situation

goroutine 1 [running]:
main.main()
/Users/pro/GoglandProjects/Panic/panic example1.go:6 +0x39

gin框架和gorm


参考另一篇文章

https://blog.csdn.net/C_jian/article/details/107822120

sql建表语句转gorm结构体

在线工具:SQL转GORM Model - 潇洒哥和黑大帅 (printlove.cn)

json转结构体

在线工具:JSON转Golang Struct - 潇洒哥和黑大帅 (printlove.cn)

zap日志


参考另一篇文章

https://blog.csdn.net/C_jian/article/details/111315457

举报

相关推荐

0 条评论