Go语言基础(二)—— 基本常用语法

栏目: IT技术 · 发布时间: 4年前

前言:

647

目录如下:

Go语言基础(一)—— 简介、环境配置、HelloWorld

Go语言基础(二)—— 基本常用语法

Go语言基础(三)—— 面向对象编程

Go语言基础(四)—— 优质的容错处理

Go语言基础(五)—— 并发编程

Go语言基础(六)—— 测试、反射、Unsafe

Go语言基础(七)—— 架构 & 常见任务

Go语言基础(八)—— 性能调优

本篇将介绍如下内容:

1.如何编写一个 Go 测试程序?

2.变量、常量的定义

3.基本数据类型

4.指针类型

5.运算符

6.条件与循环

7.数组与切片

8.Map

9.字符串

10.函数

(注:可根据数字快速定位本篇内容)

为了接下来更方便的测试我们的Go代码(Go服务), 首先,介绍一下Go语言中如何测试我们的程序。

一、起步:如何编写一个测试程序?

要求:

  1. 源码文件以 _test 结尾: xxx_test.go

  2. 测试方法名以 Test 开头: func TestXXX(t *testing.T){...}

实际步骤:

  • 创建一个 first_test.go 文件。

  • 编写如下代码:

package try_test

import "testing"

func TestFirstTry(t *testing.T) {
	t.Log("My first try!")
}
复制代码

在终端下运行:

go test first_test.go -v
复制代码

Demo:动手实现一个斐波那切(Fibonacci)数列

1,1,2,3,5,8,13,...

  • 创建一个 fibonacci_test.go 文件。

  • 编写如下代码:

package fibonacci

import (
	"testing"
)

func TestFibList(t *testing.T) {
	// var a int = 1
	// var b int = 1

	// var (
	// 	a int = 1
	// 	b int = 1
	// )

	a := 1
	b := 1

	for i := 0; i < 5; i++ {
		t.Log(" ", b)
		tmp := a
		a = b
		b = tmp + a
	}
}
复制代码
  • 在终端下运行:
go test fibonacci_test.go -v
复制代码

二、变量、常量的定义

1.变量

变量的定义,有3种方式:

  • 第一种,常规逐一声明:
var a int = 1
	var b int = 1
复制代码
  • 第二种:统一声明:
var (
	  a int = 1
	  b int = 1
	)
复制代码
  • 第三种:快速声明,编译器会根据所附的值推断出该变量的类型。
a := 1
	b := 1
复制代码

2.常量

  • 常规赋值:
const abc = 2
复制代码
  • 快速设置连续常量值:
// 连续位常量赋值
const (
	Monday = iota + 1
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
	Sunday
)
复制代码
  • 快速设置连续比特位常量值:
// 比特位常量赋值
const (
	Readabele = 1 << iota
	Writeable
	Executable
)
复制代码

三、基本数据类型

类型 语法
布尔型 bool
字符型 string
整型 int、int8、int16、int32、int64
无负号整型 uint、uint8、uint16、uint32、uint64、uintptr
浮点型 float32、float64
字符型 byte(相当于uint8)
Unicode或utf-8编码 rune(相当于int32)
复数型 complex64、complex128

注意:Go语言不支持**“隐式类型转换” ,只支持 “显式类型转换” 。 (甚至连 “别名类型” “原有类型”**之间也不支持隐式类型转换。)

举例:

var a int32 = 1
	var b int64

	b = a        // 错误:隐式类型转换,会报编译错误错误。
	b = int64(a) // 正确:显式类型转换,成功。
复制代码

常用典型的预定义值:

预定义 语法
最大Int64 math.MaxInt64
最小Int64 math.MinInt64
最小Int32 math.MaxInt32
最小Int32 math.MinInt32
最大float64 math.MaxFloat64
最大float32 math.MaxFloat32
... ...

四、指针类型

与其他编程语言的差异:

  • 不支持指针运算。

  • string 是值类型,其默认的初始化值为 空字符串 ,而不是 nil

举例:

func TestPoint(t *testing.T) {
	a := 1
	aPoint := &a
	// aPoint = aPoint + 1 // 错误:Go不支持指针运算。
	t.Log(a, aPoint)
	t.Logf("%T %T", a, aPoint)
}

func TestString(t *testing.T) {
	var s string
	t.Log("字符串:" + s + "?")
	t.Log(len(s))

	if s == "" { // 判断字符串有无初始化不能判nil,因为string类型的初始化默认是空字符串
		t.Log("s并未初始化")
	}
}
复制代码

五、运算符

1.算数运算符:

运算符 描述
+
-
*
/
% 取余
a++ 后置自增
a-- 后置自减

