一、基础
1.变量
强类型语言
-
声明单个变量
Go语言在声明变量时,自动对变量对应的内存进行初始化操作,不同于C语言的默认不初始化。
声明变量并初始化
类型推断
-
声明多个变量
var( a int b string c float32 )
-
简短声明
语法:name := initialvalue
注意::= 操作符的左边至少有一个变量是尚未声明的 -
多重赋值
用于交换两个值的内容
var a int = 100 var b int = 200 a,b = b,a
-
匿名变量(Lua中叫哑元变量)
_ ,使用匿名变量是,只需要在变量声明的位置用下划线(_)替换即可
2.数据类型
-
bool
bool类型无法参与数值运算,,也无法与其他类型进行转换
-
数字
- int8 , int16 , int32 , int64 , int(常用整形)
- uint8 , unit16 ,unit32 , unit64 , unit (无符号,最小0)
- float32 , float64 (浮点)
- complex64 , complex128 (复数,几乎不会用到吧?)
- byte (同 unit8)
- rune (同 int32)
-
string
Go中字符串作为基础数据类型出现。
- 多行字符串
使用反引号 ”`“实现
使用反引号包含的字符串,所有转义符均无效,文本将原样输出。(用处应该不会很大)- 字符串实现基于UTF-8
- 字符
字符有一下两种:
一种是unit8类型(byte),代表一个ASCII码字符。
一种是rune类型,代表一个UTF-8字符。rune实际上就是一个int32。另外,UTF-8和Unicode的区别: Unicode是字符集,类似ASCII字符集,为每个字符分配一个唯一的ID。UFT-8是编码规则,将Unicode的字符ID以某种方式进行编码。 广义的Unicode是指一个标准。
在 Printf 方法中,使用 %T 格式说明符(Format Specifier),可以打印出变量的类型。Go 的 unsafe 包提供了一个 Sizeof 函数,该函数接收变量并返回它的字节大小。unsafe 包应该小心使用,因为使用 unsafe 包可能会带来可移植性问题。不过出于本教程的目的,我们是可以使用的。
格式说明符 %T 用于打印类型,而 %d 用于打印字节大小可以使用 "+" 拼接
- 计算字符串的长度
Go语言内建函数 len() 可以获取字符串的字符数。结果为ASCII码个数,汉子等其他字符集会出错。
UTF-8包提供 RuneCountInString() 函数,可以获取Unicode字符个数。使用: len(string) utf8.RuneCountInString(string)
- 遍历字符串
遍历ASCII码字符使用for循环即可,直接用下表取。
遍历Unicode使用 for range使用: for i := 0 ; i < len(str) ; i++{ fmt.Printf("%c %d",str[i],str[i]) } for _,s : = range str{ fmt.Printf("%c %d",s,s) }
- 获取字符串的某一段字符(Substring)
strings.Index 正向搜索子字符串
strings.LastIndex 反向搜索子字符串
搜索其实位置可以使用切片偏移制作tracer := "撒夺魂幡,宝宝,baba" comma := strings.Index(tracer, ",") pos := strings.Index(tracer[comma:],"宝宝") fmt.Println(comma,pos,tracer[comma+pos:])
- 修改字符串
Go语言中字符串无法直接修改每一个字符元素,只能通过重新构造新的字符串赋值给原来的字符串来实现。实际上是将字符串转为 []byte 进行修改。
string 和 []byte 可以相互强制类型转换。- 连接字符串
可以使用 "+" 进行连接,也可以使用StringBuilder进行高效的字符串连接。
string1 := "abd" string2 := "def" stringBuilder.WriteString(string1) stringBuilder.WriteStrign(string2) fmt.Println(stringBuilder.String())
-
格式化
延续C语言
动词 功能 %v 值的本来值 %+v 在%v的基础上对结构体字段名和值进行展开 %#v Go语言语法格式的值 %T Go语言语法格式的类型和值 %% %本体 %b 二进制 %o 八进制 %d 十进制 %x 十六进制 %X 十六进制、字母大写 %U Unicode字符集 %f 浮点数 %p 指针,十六进制 - Base64编码
Base64编码是常见的对8比特字节码的编码方式之一。
Go语言的标准库自带了Base64编码算法。import { "encoding/base64" } message := "Hello World" //编码 encodeMessage := base64.StdEncoding.EncodeToString([]byte(message)) //解码 data , err := base64.StdEncoding.DecodeString(encodeMessage)
- 切片(能动态分配的空间)
切片是一个拥有相同类型元素的可变长度的序列。
声明:var name []T
T代表切片元素的类型,可以是int、float、bool、map、slice等
- 指针
每个变量都拥有地址,指针的值就是地址。同C语言,"*"代表指针取值,"&"作为取地址操作符。取地址操作符"&"和取值操作符"*"是一对互补操作符。
通过指针不仅可以取值,还可以修改值。”*“操作符的根本意义是操作指针指向的变量。
*a = *b,代表是将b指针指向的值赋值给a指针指向的值。
而,若a,b都是指针类型,使用 b,a=a,b ,赋值结果并不会影响a,b分别指向的值。创建指针的方式:new(T)
eg:str := new(string) *str = "nanjing" new()
函数可以创建一个对应的类型的指针,创建过程会自动分配内存。(并没有想到有啥应用场景,可以直接使用变量完成事情,为啥要用指针变量?性能更高?)
- 类型转换
Go 有着非常严格的强类型特征。Go 没有自动类型提升或类型转换。
使用 T(value) 进行类型转换。eg: var j float64 = float64(i)
- 类型别名(Type Alias)
类型别名语法:type NewInt = int
类型定义语法:type NewInt int
类型定义会形成一个新的类型,类型别名只会在代码中存在,编译完成时,将不存在。
非本地类型不能自定义方法(只能为当前包中的类型定义)
3.常量
表达式:const name = "value"
Go 的类型策略不允许将一种类型的变量赋值给另一种类型的变量,别名也不行。
数字常量可以在表达式中自由混合和匹配,只有当它们被分配给变量或者在需要类型的代码中的任何地方使用时
-
枚举(一组常量值)
Go语言中现在没有枚举,可以使用常量配合 iota 模拟出枚举
type Weapon int const( Arrow Weapon = iota Blower Rifle Shuriken ) fmt.Println(Arrow,Blower) //使用枚举类型 var weapon Weapon = Arrow
iota不仅只能生成每次增加1的枚举,也可以实现一些强大的枚举
const ( //左位移操作 FlagNone = 1 << iota FlagRed FlagGreen FlagBlue ) //十进制 fmt.Printf("%d %d %d",FlagRed,FlagGreen,FlagBlue) //二进制 fmt.Printf("%b %b %b",FlagRed,FlagGreen,FlagBlue) 输出结果: 2 4 8 10 100 1000
-
枚举转字符串
type ChipType int const( None ChipType = iota CPU GPU ) func (c ChipType) String() string{ switch c{ case None: return "None" case CPU: return "CPU" case GPU: return "GPU" } return "N/A" } //当这个类型需要显示为字符串时,Go语言会自动寻找 String() 方法并进行调用。 fmt.Printf("%s %d", CPU , CPU) 输出: CPU 1
5.包
包用于组织 Go 源代码,提供了更好的可重用性与可读性。由于包提供了代码的封装,因此使得 Go 应用程序易于维护。
main 函数和 main 包
-
创建自定义包
属于某一个包的源文件都应该放置于一个单独命名的文件夹里。按照 Go 的惯例,应该用包名命名该文件夹。
包中函数名首字母大写。 -
导入自定义包
import ( "fmt" "geometry/rectangle" // 导入自定义包 )
导出名字
在 Go 中,任何以大写字母开头的变量或者函数都是被导出的名字。其它包只能访问被导出的函数和变量。相当于Public和Private方法
-
init函数
所有包都可以包含一个 init 函数。init 函数可用于执行初始化任务,也可用于在开始执行之前验证程序的正确性。
包的初始化顺序如下:首先初始化包级别(Package Level)的变量(有点全局变量的意思)
紧接着调用 init 函数。包可以有多个 init 函数(在一个文件或分布于多个文件中),它们按照编译器解析它们的顺序进行调用。如果一个包导入了另一个包,会先初始化被导入的包,尽管一个包可能会被导入多次,但是它只会被初始化一次。
import ( "fmt" "geometry/rectangle" // 导入自定义包 "log" ) /* * 1. 包级别变量 */ var rectLen, rectWidth float64 = 6, 7 /* *2. init 函数会检查长和宽是否大于0 */ func init() { println("main package initialized") if rectLen < 0 { log.Fatal("length is less than zero") } if rectWidth < 0 { log.Fatal("width is less than zero") } } func main() { fmt.Println("Geometrical shape properties") fmt.Printf("area of rectangle %.2f\n",rectangle.Area(rectLen, rectWidth)) fmt.Printf("diagonal of the rectangle %.2f",rectangle.Diagonal(rectLen, rectWidth)) }
-
使用空白标识符
导入了包,却不在代码中使用它,这在 Go 中是非法的。然而,在程序开发的活跃阶段,又常常会先导入包,而暂不使用它。遇到这种情况就可以使用空白标识符 _。package main import ( "geometry/rectangle" ) var _ = rectangle.Area // 错误屏蔽器 func main() { }
有时候我们导入一个包,只是为了确保它进行了初始化,而无需使用包中的任何函数或变量。(用于一些配置文件的引入?)
```
package main
import (
_ "geometry/rectangle"
)
func main() {
}
```