这是我参与「第三届青训营 -后端场」笔记创作活动的第2篇笔记
1 前言
在对go 语言有了一段时间的学习经历之后,我越发觉得我已经到了开始学习go 的阶段了,所以重新回看青训营视频,回炉重造。
2 写Go语言需要注意的地方
Go语言语句后边不要带分号(它会在每行后自动加分号) Go语言应用程序的执行入口是main()函数 Go语言严格区分大小写 由于Go编译器是一行行进行编译的,因此一行就写一条语句,不能把多条语句写在同一行 Go语言定义的变量或者import的包如果没有使用到,代码不能编译通过
2.1 Go语言编译执行和直接run的区别
- 如果先编译再执行,那么编译后的二进制可执行文件可以拷贝到没有go开发环境的机器上,仍然可以运行。
- 如果直接run源代码,那么如果这段代码拷贝到其它没有go开发环境的机器上,是无法正常执行的
- 在编译时,编译器会将程序依赖的库文件包含在可执行文件中,所以编译后的可执行文件变大了许多
2.2 Go语言的特点
Go语言保证了既能达到静态编译语言的安全和性能,又达到了动态语言开发和维护的高效率,使用一个表达式来形容Go语言:Go=C + Python
- Go从C语言中集成了很多理念,包括表达式语法,控制结构,基础数据类型,调用参数传值,指针等等,也保留了和C语言一样的编译执行方式及弱化的指针。
- 引入了包的概念,用于组织程序结构,Go语言的一个文件都要归属于一个包,而不能单独存在。
- 垃圾回收机制gc,内存自动回收,不需要开发人员管理。
- 天然支持并发。
(1)从语言层面支持并发,实现简单。 (2)goroutine,轻量级线程,可实现大并发处理,高效利用多核。 (3)基于CPS并发模型(Communicating Sequential Processes)实现。
- 吸收了管道通信机制,形成Go语言特有的管道channel。
- 函数可返回多个值。
- 支持切片、延时执行defer等等。
2.3 Linux下配置Go环境变量
vim /etc/profile
export GOROOT=/opt/go
export PATH=$GOROOT/bin:$PATH
export GOPATH=$HOME/goprojects/
SDK: 软件开发工具包,首先要配置好SDK GOROOT: 指定SDK的安装目录 GOPATH: GO项目的目录 Path: 指定SDK目录下边的bin目录
单行注释:
//尽量用行注释来注释代码块
//Ctrl + / 快速注释
块注释:
/*
...
*/
// 块注释中不允许有块注释嵌套
2.4 随记
为什么需要变量 变量是程序的基本组成单位,其相当于内存中的一个数据存储空间的表示,我们通过变量名可以访问到变量(值)
变量的基本使用:
1、声明变量(定义变量)
2、赋值
3、使用
gofmt -w xxx.go 可以自动对齐代码
3 go_code
3.1 Variable(变量)
3.1.1 int
package main
// import "fmt"
// import "unsafe"
import (
"fmt"
"unsafe"
)
func main() {
// 有符号整数(int)的范围
// int8 1bytes -128~127
// int16 2bytes -2^15~2^15-1
// int32 4bytes -2^31~2^31-1
// ...
var y int8 = 127 //当y为128时就会报错
fmt.Println("y=", y)
// 无符号整数(uint)的范围
// uint8 1bytes 0~255
// uint16 2bytes 0~2^15-1
// uint32 4bytes 0~2^32-1
// ...
var h uint8 = 255
fmt.Println("h=", h)
// rune 有符号整数类型 类似int32 表示一个Unicode码
// byte 无符号整数类型 与uint8等价 当要存储字符时选用byte
var a int = 9000
var b uint = 1
var c byte = 255
var d rune = 100
fmt.Println("a=", a, "b=", b, "c=", c, "d=", d)
// 查看某个变量的数据类型 fmt.Printf()
fmt.Printf("a 的类型 %T\n", a)
// 查看某个变量的字节大小
fmt.Printf("a 的类型 %T,占用的字节数是 %d", a, unsafe.Sizeof(a))
// 在Golang中使用变量的时候,遵守保小不保大的原则
// 即:在保证程序正确运行下,尽量使用占用空间小的数据类型,如年纪age可以使用byte类型
var age byte = 20
fmt.Println("age=",age)
// bit:计算机中的最小存储单位。byte:计算机中基本存储单元 1byte = 8bit
}
3.1.2 float
package main
import (
"fmt"
)
func main() {
var price float32 = 88.88
fmt.Println("price is ", price)
// go语言中小数类型分为两大类,单精度float32,双精度float64
// float32 4bytes
// float64 8bytes
// 浮点数=符号位+指数位+尾数位(浮点数都是有符号位的)
var num1 float32 = -0.00001
var num2 float64 = -78098238.08
fmt.Println("num1=",num1, "num2=", num2)
fmt.Println("num1=",num1, "num2=", num2)
// 尾数部分可能丢失,造成精度丢失,同样记录一个-123.00000901双精度要比单精度更精确
// Golang的浮点类型默认声明为float64
var num3 = 2.2
fmt.Printf("num3的数据类型为%T\n",num3)
// 十进制数形式:
num4 := 5.12
num5 := .123 // =>0.123
fmt.Println("num4=",num4, "num5=", num5)
fmt.Println("num4=",num4, "num5=", num5)
// 科学计数法形式
num6 :=5.12345e2 // 5.12345 * 10的2次方 512.345
num7 :=5.12345E2 // 5.12345 * 10的2次方 512.345
num8 :=5.12345E-2 // 5.12345 / 10的2次方 0.0512345
fmt.Println("num6=",num6, "num7=", num7, "num8", num8)
// 推荐使用float64
}
3.1.3 string
package main
import (
"fmt"
)
func main() {
var address string = "我爱你中国 12345 hello world"
fmt.Println(address)
// 字符串一旦赋值内容就不能修改:在go中字符串是不可变的
// var str = "hello"
// str[0] = 'a' 这样就会报错
// 字符串有两种表示形式
// (1)双引号,会识别转义字符
// (2)反引号,以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、输出源代码等效果
str2 := "abc\nabc"
fmt.Println(str2)
str3 := `
afd
afdf
af
asf
afds
`
fmt.Println(str3)
// 字符串拼接 +
var str4 = "hello" + "world"
str4 += " haha!"
fmt.Println(str4)
// 当一个拼接的操作很长时,可以分行写,拼接符留在上一行
var str5 = "hello" + "world" +
"hello" +
"world"
fmt.Println(str5)
}
3.1.4 byte
package main
import (
"fmt"
)
// 字符串就是一串固定长度的字符连接起来的字符序列
func main() {
// Golang中没有专门的字符类型,如果要存储单个(字母),一般使用byte来保存
// Golang的字符串是由字节组成的
var c1 byte = 'a'
var c2 byte = '0' // 字符的0
fmt.Println("c1=", c1, "c2=", c2) // c1= 97 c2= 48 会输出ASCII码值
fmt.Printf("c1=%c c2=%c\n", c1, c2) // c1=a c2=0
// var c3 byte = '京' // 结果会溢出 overflow
var c3 int = '京'
fmt.Printf("c3=%c c3对应的码值=%d\n", c3, c3) // c3=京 c3对应的码值=20140
// 如果我们保存的字符在ASCII表中,比如[0-9,a-z,A-Z]可以直接保存到byte
// 如果我们保存的字符对应码值大于255,这时我们可以考虑使用int类型保存
// 如果我们需要按照字符的方式输出,需要格式化输出,即 fmt.Printf("%c")
// utf-8编码包含了ASCII编码,英文字母1个字节,汉字3个字节
// 在Go中,字符的本质是一个整数,直接输出时,是该字符对应的UTF-8编码的码值
var c4 int = 22269 // 22269 -> '国'
fmt.Printf("c4=%c\n", c4)
// 由于字符类型本质是一个整数,所以其也是可以运算的,运算的时候是按照码值运算
var n1 = 10 + 'a'
fmt.Println("n1=", n1) //n1= 107
}
// 字符型存储到计算机中,需要将字符对应的码值(整数)找出来
// 存储:字符-->对应码值-->二进制-->存储
// 读取:二进制-->码值-->字符-->读取
// Go语言的编码都统一成了utf-8,所以不会有乱码出现
3.1.5 bool
package main
import (
"fmt"
"unsafe"
)
func main() {
var b = false
fmt.Println("b=",b)
// bool类型占用的存储空间是1个字节
fmt.Println("b 的占用空间 =", unsafe.Sizeof(b))
// 布尔类型也叫bool类型,只允许其取值true or false
// 常用在循环语句中
// bool值默认是false
}
3.1.6 Var Transform(类型转换)
3.1.6.1 string转基本数据类型
package main
import (
"fmt"
"strconv"
)
func main() {
var str string = "true"
var b bool
// 转bool
// strconv.ParseBool会返回两个值,第一个值是布尔值,第二个值是error值
// 我们只想获取到布尔值,对err不关心,所以可以用下划线 _ 来忽略掉err
b , _ = strconv.ParseBool(str)
fmt.Printf("b type %T b=%v\n",b, b)
// 输出结果:b type bool b=true
// 转int
var str2 string = "123456"
var n1 int64
var n2 int
n1, _ = strconv.ParseInt(str2, 10, 64)
n2 = int(n1)
fmt.Printf("n1 type %T n1=%v\n", n1, n1)
fmt.Printf("n2 type %T n2=%v\n", n2, n2)
var str3 string = "123.456"
var f1 float64
f1, _ = strconv.ParseFloat(str3, 64)
fmt.Printf("f1 type %T f1=%v\n",f1 ,f1)
// 在String类型转成基本数据类型时,要确保String类型能够转成有效的数据
// 比如我们可以把"123"转成一个整数,但是不能把“hello”转为一个整数,如果这样Golang会将其转为0
// 如果n3没有转换成功,则会转成默认值0
var str4 string = "hello"
var n4 int64 = 11
n4, _ = strconv.ParseInt(str4, 10, 64)
fmt.Printf("n4 type %T n4=%v\n", n4, n4)
// 输出结果:n4 type int64 n4=0
// 字符串转换为bool的情况会转为默认false
var str5 string = "hellook"
var b2 bool = true
b2 , _ = strconv.ParseBool(str5)
fmt.Printf("b2 type %T b2=%v\n",b2, b2)
// 输出信息:b2 type bool b2=false
}
3.1.6.2 基本数据类型转string
// 在开发中,经常需要将基本数据类型转成string类型
// 或者将string类型转成基本数据类型 package main
import (
"fmt"
_ "unsafe"
"strconv"
)
func main() {
var num1 int = 99
var num2 float64 = 22.222
var b bool = true
var mychar byte = 'h'
var str string //空的str
// 使用第一种方式来转换 fmt.Sprintf方法
str = fmt.Sprintf("%d", num1)
fmt.Printf("str type %T str=%q\n", str, str)
str = fmt.Sprintf("%f", num2)
fmt.Printf("str type %T str=%q\n", str, str)
str = fmt.Sprintf("%t", b)
fmt.Printf("str type %T str=%q\n", str, str)
str = fmt.Sprintf("%c", mychar)
fmt.Printf("str type %T str=%q\n", str, str)
// 第二种方式转换 strconv 函数
var num3 int = 99
var num4 float64 = 23.456
var b2 bool = true
str = strconv.FormatInt(int64(num3), 10)
fmt.Printf("str type %T str=%q\n", str, str)
// 'f'格式;10表示小数部分保留几位;64表示这个小数是float64
str = strconv.FormatFloat(num4, 'f', 10, 64)
fmt.Printf("str type %T str=%q\n", str, str)
// 输出:str type string str="23.4560000000"
str = strconv.FormatBool(b2)
fmt.Printf("str type %T str=%q\n", str, str)
// 第三种 strconv包中有一个函数Itoa,可以直接把一个int转为string
var num5 int = 4567
str = strconv.Itoa(num5)
fmt.Printf("str type %T str=%q\n", str, str)
}
3.1.6.3 基本数据类型转换
package main
import (
"fmt"
_ "unsafe" // 如果我们没有使用到一个包,但是不想去掉,就在前边加一个 _ 表示忽略
)
func main() {
var i int = 100
// 将i ==> float
var n1 float32 = float32(i)
var n2 int8 = int8(i)
var n3 int64 = int64(i)
fmt.Printf("i=%v n1=%v n2=%v n3=%v\n", i, n1, n2, n3)
// Go中数据类型的转换可以是从表示范围小-->表示范围大 也可以范围大-->范围小
// 被转换的是变量存储的数据(即值),变量本身的数据类型并没有发生变化
// 在转换中,比如讲int64 转换成 int8 [-128~127] ,编译时不会报错
// 只是转换的结果是按照溢出处理的,和我们希望的结果不一样
var num1 int64 = 999999
var num2 int8 = int8(num1)
fmt.Println("num2=", num2)
// 输出结果:num2= 63
var (
m1 int32 = 12
m2 int64
m3 int8
)
m2 = int64(m1) + 20
m3 = int8(m1) + 20
fmt.Println("m2=", m2, "m3=", m3)
var (
b1 int32 = 12
b2 int8
// b3 int8
)
b2 = int8(b1) + 127 // 编译通过,但是结果不是127+12,按照溢出处理,输出结果为·117
// b3 = int8(b1) + 128 编译不通过
fmt.Println(b2)
}