注:Go语言中没有前置的自增(++)和自减(--)。

2.比较运算符:

运算符 描述
== 判断值是否 “相等”
!= 判断值是否 “不相等”
> 判断是否 “大于”
< 判断是否 “小于”
>= 判断是否 “大于等于”
<= 判断是否 “小于等于”

注:两个数组之间的 == 比较的条件? (还是值比较,并非引用比较) 1.两个数组拥有 “相同的元素个数” 。 2.两个数组中的 “每个元素的值都相等” ,才会返回 true

我们写个小 demo ,测试一下:

a := [...]int{1, 2, 3, 4}
	b := [...]int{1, 3, 4, 5}
	c := [...]int{1, 2, 3, 4}
	d := [...]int{1, 2, 3, 4, 5}

	t.Log(a == b) // false
	t.Log(a == c) // true

	// t.Log(a == d) // 编译报错,数组元素不一致
	t.Log(d)
复制代码

3.逻辑运算符:

和别的语言没太大差别,简单提一下。

运算符 描述
&& AND(与)运算符,同 truetrue ,否则 false
\ |
! NOT(非)运算符,取反, truefalsefalsetrue

4.位运算符:(新增 &^ 按位清零运算符)

运算符 描述
& 二进制“与”运算。
\
^ 二进制“异或”运算。
<< 二进制“左移”运算。
>> 二进制“右移”运算。
&^ 将二进制 “按位清零” 。将右边所有为1的位数全置为0。(对左边数进行操作)

注意:多了一个 &^ ,按位清零运算符。

举个栗子:

// 连续位常量赋值
const (
	Monday = iota + 1
	Tuesday
	Wednesday
	Thursday
	Friday
	Saturday
	Sunday
)

// 测试按位清零
func TestBitClear(t *testing.T) {
	a := 5 // 0101
	t.Log("按位清零前:", a) // 0101

	a = a &^ Wednesday // 5 &^ 3
	t.Log("按位清零后:", a) // 0100
}
复制代码

六、条件与循环

1.条件:if

与别的编程语言的区别:

bool

类似这样:

if var declaration; condition {
		//...
	}
复制代码

举个简单例子:

if a := 647; a == 647 {
		t.Log(a)
	}
复制代码

如果写了个请求,类似就成了这样:

if v, err := someFunc(); err == nil {
		// 无error,成功!do something!
	} else {
		// 有error,失败!do something!
	}
复制代码

2.条件:switch

switch与其他主流编程语言有些区别,主要是变得更方便、更快捷了。

  1. 默认 break ,不需要主动写 break 。(如果想要贯穿,才用 fallthrough 关键字,这点与swift很像。注意:但我测试结果走 fallthrough 并不会去判断下一个 case 条件,而是直接执行下一个 case 里的代码)
  2. 条件表达式不限制“常量”或“整数”。
  3. 单个 case 支持多个结果选项,用逗号隔开。
  4. 可以不设定 switch 之后的条件表达式,这样与多个 ifelse 逻辑相同。

举个例子:

func TestSwitchCondition(t *testing.T) {
	for i := 0; i < 5; i++ {
		switch i {
		case 0, 2:
			t.Log("Even")
		case 1, 3:
			t.Log("Odd")
		default:
			t.Log("it is not 0-3")
		}
	}
}

func TestSwitchCaseCondition(t *testing.T) {
	for i := 0; i < 5; i++ {
		switch {
		case i%2 == 0:
			t.Log(i)
			// fallthrough
		case i%2 == 1:
			t.Log(i)
			// fallthrough
		default:
			t.Log("unknown")
		}
	}
}
复制代码

3.循环:Go只支持for关键字。

举个例子:

func TestWhileLoop(t *testing.T) {
	/* 模仿while循环 */
	n := 0
	for n < 5 {
		t.Log(n)
		n++
	}

	// for循环
	for n := 0; n < 5; n++ {
		t.Log(n)
	}

	/* 死循环 */
	for {
      //...
	}
}
复制代码

七、数组与切片

1.数组

  • 数组的声明:
var a [3]int // 声明并初始化为默认值0
	a[0] = 1     // 简单赋值

	b := [3]int{1, 2, 3}           // 声明的同时进行初始化
	c := [2][2]int{{1, 2}, {3, 4}} // 声明多维数组进行初始化
复制代码
  • 数组的遍历:
func TestArrayTravel(t *testing.T) {
	arr := [...]int{1, 3, 4, 5}

	for i := 0; i < len(arr); i++ {
		t.Log(arr[i])
	}

	for _, element := range arr {
		t.Log(element)
	}

	for index, element := range arr {
		t.Log(index, element)
	}
}
复制代码
  • 数组的截取:
func TestArraySection(t *testing.T) {
	arr := [...]int{1, 2, 3, 4, 5}
	arr_sec := arr[1:2]
	t.Log(arr_sec)
}
复制代码

2.切片

切片是一种可变长的结构体。(有点类似于iOS中的 MutableArray

  • 切片的声明:
var s0 []int       // 声明
	s0 = append(s0, 1) // 追加

	s := []int{}            // 声明
	s1 := []int{1, 2, 3}    // 声明并提供初始化值
	s2 := make([]int, 2, 4) //声明并提供初始化个数:2,max最大元素个数:4。
	/*
		语法:make([]type, len, cap)
		其中len个元素会被初始化为默认值0,未初始化的元素不可访问。
	*/
复制代码

1. 快速声明语法: make([]type, len, cap) 其中,前 len 个元素会被初始化为默认值 0 ,未初始化的元素不可访问。(这是个容易造成crash的点,不能越界操作。)

2. 追加元素语法: s = append(s , element) 其中 s 代表切片, element 代表追加的元素。

  • 切片的简单使用: 直接上demo,
func TestSliceInit(t *testing.T) {
	var s0 []int
	t.Log(len(s0), cap(s0))

	s0 = append(s0, 1)
	t.Log(len(s0), cap(s0))

	s1 := []int{1, 2, 3, 4} // 快速初始化切片
	t.Log(len(s1), cap(s1))

	s2 := make([]int, 3, 5) // 初始化3个,max最大个数为5
	t.Log(len(s2), cap(s2))

	t.Log(s2[0], s2[1], s2[2])

	s2 = append(s2, 1) // 添加元素
	t.Log(s2[0], s2[1], s2[2], s2[3])
	// t.Log(s2[0], s2[1], s2[2], s2[3], s2[4])
}
复制代码
  • 切片的原理:切片的共享存储结构。 当切片元素个数 len 超过 cap 时,会进行 2 倍扩容。
Go语言基础(二)—— 基本常用语法

八、Map(键值对Key: Value)

1.Map基本操作:

  • Map的三种初始化方式:
/*
		第一种初始化方式:有初始值
	*/
	m1 := map[string]int{"Key1": 1, "Key2": 2, "Key3": 3}
	t.Log(m1)
	t.Logf("len m1 = %d", len(m1))

	/*
		第二种初始化方式:无初始值
	*/
	m2 := map[string]int{}
	m2["Key"] = 16
	t.Logf("len m2 = %d", len(m2))

	/*
		第三种初始化方式:初始化cap大小(最大容量),但len依然为0。(理由如下,map不像数组有默认值0,所以len依然为0)
	*/
	m3 := make(map[string]int, 10)
	t.Logf("len m3 = %d", len(m3))
复制代码
  • Map元素的访问:

当访问的 Map 不存在指定的 Key 时,会默认返回 0 值。 因此,Go语言中,不能通过 Value 是否为空来判断 Key 是否存在。

如果想要判断 Value 是否存在,可用如下方式:

if value, ok := map[key]; ok {
  // 有值
} else {
  // 无值
}
复制代码

Demo:

func TestAccessNotExistingKey(t *testing.T) {
	m1 := map[int]int{} // 初始化一个空map
	t.Log(m1[1])        // 随便访问一个Key?打印结果为0

	m1[2] = 0    // 设置一个Key(2)和Value(0)。
	t.Log(m1[2]) // 打印一下还是0

	if value, ok := m1[3]; ok { // var v = m1[3], ok是表达式的bool值
		t.Logf("Key 3‘s value is %d", value)
	} else {
		t.Log("Key 3 is not existing.")
	}
}
复制代码
  • Map的遍历:

与遍历数组for-range类似,但不同点在于 “数组返回的是index,Map返回的是Key”

func TestTravelMap(t *testing.T) {
	map1 := map[string]int{"Key1": 1, "Key2": 2, "Key3": 3}
	for key, value := range map1 {
		t.Log(key, value)
	}
}
复制代码

2.Map与工厂模式

Value
Dock type
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 }
	t.Log(m[1](2), m[2](2), m[3](3))
}
复制代码

3.在Go语言中实现Set

Go没有Set的实现,但可以通过 map[type]bool 来实现。

  1. 元素的唯一性
  2. 基本操作(添加元素、判断元素是否存在、删除元素、元素个数)
func TestMapForSet(t *testing.T) {
	mySet := map[int]bool{} // 初始化map
	mySet[1] = true         // 设置key、value
	n := 3
	if mySet[n] {
		t.Logf("%d is existing", n)
	} else {
		t.Logf("%d is not existing", n)
	}

	mySet[3] = true // 设置Key、value
	t.Log(len(mySet))

	delete(mySet, 1) // 删除Key为1的map
	t.Log(len(mySet))
}
复制代码

