2-1 go发展历程、企业应用、语言特性
go编程语言特性:
自动垃圾回收、函数多返回值、错误处理、闭包与匿名函数、接口与类型、并发编程、反射、内置丰富类型
Mac下go安装与环境变量设置:
在网站 https://golang.google.cn/dl/ 下载 .pkg 安装
GOROOT=/usr/local/go #默认的golang安装路径
GOPATH=$HOME/gopath #默认安装包的路径(源码包, 进行业务的开发, 把源码放到gopath当中, 以及第三方库或从第三方网站下载的库也会安装到默认的gopath当中)
cd $GOPATH目录下有三个文件bin、pkg、src
bin: 用于生成可执行文件.(存放一些之前安装到这里的项目)
pkg: 用于生成的第三方扩展库(.a文件)
src: 存放的源码文件(自己的项目、第三方库项目)
GOBIN=$GOPATH/bin #是我们go执行go install编译之后存在的一个路径, 一般是放在$GOPATH下的bin目录下
PATH=$PATH:$GOROOT/bin:$GOPATH/bin # PATH是系统内建的, 被称为系统的环境变量. 比如去执行安装软件它的一个可执行程序, 当我们设置了PATH, 比如说将对应的GOBIN把它环境变量配置PATH当中, 我们无论在哪个路径下面都会执行.
GOPATH把它加入到我们的环境变量当中,然后保存. 执行$ source ~/.bash_profile 命令, 让它重新生效.
通过命令 $ go env #来查看本机go语言的安装情况
3-1 变量概述、变量格式化输出
///格式化输出的所有格式:
//通用:
%v 值的默认格式表示
%+v 类似%v, 但输出结构体时会添加字段名
%#v 值的go语法表示
%T 值的类型的Go语法表示
%% 百分号
//布尔值:
%t 单词true或false
//进制数:
%b 表示二进制
%c 该值对应的Unicode码值
%d 表示十进制
%o 表示为8进制
%q 该值对应的单引号括起来的go语法字符串面值, 必要时会采用安全的转义表示
%x 表示为十六进制, 使用a-f
%X 表示为十六进制, 使用A-F
%U 表示为Unicode格式, 比如U+1234,等价于"U+%04X"
//浮点数与复数的两个组分:
%b 无小数部分、二进制指数的科学计数法, 如-123456p-78; 参见strconv.FormatFloat
%e 科学计数法, 如-1234.456e+78
%E 科学计数法, 如-1234.456E+78
%f 有小数部分但无指数部分, 如123.456
%F 等价于%f
%g 根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
%G 根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)
//字符串和[]byte:
%s 直接输出字符串或者[]byte
%q 该值对应的双引号括起来的go语法字符串字面值, 必要时会采用安全的转义表示
%x 每个字节用两个字符十六进制数表示(使用a-f)
%X 每个字节用两个字符十六进制数表示(使用A-F)
//指针:
%p 表示为十六进制, 并加上前导的0x
3-2 变量的使用、变量的内存布局
package main
import "fmt"
///声明多个全局变量
var (
variablesx int
slicex []int
interfacex interface{}
)
func main() {
/// 局部变量的方法:
//方式一
var variables1 int
//方式二: 简短声明. 只能在函数内部这样声明, 在函数外部无法这样声明使用.
variables2 := "我是老大"
//方式三:
var variables3 = 100
//方式四: 一次性申请多个变量
var variables4,variables5,variables6,variables7 = 1,"我是土豆",3.14,true
fmt.Println(variables1,variables2,variables3,variables4,variables5,variables6,variables7)
//简单的变量值交换
var i = 100
var j = 200
i,j = j,i
fmt.Println(i,j)
// _, x := 22, 100 //_声明的变量, 会被丢弃.
//定义变量注意事项:
//go语言当中大写开头的变量, 是可以被其他包读取的使用的, 相当于其他语言的public权限; 变量小写表示只能被本包使用.
var Varaibles = 19
}
3-3 常量概述、常量的使用、常量的应用场景
func main() {
// 常量 const
const constVariables1 float64 = 3.1415926
const constVariables2, constVariables3 = 100, "你好少年"
fmt.Println(constVariables1,constVariables2,constVariables3)
// iota比较特殊, 编译器会修改它的值, 它会在每个const关键字出现时重置为零, 在下一个const出现之前, 每个iota+1
const (
iotaVariables1 = iota //0
iotaVariables2 = iota //1
iotaVariables3 = iota //2
)
const iotaVariables = iota //0
fmt.Println(iotaVariables1,iotaVariables2,iotaVariables3, iotaVariables)
//常量的应用场景:
//比如用const和iota来代替枚举做声明, 表示一周
const (
Monday = iota
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
)
fmt.Println(Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday)//0 1 2 3 4 5 6
const (
iotaVariables8,iotaVariables9,iotaVariables10 = iota,iota,iota// 0 0 0
)
const (
iotaVariables11 = iota //0
iotaVariables12 = "Bobo" //Bobo
iotaVariables13 = iota //2
)
fmt.Println(iotaVariables8,iotaVariables9,iotaVariables10,iotaVariables11,iotaVariables12,iotaVariables13)//0 0 0 0 Bobo 2
}
4-1 数值类型概述、不同数值类型表示范围、数据类型的使用
package main
import (
"fmt"
"unsafe"
)
func main() {
/* 有符号位
int8 占1个字节 数值范围-2^7到2^7-1
int16 占2个字节 -2^15到2^15-1
int32 占4个字节 -2^31到2^31-1
int64 占8个字节 -2^63到2^63-1
*/
/* 无符号位
uint8 占1个字节 数值范围0到2^8-1
uint16 占2个字节 0到2^16-1
uint32 占4个字节 0到2^32-1
uint64 占8个字节 0到2^64-1
*/
var intVariables1 = 100 //int
intVariables2 := 200 //int
fmt.Printf("intVariables1=%T,intVariables2=%T\n",intVariables1,intVariables2)//intVariables1=int,intVariables2=int
var intVariables3 int32 //int32
intVariables := 126 //int
intVariables3 = int32(intVariables)// int转换为int32
fmt.Printf("intVariables3=%T\n",intVariables3)//intVariables3=int32
//查看占用空间大小(字节数)
var intVariables4 int64 = 123456789
fmt.Println(unsafe.Sizeof(intVariables4)) // 8 (占用8个字节)
}
/// ctrl + R 运行程序
4-2 浮点概述、浮点类型的使用、复数的使用
package main
import (
"fmt"
"unsafe"
)
func main() {
/* 有符号位
float32 占4个字节
float64 占8个字节 (默认类型)
*/
//1.以十进制的形式来展示
var floatVariables1 = 3.141592653
fmt.Printf("floatVariables1的类型=%T, 占用的字节大小=%d \n", floatVariables1,unsafe.Sizeof(floatVariables1))//floatVariables1的类型=float64, 占用的字节大小=8
//2.以科学计数法来展示
floatVariables3 := 3.1415926e2 // 表示3.1415926乘以10的2次方
floatVariables4 := 3.1415926e-2 // 表示3.1415926乘以10的-2次方
fmt.Println(floatVariables3,floatVariables4)//314.15926 0.031415926
///复数: 主要由浮点型表示. 实数"+"虚数i
/*
complex64 由32位实数+32位虚数组成, i是虚数的单位
complex128 由64位实数+64位虚数组成, i是虚数的单位 (默认类型)
*/
//复数定义:
var complexVariables1 complex64
complexVariables1 = 3.14+12i//复数的应用场景: 一元二次方程
complexVariables2 := complex(3.14,12)//实数部分是3.14, 虚数部分是12
fmt.Printf("complexVariables1的类型=%T,值=%v\n",complexVariables1,complexVariables1)//complexVariables1的类型=complex64,值=(3.14+12i)
fmt.Printf("complexVariables2的类型=%T,值=%v\n",complexVariables2,complexVariables2)//complexVariables2的类型=complex128,值=(3.14+12i)
//分别打印实数部分和虚数部分
fmt.Println(real(complexVariables1),imag(complexVariables1))//3.14 12
}
4-3 字符概述、使用
package main
import "fmt"
func main() {
/*
字符:
byte(uint8) //占用一个字节, 无符号, 代表一个utf-8的无符号字节值
rune 代表Unicode字符 //rune实质上是int32的别名, 代表utf-8的一个字符
字符转换成-->ASCII码值-->再转换成二进制
二进制-> ASCII码值-->字符
字符可以参与运算
*/
var charVariables1 byte = '0'
charVariables2 := '铁'
fmt.Printf("charVariables2=%c, charVariables2的类型:%T\n",charVariables2, charVariables2)//charVariables2=铁, charVariables2的类型:int32
fmt.Printf("charVariables1 = %d, charVariables2=%d\n",charVariables1,charVariables2)//charVariables1 = 48, charVariables2=38081
variables := 'a' //对应的编码值
fmt.Printf("加和=%d,a的编码值=%d\n",100+variables,variables)//加和=197,a的编码值=97
}
4-4 字符串概述、字符串使用、字符串遍历、中文字符串的处理
package main
import "fmt"
func main() {
/*
字符串:string, 采用的是utf-8编码
*/
var stringVariables1 string
stringVariables1 = "hello 拿小铁\n"
var stringVariables2 = `
a
aa
aaa
`
fmt.Printf("stringVariables1=%T,stringVariables2=%T\n",stringVariables1,stringVariables2)//stringVariables1=string,stringVariables2=string
fmt.Printf("stringVariables1=%v,stringVariables2=%v",stringVariables1,stringVariables2)
//打印字符串长度
fmt.Println(len(stringVariables2))//10
//字符串拼接
fmt.Println("hello"+"拿小铁")
//遍历字符串(以字节的方式遍历)
var stringVariables3 = "hello dbc, 我是拿小铁"
stringVariables3Len := len(stringVariables3)
for index := 0; index <stringVariables3Len; index++ {
fmt.Printf("%s--编码码=%d, 值=%c\n",stringVariables3,stringVariables3[index],stringVariables3[index])
}
///中文字符串的处理
// 遍历字符串:
for _, value := range stringVariables3{
// 此时的value是rune int32
fmt.Printf("%s--编码码=%d, 值=%c, 类型=%T\n",stringVariables3,value,value,value)
}
}
4-5 布尔类型概述、使用
package main
import "fmt"
func main() {
/*
bool true false
*/
var boolVaraibles1 bool
boolVaraibles1 = true
boolVaraibles2 := (true==false)
fmt.Println(boolVaraibles1,boolVaraibles2)
//bool 类型不接受其他类型的赋值, 不支持自定义类型转换
if 3==4 {
fmt.Println("false")
}else {
fmt.Println("true")
}
fmt.Println(!true,!false,!!true)// false true true
/// go语言不支持 ===
}
5-1 指针概述、指针使用、指针内存布局分析
package main
import "fmt"
func main() {
/*指针:
指针主要用于管理内存.
如果内存管理不当就会出现指针的混乱传递, 比如某一个内存空间已经释放了, 而另一个程序还在使用这个内存, 就会导致内存泄漏. 也野指针.
指针也是一个变量, 存储的是另一个变量的内存地址.
指针变量的值是一个地址
*/
/// 指针的使用与内存布局
var intVariables int = 100
//打印指针
fmt.Printf("intVariables的值=%d,地址=%v\n",intVariables,&intVariables)
//定义指针, 取内存地址
var pointerVariables *int = &intVariables
fmt.Printf("pointerVariables的值=%v,地址=%v\n",pointerVariables,&pointerVariables)
/*
变量名 变量值 内存地址
intVariables 100 0xc00001c090
pointerVariables 0xc00001c090 0xc00000e030
*/
}
5-2 指针内存布局分析、指针作用、指针运算与多级指针
package main
import "fmt"
func main() {
/*指针的作用:
一、节省内存空间, 提高程序执行效率
二、间接访问与修改变量的值
*/
/// 指针的使用与内存布局
var intVariables int = 100
//定义指针, 取内存地址
var pointerVariables *int = &intVariables
//获取到指针指向的值, 并修改
fmt.Println(intVariables)
*pointerVariables = 200
fmt.Println(intVariables)
// 通过指针获取变量的值
var variables2 int = 123 // 定义变量
var pointerVariables2 *int = &variables2 //获取指针(内存地址)
fmt.Println(*pointerVariables2,variables2)
/*
值类型: 整形, 浮点型, bool, array, string. 值类型一般在栈中分配内存
引用类型: 指针, slice, map, chan, interface. 引用类型一般在堆中分配内存
*/
//指针可以指向另一个指针, 不推荐这样做
//当我们定义一个指针的时候, 并没有分配任何变量, 那么它的值默认为nil
var pointer1 *int
*pointer1 = 100 //报错: 无效的内存地址
}
5-3 数组概述、内存布局分析、数组的使用
package main
import "fmt"
func main() {
/*数组
数组的下标, 从0开始, 数组的长度通过len()获取
数组是长度固定的数据类型, 元素的类型都是一样的, 数组在内存当中是连续的存储空间, 可以有效的提升CPU的执行效率
变量或数组在定义时, 如果没有赋值, 会有默认值(零值)
整形: 默认值0
字符串: 默认值""
bool: 默认值false
*/
// 数组的定义方式一:
var arrayVariables [10]int
arrayVariables[0] = 100
arrayVariables[3] = 200
fmt.Println(arrayVariables)//[100 0 0 200 0 0 0 0 0 0]
// 数组在内存当中是连续的存储空间
var arrayVariables2 [5] int = [5]int{1,2,3,4,5}
var length = len(arrayVariables2)
for i:=0; i<length; i++ {
fmt.Printf("arrayVariables2[%d]=%d,地址=%p\n",i,arrayVariables2[i],&arrayVariables2[i])
}
/// 数组定义方式2
// 注意:在go语言中 [10]int 与 [5]int 被认为是不同的类型
// 数组的长度是数组的组成部分
arrayVariables3 := [...]int{1,2,3,4,5}
fmt.Println(arrayVariables3,len(arrayVariables3))
//数组的定义方式3: 指定下标的值
arrayVariables4 := [...]int{100:200,300:500}
fmt.Println(len(arrayVariables4))// 301
}
5-4 数组的使用、数组作为函数参数意义、二维数组与多维数组
package main
import "fmt"
func main() {
//在go语言中数组是值类型
arrayVariables5 := [10]int{1,2,3}
fmt.Println(arrayVariables5)
changeArr(arrayVariables5)
fmt.Println(arrayVariables5)
// 通过指针的方式来改变数组的元素
changeArrByPointer(&arrayVariables5)
fmt.Println(arrayVariables5)
//二维数组
var arrayVariables8 [4][2]int //数组中有4个数组元素, 每个数组元素包含两个int元素
fmt.Println(arrayVariables8) //[[0 0] [0 0] [0 0] [0 0]]
//使用字面量进行初始化二维数组
arrayVariables9 := [4][2]int{{1,2},{3,4},{5,6},{7,8}}
fmt.Println(arrayVariables9)// [[1 2] [3 4] [5 6] [7 8]]
//指定下标初始化二维数组
arrayVariables10 := [4][2]int{1:{10,9}}
fmt.Println(arrayVariables10)// [[0 0] [10 9] [0 0] [0 0]]
//多维数组
var arrayVariables11 [4][3][2]int = [4][3][2]int{}
fmt.Println(arrayVariables11)
}
// 成功的改变了数组的值✅
func changeArrByPointer(arr *[10]int) {
(*arr)[0] = 100
}
// 这样并不会改变数组的内容❌
func changeArr(arr [10]int) {
arr[0] = 100
}
5-5 切片概述、切片使用、切片与数组内存布局分析
/*切片是什么?
一种数据结构, 便于使用与管理我们的数据集合.
按需自动增长, 动态数组(通过append来完成)
底层指向的是数组
内存当中是连续的存储空间, 可以有效的提升CPU的执行效率
引用类型
*/
package main
import "fmt"
func main() {
/*切片 slice
切片又被称为动态数组, 切片长度是可以改变的
切片由三个部分组成: 指向底层数组的指针, 切片的元素个数(长度len), 切片的容量cap.
切片是引用类型
遍历方式和数组类似, 都是通过for, for range
*/
//切片的定义
var sliceVariables []int
//定义一个数组
arrayVariables := [...]int{12,21,23,55,98,2}
for i := 0; i < len(arrayVariables); i++ {
fmt.Printf("arrayVariables[%d]=%d, 地址=%p\n",i,arrayVariables[i],&arrayVariables[i])
}
//通过切片去引用数组
sliceVariables = arrayVariables[:]
for i := 0; i < len(sliceVariables); i++ {
fmt.Printf("sliceVariables[%d]=%d, 地址=%p\n",i,sliceVariables[i],&sliceVariables[i])
}// 切片元素中元素的地址值 与 数组中元素的地址值一直
//切片指向的是底层数组
var sliceVariables2 []int
sliceVariables2 = arrayVariables[1:3] //引用数组的第2-4个元素
fmt.Println(sliceVariables2)// [21 23]
for i := 0; i < len(sliceVariables2); i++ {
fmt.Printf("sliceVariables2[%d]=%d, 地址=%p\n",i,sliceVariables2[i],&sliceVariables2[i])
}
//通过切片改变对应的值
sliceVariables2[0] = 100 //切片对应的是底层数组, 改变的是底层数组的元素的值
fmt.Println(sliceVariables)// [12 100 23 55 98 2]
fmt.Println(sliceVariables2)// [100 23]
}
5-6 切片作为函数参数
package main
import "fmt"
func main() {
//切片的第二种定义方式: make
var sliceVariable3 []int = make([]int,5,6)//长度是5, 容量为6
fmt.Printf("sliceVariables3的长度=%d,容量=%d,切片指向的底层数组的地址=%p,切片自己的地址=%p\n",len(sliceVariable3),cap(sliceVariable3),sliceVariable3,&sliceVariable3)//sliceVariables3的长度=5,容量=6,切片指向的底层数组的地址=0xc00010c060,切片自己的地址=0xc000120048
//切片的追加: append
sliceVariable3 = append(sliceVariable3, 7)
fmt.Printf("第一次追加sliceVariables3的长度=%d,容量=%d,切片指向的底层数组的地址=%p,切片自己的地址=%p\n",len(sliceVariable3),cap(sliceVariable3),sliceVariable3,&sliceVariable3)
sliceVariable3 = append(sliceVariable3, 8)
fmt.Printf("第二次追加sliceVariables3的长度=%d,容量=%d,切片指向的底层数组的地址=%p,切片自己的地址=%p\n",len(sliceVariable3),cap(sliceVariable3),sliceVariable3,&sliceVariable3)
//切片的copy
sliceVariable4 := []int{1,2,3,4,5}
sliceVariables5 := make([]int, 10)
fmt.Println(sliceVariable4)// [1 2 3 4 5]
fmt.Println(sliceVariables5)// [0 0 0 0 0 0 0 0 0 0]
copy(sliceVariables5,sliceVariable4)//将sliceVariables4 的内容拷贝到 sliceVariables5当中
fmt.Println(sliceVariable4)// [1 2 3 4 5]
fmt.Println(sliceVariables5)// [1 2 3 4 5 0 0 0 0 0]
sliceVariables6 := make([]int,1)
copy(sliceVariables6,sliceVariable4)
fmt.Println(sliceVariable4)// [1 2 3 4 5]
fmt.Println(sliceVariables6)// [1]
/// 切片是引用类型
fmt.Println(sliceVariables5)// [1 2 3 4 5 0 0 0 0 0]
changeSlice(sliceVariables5)
fmt.Println(sliceVariables5)// [100 2 3 4 5 0 0 0 0 0]
}
func changeSlice(slice []int) {
slice[0] = 100
}
5-7 map概述、定义与使用方式、map内存布局分析
package main
import "fmt"
func main() {
/*
map 也被称为集合, 这种数据类型是一个无序的键值对
键值对: map[key] = value
通过散列表函数对我们的map进行存储
*/
// 1.map的声明
var mapVariabels1 map[string]string
// 2.map的定义--make
mapVariabels1 = make(map[string]string)
mapVariabels1["Monday"] = "周一"
mapVariabels1["Tuesday"] = "周二"
fmt.Println(mapVariabels1)//map[Mondy:周一 Tuesday:周二]
// map这里的key是唯一的, 不能重复
mapVariabels1["Monday"] = "大周一"
fmt.Println(mapVariabels1)// map[Monday:大周一 Tuesday:周二]
// map len
fmt.Println(len(mapVariabels1)) //2
// 声明的时候同时赋值
var mapVariable2 = map[string]int{
"Monday":1,"Tuesday":2,
}
fmt.Println(mapVariable2)// map[Monday:1 Tuesday:2]
// 通过 := 和make 来声明map
mapVariables3 := make(map[string]int)
mapVariables3["Monday"] = 100
fmt.Println(mapVariables3) //map[Monday:100]
}
5-8 map的使用方式、遍历、有序输出
package main
import (
"fmt"
"sort"
)
func main() {
//切片和函数不可以作为map的key
//mapVariables4 := map[[]string]int{}
mapVariables5 := map[int][]string{}
var sliceString []string
sliceString = make([]string,3)
sliceString[0] = "拿小铁"
sliceString[1] = "golang"
sliceString[2] = "go瑜伽课"
mapVariables5[0] = sliceString
fmt.Println(mapVariables5)//map[0:[拿小铁 golang go瑜伽课]]
//⚠️只声明没有make, 报错
//var mapVariables6 map[string]string
//mapVariables6["hello"] = "拿小铁"
//fmt.Println(mapVariables6)// panic: assignment to entry in nil map
//map是一个引用类型
fmt.Printf("mapVariables3['Monday']在函数调用之前的值:%v\n",mapVariables3["Monday"])
changeMap(mapVariables3)
fmt.Printf("mapVariables3['Monday']在函数调用之后的值:%v\n",mapVariables3["Monday"])
// 如何来对map的key进行顺序输出, for range
var sortKeys []string //sp1.定义切片
for key,_ := range mapVariables3{
sortKeys = append(sortKeys, key)//sp2.把map的key保存到切片中
}
fmt.Printf("排序前sortkeys的值=%v\n",sortKeys)
// sort: 将一个切片进行排序, 按照增长的方式
sort.Strings(sortKeys)//sp3.按照ASSIC码值进行排序
fmt.Printf("排序后sortkeys的值=%v\n",sortKeys)
for i := 0; i < len(sortKeys); i++ {
fmt.Printf("mapVairables3[%s]=%d\n",sortKeys[i],mapVariables3[sortKeys[i]])
}
}
func changeMap(mapVariables map[string]int) {
mapVariables["Monday"] = 888
}
5-9 结构体作为map的值、map切片
package main
import (
"fmt"
"sort"
)
func main() {
//结构体和C语言的类似, 需求: 定一个结构体, 作为map的值
type course struct {
courseName string //课程名称
courseTime float32 //课程时长 单位分钟
courseTeacher string //课程讲师
}
//定义结构体变量
course1 := course{
"go瑜伽课",300.3,"拿小铁",
}
course2 := course{
courseTeacher: "胡歌",
courseTime: 100.2,
courseName: "如何变帅?",
}
courser := make(map[string]course)
courser["go"] = course1
courser["美容"] = course2
fmt.Println(courser)//map[go:{go瑜伽课 300.3 拿小铁} 美容:{如何变帅? 100.2 胡歌}]
/// 判断一个key在我们的map当中是否存在?
//方式一:
value,ok := courser["go"]
if ok {
fmt.Println(value)
}else {
fmt.Println("no value")
}
//方式二:
if value,ok := courser["go"];ok{
fmt.Println(value)
}
///map切片(map+切片): 切片也可以把map作为值来使用, 长度是可以动态改变的
//interface{} 可以把它当做万能类型
var mapVariables6 []map[string]interface{} // 可以把整型、浮点型、字符串等赋值给interface{}
mapVariables6 = make([]map[string]interface{},2)//定义切片, 含两个map
mapVariables6[0] = make(map[string]interface{},2)//定义map1
mapVariables6[0]["name"] = "拿小铁"
mapVariables6[0]["age"] = 18
mapVariables6[1] = make(map[string]interface{},2)//定义map2
mapVariables6[1]["name"] = "胡歌"
mapVariables6[1]["age"] = 16
fmt.Println(mapVariables6)
}