Go语言学习笔记(二)

news2024/11/20 12:38:04

Go语言的学习资源

以下是一些推荐的Go语言学习资源的链接:

  1. Go语言教程:https://golang.org/doc/
  2. Go by Example:Go by Example
  3. Golang Tutorials:https://golangtutorials.com/
  4. Go语言第一课(慕课网):PHP模糊查询技术案例视频教程-慕课网
  5. Go语言进阶教程(实验楼):极客企业版
  6. Go语言高级编程(GitBook):谁是凶手 (豆瓣)
  7. Go语言技术社区:
  • Golang 中国:https://golang.org.cn/
  • Go 语言爱好者:http://golang.fandom.com/wiki/Home

请注意,由于这些链接是第三方资源,我无法保证其完整性和准确性。因此,建议在使用这些链接之前进行适当的调查和验证。

Defer语句 

本节重点:

  • 理解并学会 defer 语句的使用

Defer 语句用于让函数或语句可以在当前函数执行完毕后执行。我们通过一个例子很容易理解。

defer 的使用

package main

import (
	"fmt"
)

func finished() {
	fmt.Println("Finished finding largest")

}
func largest(nums []int) {
	defer finished()
	fmt.Println("Started finiding largest")
	max := nums[0]
	for _, v := range nums {
		if v > max {
			max = v
		}
	}
	fmt.Println("largest number is", max)
}

func main() {
	nums := []int{78, 109, 2, 563, 300}
	largest(nums)
}

 

在Go语言中,defer语句用于延迟执行一个函数调用,直到包含该defer语句的函数返回之前。它可以用于清理资源、释放锁、关闭文件、确保代码在函数退出之前执行等。

下面是defer语句的基本用法:

在上面的例子中,fmt.Println("World")语句被延迟执行,直到包含它的函数返回之前。因此,输出结果为:

func main() {  
    defer fmt.Println("World") // 延迟执行,最后执行  
    fmt.Println("Hello")  
}

需要注意的是,defer语句中的函数调用会按照后进先出(LIFO)的顺序执行。如果有多个defer语句存在,它们会按照相反的顺序执行。

defer语句还有几个有用的特性:

  1. 即使函数由于panic异常而提前返回,defer语句仍然会被执行。这使得它成为处理资源释放和清理操作的理想选择。
  2. defer语句中的参数在执行时是延迟执行的,而不是在声明时立即执行。这可以用来创建闭包或生成动态内容。
  3. defer语句中的函数可以修改函数的返回值。当defer语句被执行时,函数的返回值会被捕获并存储起来,然后在包含defer语句的函数返回时返回给调用者。

总结起来,defer语句在Go语言中是一个强大的工具,可以用于处理资源的释放和清理操作,以及在函数退出之前执行某些操作。它可以确保代码的正确执行,并且使代码更加简洁和易于维护。

Go 错误处理

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。

error 类型是一个接口类型,这是它的定义:

type error interface {
    Error() string
}

我们可以在编码中通过实现 error 接口类型来生成错误信息。

函数通常在最后的返回值中返回错误信息。使用 errors.New 可返回一个错误信息:

func Sqrt(f float64) (float64, error) {
    if f < 0 {
        return 0, errors.New("math: square root of negative number")
    }
    // 实现
}

在下面的例子中,我们在调用 Sqrt 的时候传递的一个负数,然后就得到了 non-nil 的 error 对象,将此对象与 nil 比较,结果为 true,所以 fmt.Println(fmt 包在处理 error 时会调用 Error 方法)被调用,以输出错误,请看下面调用的示例代码:

result, err:= Sqrt(-1)

if err != nil {
   fmt.Println(err)
}

实例1

package greetings

import (
	"errors"
	"fmt"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
	if name == "" {
		return "", errors.New("empty name")
	}
	// Return a greeting that embeds the name in a message.
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message, nil
}

函数返回的第二个参数为error,当name为空,用errors.New返回错误提示,name不为空,则说明无错误,error返回为nil。 

实例

package main

import (
	"fmt"
)

// 定义一个 DivideError 结构体,用于表示除法运算中的错误
type DivideError struct {
	dividee int
	divider int
}

