Go 语言从入门到实战

news2024/9/28 15:25:00

《Go 语言从入门到实战》 的学习笔记,欢迎阅读斧正。感觉该专栏整体来说对有些后端编程经验的来说比无后端编程经验的人更友好。

数据类型

运算符

算数运算符

在这里插入图片描述

比较运算符

在这里插入图片描述

用 == 比较数组

相同维数切含有相同个数元素的数组才可以比较,每个元素都相同的才相等。

import "testing"

// 相同维数切含有相同个数元素的数组才可以比较,每个元素都相同的才相等。
func TestCompareArray(t *testing.T) {
	a := [...]int{1, 2, 3, 4}
	b := [...]int{1, 3, 4, 5}
	//c := [...]int{1, 2, 3, 4, 5}
	d := [...]int{1, 2, 3, 4}
	e := [...]int{1, 4, 3, 5}

	// 输出 false
	t.Log(a == b)
	// 长度不同的两个数组,比较会得到一个编译错误
	//t.Log(a == c)
	// 输出 true
	t.Log(a == d)
	// 输出 false
	t.Log(b == e)
}

逻辑运算符

在这里插入图片描述

位运算符

按位清零运算符

在这里插入图片描述

import "testing"

const (
	Readable = 1 << iota
	Writable
	Executable
)

// 按位清零运算符
func TestBitClear(t *testing.T) {
	a := 7 // 0111
	// true true true
	t.Log(a&Readable == Readable, a&Writable == Writable, a&Executable == Executable)
	a = a &^ Readable
	// 6
	t.Log(a &^ Readable)
	// false true true
	t.Log(a&Readable == Readable, a&Writable == Writable, a&Executable == Executable)
}

循环

Go 语言只支持循环关键字 for

While条件循环

无限循环

条件

if 条件

在这里插入图片描述
与其他语言差异:

  1. condition 表达式结果必须是布尔值
  2. 支持变量赋值