九、字符串

与其他编程语言的差异:

  • string“值类型” ,而不是引用或指针类型。因此,默认初始化时,会是一个 "" 空的字符串。(而不是 nil

  • string 是只读的 byte slicelen 函数返回的是 string 中的 byte 数。

  • string 类型的 byte 数组可以存放任何数据。

例如:

s = "\xE4\xB8\xA5" // 可以存储任何二进制数据
复制代码
  • len 求出来的是 byte 数,并不是字符数。

问:Unicode 与 UTF8 的关系?

答:

Unicode 是一种字符集。( code point

UTF-8Unicode 的存储实现。(转换为字节序列的规则)

举个例子:

编码 存储
字符 "中"
Unicode 0x4E2D
UTF-8 0xE4B8AD
string/[]byte [0xE4, 0xB8, 0xAD]
  • 使用Go语言支持的 stringsstrconv 库:

首先,要导入strings和strconv库:

import (
	"strconv"
	"strings"
)
复制代码

字符串的分割与拼接的demo:

// 测试strings库
func TestStringFunc(t *testing.T) {
	s := "A,B,C"
	parts := strings.Split(s, ",") // 字符串按","分割
	for _, part := range parts {
		t.Log(part)
	}
	t.Log(strings.Join(parts, "-")) // 字符串按"-"拼接
}
复制代码

字符串转型demo:

// 测试strconv库
func TestConv(t *testing.T) {
	s := strconv.Itoa(10) // Int转string
	t.Log("str" + s)

	if value, err := strconv.Atoi("10"); err == nil {
		t.Log(10 + value)
	} else {
		t.Log("转换不成功!")
	}
	// t.Log(10 + strconv.Atoi("10")) // string转Int,编译错误
}
复制代码

十、函数:是一等公民(可作为变量、参数、返回值)

与其他编程语言的区别:

  • 函数可以有 多个返回值

  • 所有参数都是 值传递 ,不是引用传递。

    slicemapchannel 会有传引用的错觉。( slicemapchannel 本身是一个结构体,其中会包含下一块 slicemapchannel 的指针地址。因此我们作为参数传入函数的 slicemapchannel 实际上被复制了一份,但本身操作的其他对象依然会是原 slicemapchannel 的内存地址。)

这块可能稍微有点绕,可以看下slice的实现原理。(PS:可以参考“第七条”切片原理相关内容)

  • 函数可以作为**“变量”**的值。

  • 函数可以作为**“参数” “返回值”**。

重点:函数式编程(Go)

因为在Go语言中,函数可以作为变量、参数、返回值。 因此,在编程习惯上与别的编程语言有些差异。 需要开发者去慢慢适应。

实战小技巧

  1. 支持函数可变长参数

语法: [参数名] ...[参数类型]

PS:不指定参数个数,但需要指定参数类型。

举个例子,我们想写个求和函数,就可以这么写:

// 求和函数
func sum(ops ...int) int {
	s := 0
	for _, op := range ops {
		s += op
	}
	return s
}
复制代码
  1. 支持函数延时执行,多用于释放资源(解锁)

还是举个例子:

// 模仿释放资源的函数
func Clear() {
	fmt.Println("Clear resources.")
}

// 测试函数延时执行:多用于释放资源
func TestDefer(t *testing.T) {
	defer func() {
		// 用于释放一些资源(锁)
		Clear()
	}()
	fmt.Println("Started")
	panic("Fatal error") // panic:程序异常中断,defer仍会执行
}
复制代码

最后,本系列我是在蔡超老师的 技术分享 下总结、实战完成的, 感谢蔡超老师的 技术分享

PS:另附上,分享链接: 《Go语言从入门到实战》 祝大家学有所成,工作顺利。谢谢!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

编码整洁之道

编码整洁之道

罗伯特·C.马丁 / 电子工业出版社 / 2012-8 / 59.00元

忍受各种不确定性及不间断的压力并能够获取成功的程序员有一个共通特征:他们都深度关注软件创建实践。他们都把软件看做一种工艺品。他们都是专家。在“鲍勃大叔”看来“专业”的程序员不仅应该具备专业的技能,更应该具备专业的态度,这也是本书阐述的核心。专业的态度包括如何用带着荣誉感、自尊、自豪来面对进行软件开发,如何做好并做得整洁,如何诚实地进行沟通和估算,如何透明并坦诚地面对困难做抉择,如何理解与专业知识相......一起来看看 《编码整洁之道》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具