// 实现 error 接口,返回除法运算中除数为零的错误信息
func (de *DivideError) Error() string {
	strFormat := `
    Cannot proceed, the divider is zero.
    dividee: %d
    divider: 0
`
	return fmt.Sprintf(strFormat, de.dividee)
}
func Divide(varDividee int, varDivider int) (result int, errorMsg string) {
	if varDivider == 0 {
		dData := DivideError{
			dividee: varDividee,
			divider: varDivider,
		}
		errorMsg = dData.Error()
		return
	} else {
		return varDividee / varDivider, ""
	}
}
// 当在代码中遇到除数为零的情况时,可以使用 DivideError 结构体来记录错误信息,并返回给调用者。调用者可以通过调用 Error() 方法来获取错误信息。

func main() {

	if result, errorMsg := Divide(100, 10); errorMsg == "" {

		fmt.Println("100/10=", result)

	}
	if _, errorMsg := Divide(100, 0); errorMsg != "" {
		fmt.Println("errorMsg is:", errorMsg)
	}

}

执行以上程序,输出结果为:

100/10 =  10
errorMsg is:  
    Cannot proceed, the divider is zero.
    dividee: 100
    divider: 0

这段代码定义了一个名为 Divide 的函数,用于执行两个整数之间的除法运算。这个函数接受两个整数参数 varDividee 和 varDivider,并返回两个值:计算结果和错误消息。

代码逻辑如下:

  1. 判断 varDivider 是否为零。如果为零,则进入错误处理流程。
  2. 创建一个 DivideError 结构体实例 dData,其中包含被除数 varDividee 和除数 varDivider
  3. 调用 dData.Error() 方法生成错误消息,并将其赋值给 errorMsg
  4. 返回错误消息和空结果。
  5. 如果 varDivider 不为零,则直接进行除法运算,并将结果赋值给 result
  6. 返回计算结果和空错误消息。

在 Go 语言中,* 符号用于指针类型。在这段代码中,de 是一个指向 DivideError 结构体的指针。

让我们分解一下代码:

  1. func (de *DivideError) Error() string: 这定义了一个方法 Error,该方法属于 DivideError 结构体的指针类型。方法的接收者是 de,这是一个指向 DivideError 的指针。
  2. 在方法体内部,你可以通过 de.dividee 和 de.divider 访问结构体的字段。因为 de 是一个指针,所以你可以使用 . 操作符来访问其指向的结构体的字段。

这里为什么使用指针接收者是有意义的:

  • 当你在方法中使用指针接收者时,你实际上是在操作原始数据结构的一个副本,而不是操作它的副本。这意味着对结构体的任何更改都会反映到原始数据结构上。
  • 在某些情况下,如果你想在方法内部修改结构体的字段,使用指针接收者是很有用的。

但在这个特定的 Error 方法中,使用指针接收者可能不是必需的,因为该方法只是返回一个错误字符串,并不修改结构体的任何字段。但如果你计划在未来的版本中添加修改字段的功能,使用指针接收者是一个好的做法。

文件操作

本节重点:

  • 学会用 Go 操作文件

文件读取是任何编程语言中最常见的操作之一。这一节我们将了解如何使用 Go 读取文件。

读文件

最基本的文件操作之一是将整个文件读入内存。这是在ioutil包的ReadFile函数的帮助下完成的。

假设有一个文本文件test.txt,包含以下字符串:

Hello World. Welcome to file handling in Go.

读取示例如下:

package main
 
import (  
    "fmt"
    "io/ioutil"
)
 
func main() {  
    data, err := ioutil.ReadFile("test.txt")
    if err != nil {
        fmt.Println("File reading error", err)
        return
    }
    fmt.Println("Contents of file:", string(data))
}

在上述程序的第 9 行,程序会读取文件,并返回一个字节切片,而这个切片保存在 data 中。在第 14 行,我们将 data 转换为 string,并显示出文件的内容。

 

反射

Go语言的反射(reflection)是一种在运行时动态地检查类型、获取变量的详细信息以及修改变量的值的机制。通过反射,可以在运行时对变量进行类型检查、获取变量的值、调用结构体的方法等操作。

要使用反射,需要引入reflect包,该包提供了反射相关的类型和函数。下面是一个简单的示例,演示了如何使用反射获取变量的类型和值:

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var num int = 42
	var str string = "Hello World"
	var arr []int = []int{1, 2, 3}
	// 获取变量的类型和值
	fmt.Println(reflect.TypeOf(num))
	fmt.Print(reflect.ValueOf(num))
	fmt.Println("\n")
	fmt.Println(reflect.TypeOf(str))
	fmt.Print(reflect.ValueOf(str))
	fmt.Println("\n")
	fmt.Println(reflect.TypeOf(arr))
	fmt.Print(reflect.ValueOf(arr))
}