if var declaration; condition{
//do something

switch 条件

在这里插入图片描述

与其他语言差异:

  1. 条件表达式不限制为常量或者整数;
  2. 单个 case 中,可以出现多个结果选项,使用逗号分隔;
  3. 与C语言等规则相反,Go语言不需要用 break 来明确退出一个 case;
  4. 可以不设定 switch 之后的条件表达式,在此种情况下,整个 switch 结构与多个 if…else… 的逻辑作用等同

数组和切片

数组

声明

在这里插入图片描述

遍历

在这里插入图片描述

截取

在这里插入图片描述

切片

声明

在这里插入图片描述

内部结构

在这里插入图片描述

注意:切片不可以比较

Map

声明

在这里插入图片描述

访问的 key 不存在时,仍会返回 0 值,不能通过返回 nil 来判断元素是否存在(int 类型,默认是 0,如果换成 string 类型,默认就是空字符串了)

Map 与工厂

在这里插入图片描述

import (
	"testing"
)

func TestMapWithFunValue(t *testing.T) {
	m := map[int]func(op int) int{}
	m[1] = func(op int) int { return op }

	m[2] = func(op int) int { return op * op }
	m[3] = func(op int) int { return op * op * op }
	// 输出 2 4 8
	t.Log(m[1](2), m[2](2), m[3](2))

}

实现 set

在这里插入图片描述

字符串

与其他语言差别:

  1. string 是数据类型,不是引用或指针类型
  2. string 是只读的 byte slice,len() 函数可以它所包含的 byte 数
  3. string 的 byte 数组可以存放任何数据

函数

  1. 可以有多个返回值
  2. 所有参数都是值传递
  3. 函数可以作为变量的vi
  4. 函数可以作为参数和返回值

这个需要好好看看,好像还有闭包之类的知识

// 延时一秒后返回入参
func SlowFun(op int) int {
	time.Sleep(time.Second * 1)
	return op
}

// 定义一个函数timeSpent帮助记录入参函数的执行耗时
//
//	入参是一个函数,               返回也是一个函数
func timeSpent(入参函数名 func(op int) int) func(op int) int {
	return func(返回函数的参数 int) int {
		start := time.Now()
		ret := 入参函数名(返回函数的参数) // 执行有入参的该函数
		fmt.Println("该入参函数执行结果:", ret)
		fmt.Println("时间损耗:", time.Since(start).Seconds())
		return ret
	}
}

func TestFn1(t *testing.T) {
	// 给timeSpent入参一个函数, 我们返回一个函数, 这个函数执行这个入参函数并打印该的执行时间
	// tsSF 得到的就是返回的函数,我们给这个函数提供一个 「返回函数的参数」-> 10
	tsSF := timeSpent(SlowFun)
	// 打印执行
	t.Log(tsSF(10))
}

可变参数

在这里插入图片描述

defer 函数

延迟执行函数,可以当做 Java 中的 try…catch…finally 使用
在这里插入图片描述

面向对象

在这里插入图片描述

封装数据和行为

结构体定义

在这里插入图片描述

初始化

在这里插入图片描述

行为/方法定义

在这里插入图片描述

接口

定义

type Programmer interface {
	WriteHelloWorld() Code
}

实现

type GoProgrammer struct {
}
func (p *GoProgrammer) WriteHelloWorld() Code {
	return "fmt.Println(\"Hello World!\")"
}

接口变量

在这里插入图片描述

自定义类型

type IntConv func(op int) int 注意 func

复合

Go 不支持继承,但是可以通过复合的方式来复用

匿名类型嵌入

import (
	"fmt"
	"testing"
)

type Pet struct {
}

func (p *Pet) Speak() {
	fmt.Print("...")
}

func (p *Pet) SpeakTo(host string) {
	p.Speak()
	fmt.Println("\n", host)
}

type Dog struct {
	//p *Pet
	Pet // 内嵌的结构类型没法当继承来使用
}

不是继承,如果把内部的 struct 看做父类,外部的 struct 看成子类,会发现以下问题:

  1. 不支持子类替换
  2. 子类并不是继承了父类的方法
  3. 父类的定义的方法无法访问子类的数据和方法

多态

代码:

import (
	"fmt"
	"testing"
)

type Code string
type Programmer interface {
	WriteHelloWorld() Code
}

type GoProgrammer struct {
}

func (p *GoProgrammer) WriteHelloWorld() Code {
	return "fmt.Println(\"Hello World!\")"
}

type JavaProgrammer struct {
}

func (p *JavaProgrammer) WriteHelloWorld() Code {
	return "System.out.Println(\"Hello World!\")"
}

// interface 作为入参,必须是指针实例
func writeFirstProgram(p Programmer) {
	fmt.Printf("%T %v\n", p, p.WriteHelloWorld())
}

func TestPolymorphism(t *testing.T) {
	goProg := &GoProgrammer{}// 注意 &,取地址或者理解为指针
	javaProg := new(JavaProgrammer)
	writeFirstProgram(goProg)
	writeFirstProgram(javaProg)
}

空接口

  1. 空接口可以表示任何类型(可以理解为 Java 中的 Object)
  2. 通过断言来将空接口转换为指定类型v, ok := p.(int) // ok = true 时转换成功
import (
	"fmt"
	"testing"
)

// 空接口可以代表任何类型
func DoSomething(p interface{}) {
	// if 版本:
	//if i, ok := p.(int); ok {
	//	fmt.Println("Integer", i)
	//	return
	//}
	//if i, ok := p.(string); ok {
	//	fmt.Println("string", i)
	//	return
	//}
	//fmt.Println("Unknown Type")

	// switch 版本:
	switch v := p.(type) {
	case int:
		fmt.Println("Integer", v)
	case string:
		fmt.Println("string", v)
	default:
		fmt.Println("Unknown Type")
	}
}

func TestEmptyInterfaceAssertion(t *testing.T) {
	DoSomething(1)
	DoSomething("test")
	DoSomething(true)
}

接口使用原则

在这里插入图片描述
第一行可以理解为 单一职责,第三行可以理解为 接口隔离原则

错误机制

  1. 没有异常机制
  2. error 类型实现了 error 接口
  3. 可以通过 errors.New 来快速创建错误实例errors.New("it's a arrer

实践

定义不同的错误变量,以便于判断错误类型

在这里插入图片描述

及早失败,避免嵌套

感觉类似于 卫语句

func GetFibonacci1(str string) {
	var (
		i    int
		err  error
		list []int
	)
	if i, err = strconv.Atoi(str); err == nil {
		if list, err = GetFibonacci(i); err == nil {
			fmt.Println(list)
		} else {
			fmt.Println("Error", err)
		}
	} else {
		fmt.Println("Error", err)
	}
}

// GetFibonacci2 相对于 GetFibonacci1 来说,避免了错误嵌套,感觉类似 Java 中卫语句?
func GetFibonacci2(str string) {
	var (
		i    int
		err  error
		list []int
	)
	if i, err = strconv.Atoi(str); err != nil {
		fmt.Println("Error", err)
		return
	}
	if list, err = GetFibonacci(i); err != nil {

		fmt.Println("Error", err)
		return
	}
	fmt.Println(list)

}

panic

  1. panic 用于不可以恢复的错误
  2. panic 退出前会执行 defer 指定的内容

panic vs os.Exit

后者不会调用 defer 执行的函数,后者退出时不输出当前调用栈信息

recover

错误恢复代码,类似于 Java 中的 try...catch... 中的 catch

import (
	"errors"
	"fmt"
	"testing"
)

func TestPanicVxExit(t *testing.T) {

	defer func() {
		if err := recover(); err != nil {
			fmt.Println("recovered from ", err)
		}
	}()
	fmt.Println("Start")
	panic(errors.New("Something wrong!"))
}

// 输出:
// Start
// recovered from  Something wrong!
// --- PASS: TestPanicVxExit (0.00s)

使用的时候不能随便 recover,不要随便捕获异常,该抛错误的就抛错误,要不不知道什么错。

package

  1. 基本的复用模块单元(另外,首字母大写表明可被包外代码访问
  2. 代码的 package 可以和所在目录不一致
  3. 同一目录里的 Go 代码的 package 要保持一致

init 方法

  1. 在 main 被执行前,所有依赖的 package 的 init 方法都会被执行
  2. 不同包的 init 函数按照包导入的依赖关系决定执行顺序
  3. 每个包可以有多个 init 函数
  4. 包的每个源文件也可以有多个 init 函数,这点比较特殊

测试

单元测试

内置单元测试框架

  • Fail,Error:该测试失败,该测试继续,其他测试继续执行
  • FailNow,Fatal:该测试失败,该测试中止,其他测试继续执行
  • 代码覆盖率:go test-v-cover
  • 断言:https://github.com/stretchr/testify
import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestSquare(t *testing.T) {
	inputs := [...]int{1, 2, 3}
	expected := [...]int{1, 4, 9}
	for i := 0; i < len(inputs); i++ {
		ret := square(inputs[i])
		if ret != expected[i] {
			t.Errorf("input is %d, the expected is %d, the actual %d",
				inputs[i], expected[i], ret)
		}
	}
}

func TestErrorInCode(t *testing.T) {
	fmt.Println("Start")
	t.Error("Error")
	fmt.Println("End")
}

func TestFailInCode(t *testing.T) {
	fmt.Println("Start")
	t.Fatal("Error")
	fmt.Println("End")
}

func TestSquareWithAssert(t *testing.T) {
	inputs := [...]int{1, 2, 3}
	expected := [...]int{1, 4, 9}
	for i := 0; i < len(inputs); i++ {
		ret := square(inputs[i])
		assert.Equal(t, expected[i], ret)
	}
}

Benchmark

在这里插入图片描述
相关命令(非 Windows 把双引号去掉):

  1. go test -bench=“.”
  2. go test -bench=“.” -benchmem

BDD

Behavior Driven Development,让业务领域的专家参与开发。
在这里插入图片描述

感觉理解起来就是写清需求……

BDD In Go

项目网站:https://github.com/smartystreets/goconvey

安装:go get -u github.com/smartystreets/goconvey/convey

启动 UI:$GOPATH/bin/goconvey

不安全编程

理解的 Go 的不安全编程是指用到了 unsafe 包中的指针:使用"不安全编程"帮你绕过 Go 语言中的类型检测系统

合理的类型转换以及原子操作不算不安全编程

import (
	"fmt"
	"sync"
	"sync/atomic"
	"testing"
	"time"
	"unsafe"
)

type Customer struct {
	Name string
	Age  int
}

// 理解的 Go 的不安全编程是指用到了 unsafe 包中的内容:https://www.cnblogs.com/traditional/p/11890639.html
func TestUnsafe(t *testing.T) {
	i := 10
	f := *(*float64)(unsafe.Pointer(&i))
	t.Log(unsafe.Pointer(&i))
	t.Log(f)
}

// The cases is suitable for unsafe
type MyInt int

// 合理的类型转换
func TestConvert(t *testing.T) {
	a := []int{1, 2, 3, 4}
	b := *(*[]MyInt)(unsafe.Pointer(&a))
	t.Log(b)
}

// 原子类型操作
func TestAtomic(t *testing.T) {
	var shareBufPtr unsafe.Pointer
	writeDataFn := func() {
		data := []int{}
		for i := 0; i < 100; i++ {
			data = append(data, i)
		}
		atomic.StorePointer(&shareBufPtr, unsafe.Pointer(&data))
	}
	readDataFn := func() {
		data := atomic.LoadPointer(&shareBufPtr)
		fmt.Println(data, *(*[]int)(data))
	}
	var wg sync.WaitGroup
	writeDataFn()
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			for i := 0; i < 10; i++ {
				writeDataFn()
				time.Sleep(time.Microsecond * 100)
			}
			wg.Done()
		}()
		wg.Add(1)
		go func() {
			for i := 0; i < 10; i++ {
				readDataFn()
				time.Sleep(time.Microsecond * 100)
			}
			wg.Done()
		}()
	}
	wg.Wait()
}

常见任务

  • 这些没太花重心学,后期直接看 web 相关框架

内置 JSON 解析

利用 反射 实现,通过 FeildTag 来标识对应的 json 值

var jsonStr = `{
	"basic_info":{
	  	"name":"Mike",
		"age":30
	},
	"job_info":{
		"skills":["Java","Go","C"]
	}
}	`

func TestEmbeddedJson(t *testing.T) {
	e := new(Employee)
	err := json.Unmarshal([]byte(jsonStr), e)
	if err != nil {
		t.Error(err)
	}
	fmt.Println(*e)
	if v, err := json.Marshal(e); err == nil {
		fmt.Println(string(v))
	} else {
		t.Error(err)
	}

}

easyjson

是用代码生成的

相关代码:easyjson

HTTP 服务

import (
	"fmt"
	"net/http"
	"time"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello World!")
	})
	http.HandleFunc("/time/", func(w http.ResponseWriter, r *http.Request) {
		t := time.Now()
		timeStr := fmt.Sprintf("{\"time\": \"%s\"}", t)
		w.Write([]byte(timeStr))
	})

	http.ListenAndServe(":8080", nil)
}

路由规则

  • URL分为两种,末尾是 /:表示一个子树,后面可以跟其他子路径;末尾不是 /,表示一个叶子,固定的路径
  • 以 / 结尾的URL可以匹配它的任何子路径,比如 /images会匹配 /images/cute-cat.jpg
  • 它采用最长匹配原则,如果有多个匹配,一定采用匹配路径最长的那个进行处理
  • 如果没有找到任何匹配项,会返回404错误

default router

在这里插入图片描述

更好的 router

httprouter: A high performance HTTP request router that scales well

import (
	"fmt"
	"log"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	fmt.Fprint(w, "Welcome!\n")
}

func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
}

func main() {
	router := httprouter.New()
	router.GET("/", Index)
	router.GET("/hello/:name", Hello)

	log.Fatal(http.ListenAndServe(":8080", router))
}

构建 RESTful

这个看这里的代码吧:geektime/go_learning/code/ch45/roa/resource_oriented_arc.go

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

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

相关文章

四旋翼无人机学习第18节--cadence的bom表、网表导出,PCB板创建,层叠设置

文章目录1 bom表导出2 网表导出3 PCB板创建4 PCB板文件重要设置5 层叠设置1 bom表导出 1、选择DSN文件&#xff0c;之后依次点击Tools&#xff0c;Bill of Materials。 2、当然&#xff0c;也可以点击图标进行bom导出。 3、下图标出的地方可以自行修改&#xff0c;最后点击O…