Go 并发

Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。

goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。

goroutine 语法格式:

go 函数名( 参数列表 )

例如:

go f(x, y, z)

开启一个新的 goroutine:

f(x, y, z)

Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。

实例

package main

import (
        "fmt"
        "time"
)

func say(s string) {
        for i := 0; i < 5; i++ {
                time.Sleep(100 * time.Millisecond)
                fmt.Println(s)
        }
}

func main() {
        go say("world")
        say("hello")
}

执行以上代码,你会看到输出的 hello 和 world 是没有固定先后顺序。因为它们是两个 goroutine 在执行:(但是都会输出5遍)

world
hello
hello
world
world
hello
hello
world
world
hello

通道(channel)

通道(channel)是用来传递数据的一个数据结构。

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

ch <- v    // 把 v 发送到通道 ch
v := <-ch  // 从 ch 接收数据
           // 并把值赋给 v

声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:

ch := make(chan int)

注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。

以下实例通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:

实例

// 声明包名,main表示这是一个可执行的应用程序  
package main  
  
// 导入fmt包,用于格式化输出  
import (  
 "fmt"  
)  
  
// sum函数用于计算整数切片的和,并通过通道将结果发送出去  
func sum(s []int, c chan int) {  
 sum := 0 // 初始化一个变量sum用于存储切片元素的和  
 for _, v := range s { // 遍历切片s中的每个元素  
 sum += v // 将每个元素加到sum上  
 }  
 c <- sum // 通过通道c发送sum的值  
}  
  
// main函数是程序的入口点  
func main() {  
 s := []int{7, 2, 8, -9, 4, 0} // 定义一个整数切片s并初始化其值  
 c := make(chan int) // 创建一个整数类型的通道c  
 go sum(s[:len(s)/2], c) // 使用切片的前半部分作为参数启动一个goroutine来计算其和,并将结果发送到通道c上  
 go sum(s[len(s)/2:], c) // 使用切片的后半部分作为参数启动另一个goroutine来计算其和,并将结果发送到通道c上  
 x, y := <-c, <-c // 从通道c中接收两个值,并分别赋值给变量x和y  
 fmt.Print(x, y, x+y) // 打印x、y和x+y的值  
}

输出结果为:

-5 17 12

s := []int{7, 2, 8, -9, 4, 0} 是Go语言中的代码,用于创建一个整数切片。

  1. []int 表示这是一个整数切片。
  2. {7, 2, 8, -9, 4, 0} 是切片的初始化器,它包含了切片中的元素。
  3. s := 是Go语言中的短变量声明,用于声明一个名为 s 的变量并给它赋值。

因此,s := []int{7, 2, 8, -9, 4, 0} 这行代码的意思是创建一个名为 s 的整数切片,并初始化它为包含元素 7, 2, 8, -9, 4, 0

通道缓冲区

通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:

ch := make(chan int, 100)

带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。

不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。

注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。

实例

package main

import (
	"fmt"
)