设计师必须知道的 5个设计灵感网站

设计没灵感&#xff0c;一定要上这5个网站&#xff0c;设计师每天必逛&#xff0c;建议收藏&#xff01; 设计素材免费下载&#xff1a; https://www.sucai999.com/?vNTYwNDUx 国内灵感网 1、设计之窗 http://www.333cn.com/ 设计之窗是一个设计师分享作品及备案的网站&…

【Ctfer训练计划】——命令执行的解题技巧(持续更新中)

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右…

什么是软考?软考有什么作用?

一、软考是什么&#xff1f; 1.软考介绍 计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试是由国家人力资源和社会保障部、工业和信息化部领导的国家级考试&#xff0c;其目的是&#xff0c;科学、公正地对全国计算机与软件专业技术人员进行职业资格、专业技术…

保姆教程系列三、Redis高可用(Cluster集群模式)

系列文章目录 &#xff01;&#xff01;&#xff01;是的没错&#xff0c;胖友们&#xff0c;保姆教程系列又更新了&#xff01;&#xff01;&#xff01; 保姆教程系列一、Redis部署 so easy 保姆教程系列二、Redis高可用&#xff08;主从同步哨兵模式&#xff09; 保姆教程系…

java开发的医院体检预约系统

简介 体检项目预约网站&#xff0c;普通用户注册登录可以网上预约体检项目&#xff0c;经过后台人员审核后可以去体检。用户还可以记录自己的身体指标下载体检报个&#xff0c;查看医嘱等。医院后台可以进行权限管理&#xff0c;实现多角色管理后台的其他业务等&#xff0c;实…

前端—化繁为简

化繁为简 HTML5要的就是简单、避免不必要的复杂性。HTML5的口号是“简单至上&#xff0c;尽可能简化”。因此&#xff0c;HTML5做了以下改进&#xff1a; 以浏览器原生能力替代复杂的JavaScript代码。 新的简化的DOCTYPE。 新的简化的字符集声明。 简单而强大的HTML5API。 我…

【 shell 编程 】第2篇 判断

判断 文章目录判断一、条件测试二、流程控制1.单分支结构2.双分支结构3.多分支结构4.嵌套结构三、匹配模式一、条件测试 格式&#xff1a; 格式1&#xff1a;test 条件表达式 格式2&#xff1a;[ 条件表达式 ] 格式3&#xff1a;[[ 条件表达式 ]] 注意&#xff1a;[] 中的条件…

电气器件系列三十五:开关电源选型实例

开关模式电源&#xff08;Switch Mode Power Supply&#xff0c;简称SMPS&#xff09;&#xff0c;又称交换式电源、开关变换器&#xff0c;是一种高频化电能转换装置&#xff0c;是电源供应器的一种。其功能是将一个位准的电压&#xff0c;透过不同形式的架构转换为用户端所需…

c++11 标准模板(STL)(std::deque)(三)

定义于头文件 <deque> std::deque 赋值给容器 std::deque<T,Allocator>::operator deque& operator( const deque& other ); (1) deque& operator( deque&& other ); (2)(C11 起) (C17 前) deque& operator( deque&& other ) no…