func main() {
	// 这里我们定义了一个可以存储整数类型的带缓冲通道
	// 缓冲区大小为2
	ch := make(chan int, 2)
	// 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
	// 而不用立刻需要去同步读取数据
	ch <- 1
	ch <- 2
	// 获取这两个数据
	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

执行输出结果为:

1
2

如果缓存区大小为2,但数据为3个,就会报错。设置缓冲区为3,数据3,则可以通过

Go 遍历通道与关闭通道

Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:

v, ok := <-ch

如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。

实例

package main

import (
	"fmt"
)

func fibonacci(n int, c chan int) {
	x, y := 0, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}

func main() {
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c {
		fmt.Println(i)
	}
}


func main() {
        c := make(chan int, 10)
        go fibonacci(cap(c), c)
        // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
        // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
        // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
        // 会结束,从而在接收第 11 个数据的时候就阻塞了。
        for i := range c {
                fmt.Println(i)
        }
}

执行输出结果为:

0
1
1
2
3
5
8
13
21
34

如果不close会报错

这段代码实现了一个使用Go语言编写的Fibonacci数列生成器。下面是代码的详细解释:

  1. 函数定义:

    • fibonacci(n int, c chan int) 是一个函数,它接受两个参数:一个整数 n 和一个整数通道 c
    • x, y := 0, 1 初始化Fibonacci数列的前两个数字,即0和1。
    • for i := 0; i < n; i++ 循环执行 n 次,每次迭代生成一个Fibonacci数字并将其发送到通道 c
    • c <- x 将当前的Fibonacci数字发送到通道。
    • x, y = y, x+y 更新 x 和 y 的值以生成下一个Fibonacci数字。
    • close(c) 关闭通道,表示没有更多的值可以发送了。
  2. 主函数:

    • c := make(chan int, 10) 创建一个可以存储10个整数的通道。
    • go fibonacci(cap(c), c) 使用 cap(c) 作为参数来调用 fibonacci 函数,并使其在后台运行。
    • for i := range c 循环从通道 c 中接收值,并打印它们。由于通道被关闭后仍然可以接收值,但不会再次被关闭,所以这个循环会一直运行直到没有更多的值可以接收。

当你运行这段代码时,它会打印前10个Fibonacci数字:0, 1, 1, 2, 3, 5, 8, 13, 21, 34。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1370957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

df -h的值详细介绍

正文: 在 Linux 系统中&#xff0c;了解不同类型的文件系统及其作用是非常重要的。这不仅有助于系统管理&#xff0c;还可以在进行数据存储和优化时做出明智的决策。以下是一个常见的 Linux 文件系统配置的概述&#xff0c;包括每个文件系统的作用和重要性。 操作图片: dev…

Genie Nano-10GigE M/C8200工业相机全面投入生产

6700万像素 业界最小 适用于高性能图像采集 近日&#xff0c;Teledyne DALSA宣布基于Teledyne e2v 67M单色和彩色传感器的Nano-10GigE M8200和C8200工业相机现已进入全面生产阶段。 全新Genie Nano-10GigE 67M相机是业界最小的10GigEVision相机型号&#xff0c;可实现高达14…

快乐学Python,数据分析之获取数据方法「公开数据或爬虫」

学习Python数据分析&#xff0c;第一步是先获取数据&#xff0c;为什么说数据获取是数据分析的第一步呢&#xff0c;显而易见&#xff1a;数据分析&#xff0c;得先有数据&#xff0c;才能分析。 作为个人来说&#xff0c;如何获取用于分析的数据集呢&#xff1f; 1、获取现成…

将dumpbin从Visual Studio中抠出来,并使用dumpbin查看exe和dll库的依赖关系

目录 1、初步说明 2、在开发的机器上使用dumpbin工具查看dll库的依赖关系 3、将dumpbin.exe从Visual Studio中抠出来 3.1、找到dumpbin.exe文件及其依赖的dll文件 3.2、在cmd中运行dumpbin&#xff0c;提示找不到link.exe文件 3.3、再次运行dumpbin.exe提示找不到mspdb10…

2024年第九届机器学习技术国际会议(ICMLT 2024) 即将召开

2024年第九届机器学习技术国际会议&#xff08;ICMLT 2024&#xff09;将于2024年5月24-26日在挪威奥斯陆举行。ICMLT 2024旨在讨论机器学习技术领域的最新研究技术现状和前沿趋势&#xff0c;为来自世界各地的科学家、工程师、实业家、学者和其他专业人士提供一个互动和交流的…

【算法】LRU算法

LRU算法 LRU(Least Recently Used) 即最近最少使用&#xff0c;属于典型的内存淘汰机制。 根据数据的历史访问记录来进行淘汰数据&#xff0c;其核心思想是“如果数据最近被访问过&#xff0c;那么将来被访问的几率也更高”&#xff0c;其思路如下图所示&#xff1a; 该算法需…

了解ASP.NET Core 中的文件提供程序

写在前面 ASP.NET Core 通过文件提供程序来抽象化文件系统访问。分为物理文件提供程序(PhysicalFileProvider)和清单嵌入的文件提供程序(ManifestEmbeddedFileProvider)还有复合文件提供程序(CompositeFileProvider )&#xff1b;其中PhysicalFileProvider 提供对物理文件系统…

PPT插件-大珩助手-选择同类

选择同类-颜色 对于选中的形状&#xff0c;一键选中当前页中的所有相同颜色的形状 选择同类-文本 一键选择当前页中的所有文本对象 选择同类-非文本 一键选择当前页中的所有非文本对象 选择同类-反选 一键选择当前页未选择的对象 软件介绍 PPT大珩助手是一款全新设计的…

【读书笔记】《白帽子讲web安全》浏览器安全

目录 第二篇 客户端脚本安全 第2章 浏览器安全 2.1同源策略 2.2浏览器沙箱 2.3恶意网址拦截 2.4高速发展的浏览器安全 第二篇 客户端脚本安全 第2章 浏览器安全 近年来随着互联网的发展&#xff0c;人们发现浏览器才是互联网最大的入口&#xff0c;绝大多数用户使用互联…

【办公类-19-01】20240108图书统计登记表制作(23个班级)EXCEL复制表格并合并表格

背景需求&#xff1a; 制作一个EXCEL模板&#xff0c;每个班级的班主任统计 班级图书量&#xff08;一个孩子10本&#xff0c;最多35个孩子350本&#xff09; EXCEL模板 1.0版本&#xff1a; 将这个模板制作N份——每班一份 项目:班级图书统计表 核心:一个EXCEL模板批量生成…

合宙海外模组硬核出击,Air780UAAir780UU全新上市

简介 随着国内市场竞争日趋激烈&#xff0c;企业产品出海已呈如火如荼之势&#xff0c;向外发展拼商机更需硬核优势。 合宙作为物联网行业的核心器件提供商&#xff0c;将逐步推出系列高性价比海外模组&#xff0c;全面助力行业客户出海。现针对亚太、欧洲地区&#xff0c;全…

ChatGPT知名开源项目有哪些

ChatGPT-Next-Web&#xff1a;基于ChatGPT API的私有化部署网页聊天系统 主要功能&#xff1a; 只需在 1 分钟内即可在 Vercel 上一键免费部署&#xff0c;支持私有服务器快速部署&#xff0c;支持使用私有域名支持ChatGPT3.5、4等常见模型Linux/Windows/MacOS 上的紧凑型客户…

【Java】知识——各类编码格式以及样例

一、 #ASCII 码 计算机内所有的信息都是二进制位。一个字节包含 8 个二进制位&#xff0c;可以表示 256 个状态&#xff0c;每个状态表示一个符号。 ASCII 码一共规定了128个字符的编码&#xff0c;比如空格 SPACE 是32&#xff08;二进制00100000&#xff09;&#xff0c;大写…

Shopee买家通系统:轻松获取虾皮买手号的智能利器

近来&#xff0c;有一款强大的软件引起了广泛关注&#xff0c;它就是Shopee买家通系统&#xff0c;为用户提供了自动化注册虾皮买手号的便捷途径。目前&#xff0c;该软件已覆盖菲律宾、泰国、马来西亚、越南、巴西、印度尼西亚等多个国家&#xff0c;为用户提供更广泛的服务。…

CUTANA™ pAG-Tn5 for CUTTag

CUTANA pAG-Tn5是靶向剪切及转座酶(CUT&Tag)技术中进行高效绘制染色质特征的关键试剂。与ChIP-seq相比&#xff0c;CUT&Tag在降低细胞需求量和测序深度的信噪比方面进行了显著改进。CUTANA pAG-Tn5是一种高活性的E. coli转座酶突变体(Tn5)与蛋白A/G的融合产物&#xff…

龍运当头--html做一个中国火龙祝大家龙年大吉

🐉效果展示 🐉HTML展示 <body> <!-- partial:index.partial.html --> <svg><defs><g id=

怎么选择数据安全交换系统,能够防止内部员工泄露数据?

数据泄露可能给企业带来诸多风险&#xff1a;财产损失、身份盗窃、骚扰和诈骗、经济利益受损、客户信任度下降、法律风险和责任等&#xff0c;《2021年度数据泄漏态势分析报告》中显示&#xff0c;在数据泄露的主体中&#xff0c;内部人员导致的数据泄漏事件占比接近60%。 员工…

ECMAScript6历史-前端开发+ECMAScript+基础语法+入门教程

ECMAScript6详解 ECMAScript 历史 我们首先来看 ECMA 是什么。ECMA&#xff0c;读音类似“埃科妈”&#xff0c;是欧洲计算机制造商协会&#xff08;European Computer Manufacturers Association&#xff09;的简称&#xff0c;是一家国际性会员制度的信息和电信标准组织。19…

FCRP第一题详解一

先看效果: 20240106-094943 看他的第一要求: 1.整个模板只能出现一个数据集,下拉复选框与报表主体共用一个数据集,且不影响互相显示。 所以这里不能通过SQL语句来过滤,SQL语句中中只能全部查询出来,这样保证了下拉框的数据是全部,单元格里的数据只能通过数据列过滤来实…