Docker的基本使用

文章目录1.初识Docker1.1 为什么会有docker出现&#xff1f;1.2 docker的理念1.3 Docker和虚拟机的区别1.4 Docker的基本组成(docker的三要素)1.4.1 镜像1.4.2 容器1.4.3 仓库1.5 Docker的架构2.Docker的安装3.常用命令3.1 帮助启动类命令3.2 镜像命令3.3 容器命令1.初识Docker…

使用Lua Script实现不同的限流算法

文章目录Redis中执行Lua ScriptRedis中Debug Lua Script固定窗口代码关键部分解释验证滑动窗口令牌桶漏桶参考资料Redis中执行Lua Script redis-cli --eval /tmp/script.lua mykey somekey , arg1 arg2特别注意&#xff1a;key和arg之间是空格逗号空格&#xff0c;否则脚本调用…

API网关在API安全性中的作用

从单一应用程序切换到微服务时&#xff0c;客户端的行为不能与客户端具有该应用程序的一个入口点的行为相同。简单来说就是微服务上的某一部分功能与单独实现该应用程序时存在不同。 目前在使用微服务时&#xff0c;客户端必须处理微服务体系结构带来的所有复杂性&#xff0c;…

一个月裂变50多万人?商城引流模式玩法——分享购

随着经济进入转型的关键期&#xff0c;零售行业的营销模式正在发生转变&#xff0c;以消费者为中心驱动营销数字化成为当下企业的共识。 新零售时代数字化的核心是数据能力的全面升级&#xff0c;企业通过数字化协同能力整合线上线下全域消费场景&#xff0c;赋能消费者深度洞…

JDK16及其以后的版本Maven打包成可运行的程序的方法

Java应用的分发一直是一个比较麻烦的问题。这是因为Java应用的运行需要虚拟机的支持&#xff0c;仅有Java应用打包的JAR文件是不够的&#xff0c;目标机器还需要安装版本匹配的JDK或JRE。随着云原生和容器化技术的流行&#xff0c;Java应用可以选择以容器镜像的形式来打包和分发…

以太网,拥塞控制与 AQM

拥塞的表现是冲突&#xff0c;大量消费者挤兑有限资源&#xff0c;即拥塞&#xff0c;在一个绝对没有冲突的系统里&#xff0c;自然就没有拥塞。而冲突的来源有两个&#xff0c;一个是无序&#xff0c;一个是贪婪。 仅引入秩序不能解决冲突&#xff0c;但可减少冲突损耗&#…

JavaScript高级 浏览器的渲染原理

浏览器的渲染原理1. 网页的解析过程2. 浏览器内核3. 浏览器渲染过程1. HTML解析2. 生成CSS规则3. 构建Render Tree4. 布局&#xff08;layout&#xff09;5. 绘制&#xff08;Paint&#xff09;4. 回流和重绘解析1. 回流2. 重绘3. web 性能优化4. 合成和性能优化5. 浏览器遇到 …

【VulnHub靶场】——CHRONOS: 1

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右…

PaddlePaddle/PaddleX本地离线安装(分别以C++和Python为例)

目录一. 本地离线安装简介1.1 为什么需要源码编译1.2 源码编译的产物1.3 源码编译的原理二. 本地离线安装准备2.1 编译准备(C/Python通用)2.2 本地编译推荐配置(C)2.3 本地编译推荐配置(Python)三. Windows本地离线安装(C源码编译)3.2 编译工具安装3.3 打开Visual studio 终端3…

torch.cat函数

#dim0,上下拼接 #dim1&#xff0c;左右拼接 import torch # x1 x1 torch.tensor([[1,2,3],[4,5,6]],dtypetorch.int)# x2 x2 torch.tensor([[7,8,9],[10,11,12]],dtypetorch.int)#inputs为&#xff12;个形状为[2 , 3]的矩阵 inputs [x1, x2] print(inputs)#dim0,上下拼接…