go相关命令
//对go源码进行编译,生成.exe文件
go build go文件名
//直接运行go源码(生成.exe文件执行后,又删除.exe文件)
go run go文件名
go中的package和import
/*package:用来声明这个文件是属于哪个包的*/
package main
/*import:导入包,对包中的函数进行引用和复用*/
import "fmt"
//main函数是程序的入口
func main() {
fmt.Println("hello world") //自带换行
fmt.Printf("占位符格式化打印,传递过来的值,这个值用 %s 占位", "运维开发")
fmt.Print("hello world") //不换行
fmt.Println("hello", "world") //类似于python的拼接
fmtEnv := fmt.Sprintf("你好%s,大小%d", "hello", 5) //将拼接后的字符串赋值给变量
fmt.Println(fmtEnv) //打印变量
fmt.Println(3.1415926) //打印整数
}
什么是包:可以理解为go源码的集合,也是一种比较高级的代码复用方案
- 我们可以把一些复用的代码或功能函数封装在一起,然后形成一个包,可以被其他包或go文件进行引用
什么是main包:main是一个特殊的包,一个可执行的程序有且只有一个main包
什么是main函数:main函数是整个程序的入口,如果一个程序没有main函数是无法正常执行程序的
花括号:表示一个代码块,花括号内的代码属于同一个块内,也可以说属于同一个域内
函数使用func进行声明
人机交互
package main
import "fmt"
func main() {
//方式1:fmt.Scanln(指针)
var name string
var age byte
var salary float32
var isPass bool
//当程序执行到fmt.Scanln(&name),程序会停止在这里,等待用户输入,并回车
fmt.Printf("请输入姓名:")
fmt.Scanln(&name)
fmt.Printf("请输入年龄:")
fmt.Scanln(&age)
fmt.Printf("请输入薪水:")
fmt.Scanln(&salary)
fmt.Printf("是否通过考试(true or flase):")
fmt.Scanln(&isPass)
fmt.Printf("名字:%v\n年龄:%v\n薪水:%v\n是否通过考试:%v", name, age, salary, isPass)
}
package main
import "fmt"
func main() {
//方式2:fmt.Scanf()
var name string
var age byte
var salary float32
var isPass bool
fmt.Printf("输入姓名,年龄,薪水,是否通过考试:")
fmt.Scanf("%s %d %f %t", &name, &age, &salary, &isPass)
fmt.Printf("名字:%v\n年龄:%v\n薪水:%v\n是否通过考试:%v", name, age, salary, isPass)
}
变量
package main
import "fmt"
//函数外赋值
var address string = "172.25.254.80"
add := "gaga" //会报错
func main() {
//var 变量名 变量类型
var name1 string //先定义
name1 = "huazi" //再赋值
fmt.Println(name1)
//一次性定义多个相同类型的变量
var name2, user string = "hua", "huazi"
fmt.Println(name2, user)
//一次性定义多个不同类型的变量
var (
age1 int = 18
name3 string = "huazi"
)
fmt.Println(name3, age1)
//当省略类型后,go会自动判断,自动转换为强数据类型
var (
name4 = "huazi"
age2 = 18
)
fmt.Println(name4, age2)
//当省略了var,只能再函数内使用,要用:= ,称之为语法糖
//相当于var变成了:
name5 := "huazi" //我们成为语法糖写法
age3 := 18
fmt.Println(name5, age3)
//交换变量的值
name6 := "hua"
name7 := "huazi"
name6, name7 = name7, name6
fmt.Println(name6, name7)
//reflect.TypeOf(变量名) 打印变量的类型或者使用%T来表示类型
fmt.Println(reflect.TypeOf(name6))
}
在函数外定义并赋值的变量是包级别的变量,这种变量可以不对其进行使用。但是在函数中定义的变量必须得到使用
函数外不能使用语法糖来定义变量
常量
package main
import (
"fmt"
)
const ADDRESS string = "172.25.254.80"
func main() {
fmt.Println(ADDRESS)
//一次性定义多个相同类型的变量
const name1, name2 string = "hua", "huazi"
fmt.Println(name1, name2)
//一次性定义多个不同类型的常量
const (
name3 string = "yao"
age int = 18
)
fmt.Println(name3, age)
//常量可以省略类型,但是不能省略const
const (
name4 = "ze"
age1 = 18
)
fmt.Println(name4, age1)
age1 = 20 //会报错,因为常量不能修改
const (
num1 int = 1
num2
num3
str1 string = "hua"
str2
str3
)
fmt.Println(num1, num2, num3) //1 1 1
fmt.Println(str1, str2, str3) //hua hua hua
//枚举(const+iota),iota的基数是0
const (
e1 int = (1 + iota) * 10
e2
e3
)
fmt.Println(e1, e2, e3) //10 20 30
}
常量在函数内外都是可以不进行使用的
%v
在 Go 语言中,%v
是一个通用的格式化动词
(format verb
),用于 fmt
包中的格式化函数,如 fmt.Printf
、fmt.Sprintf
和 fmt.Fprintf
等。%v
可以用于不同类型的值,并会根据值的类型输出其默认的字符串表示。
以下是一些示例,展示了 %v
如何处理不同类型的数据:
整数:
fmt.Printf("%v\n", 42) // 输出: 42
浮点数:
fmt.Printf("%v\n", 3.14) // 输出: 3.14
字符串:
fmt.Printf("%v\n", "hello") // 输出: hello
布尔值:
fmt.Printf("%v\n", true) // 输出: true
指针:
var p *int = &age fmt.Printf("%v\n", p) // 输出: 0xc0000180b0(或类似的内存地址)
结构体:
type Person struct { Name string Age int } p := Person{Name: "Alice", Age: 30} fmt.Printf("%v\n", p) // 输出: {Alice 30}
切片:
slice := []int{1, 2, 3} fmt.Printf("%v\n", slice) // 输出: [1 2 3]
映射:
m := map[string]int{"a": 1, "b": 2} fmt.Printf("%v\n", m) // 输出: map[a:1 b:2]
接口:
var i interface{} = "world" fmt.Printf("%v\n", i) // 输出: world
使用 %v
时,Go
会根据值的实际类型
选择适当的字符串
表示,因此它是一个方便且通用的格式化动词
。然而,对于某些特定类型,你可能希望使用更具体的格式化动词以获得更精细的控制。例如,对于整数可以使用 %d
,对于浮点数可以使用 %f
,对于字符串可以使用 %s
等等。
作用域
- 在
go
中使用{}
来定义作用域的范围
- 使用原则:子语句块中可以使用父语句块中的标识符,父不能使用子的
package main
import (
"fmt"
)
func main() {
num1 := 1
{
num2 := 2
fmt.Println(num1)
fmt.Println(num2)
}
//fmt.Println(num2) //报错
}
package main
import (
"fmt"
)
func printAll() {
fmt.Println("今天是个好日子")
fmt.Println("hello world")
num1 := 1
fmt.Println(num1)
}
func main() {
printAll() //调用函数
//fmt.Println(num1) 报错
}
运算符
- 算术运算
package main
import "fmt"
//定义参数
func Calculate(num1 int, num2 int) {
fmt.Printf("num1+num2=%d\n", num1+num2)
fmt.Printf("num1-num2=%d\n", num1-num2)
fmt.Printf("num1*num2=%d\n", num1*num2)
fmt.Printf("num1/num2=%d\n", num1/num2) //取整
fmt.Printf("num1取余num2=%d\n", num1%num2)
}
func main() {
num1 := 1
num2 := 2
Calculate(num1, num2) //值传递
}
- 字符串拼接
package main
import "fmt"
func stringCalculate(str1 string, str2 string) {
fmt.Printf("str1+str2=%s\n", str1+str2)
word := fmt.Sprintf("str1+str2=%s", str1+str2)
fmt.Println(word)
}
func main() {
str1 := "hello"
str2 := "world"
stringCalculate(str1, str2)
}
//自增自减
p1 := 8
p1++
fmt.Println("p1 =", p1)
p1--
fmt.Println("p1 =", p1)
package main
import "fmt"
func main() {
a := 3
b := 2
fmt.Println(a / b) //取整 1
fmt.Println(float64(a) / float64(b)) // 1.5小数运算
}
浮点数不能和整数一起运算
float32类型的和float64类型的不能运算
int类型和float类型
package main
import (
"fmt"
"math"
"reflect"
)
func main() {
// 数值类型:int int8 int16 int32 int64 uint
// int: 正负数 uint:不带符号的数字 // int
defaultIntType := 1
fmt.Println("默认的数值类型是:", reflect.TypeOf(defaultIntType))
// int和操作系统是有关系的
// 64位的,int64 32位的 int32
var int64Num int64 = 1
fmt.Println("int64Num的数值类型是:", reflect.TypeOf(int64Num))
var uintNum uint = 1
fmt.Println("uintNum的数值类型是:", reflect.TypeOf(uintNum))
fmt.Println("int的取值范围:", math.MinInt, math.MaxInt)
fmt.Println("uint的取值范围:", uint(math.MaxUint))
fmt.Println(18446744073709551615 > 9223372036854775807)
// float float32和float64
var floatNum1 float64 = 3.14
var floatNum2 float32 = 3.15
// floatSum := floatNum1 + floatNum2
fmt.Println(floatNum1, floatNum2)
}
关系与逻辑运算符
package main
import (
"fmt"
"reflect"
)
func main() {
// 大于, 小于
fmt.Println(727585 > 727588) // false
fmt.Println(727585 < 727588) // true
// 是否相等, =和==的区别
fmt.Println("a" == "b") // false
fmt.Println(3.14 == 3.14)
s1 := "dukuan"
s2 := "dotbalo"
// xxx := s1 == s2
fmt.Println("s1和s2相等: ", s1 == s2)
fmt.Println("s1和s2不相等: ", s1 != s2)
fmt.Println("s1 > s2:", s1 > s2)
fmt.Println("s2 > s1:", s2 > s1) //字符串是可以比较大小的, ASCII
// 逻辑与和逻辑或 && ||
n1 := 1
n2 := 1
n3 := 2
// 与: 所有的表达式都为true,最终的结果就为true
fmt.Println(n1 == n2 && n2 == n3) // true false => false
// 或:任意一个为true,最终结果就为true
fmt.Println(n1 == n2 || reflect.TypeOf(n3).Kind() == reflect.String)
}
if-else
package main
import (
"fmt"
)
func printWeather(weather string) {
if weather == "sunny" {
fmt.Println("今天是晴天")
} else if weather == "rain" {
fmt.Println("今天是雨天")
} else {
fmt.Println("今天气候不明")
}
}
func main() {
weather := "rain"
printWeather(weather)
}
if的特殊用法
package main
import "fmt"
func backBool() bool {
return true
}
func main() {
a := false
if res := backBool(); res != a { //在判断前,先进行运算
fmt.Println("true")
} else {
fmt.Println("false")
}
}
switch
package main
import (
"fmt"
)
func printScore(score int) {
sc := score / 10
switch sc {
case 10, 9:
fmt.Println("优秀")
break
case 8:
fmt.Println("良好")
case 7:
fmt.Println("中等")
case 6:
fmt.Println("及格")
default:
fmt.Println("不及格")
}
}
func main() {
printScore(66)
}
break默认可以省略不写
for
package main
import (
"fmt"
"time"
)
func main() {
count := 0 //记录偶数的个数
for num := 0; num < 100; num++ {
if num%2 == 0 {
fmt.Println("发现一个偶数:", num)
count++
}
time.Sleep(time.Second) //停顿1秒,second是秒的意思
}
fmt.Printf("一共有%d个偶数", count)
}
for
实现死循环
package main
import (
"fmt"
"time"
)
func main() {
for {
timeNow := time.Now() //获取当前时间
// 2006-01-02 15:04:05 go语言的诞生时间
fmt.Println("当前时间是:", timeNow.Format("2006-01-02 15:04:05"))
time.Sleep(time.Second * 3) //停顿3秒
}
}
break与continue
package main
import (
"fmt"
)
func main() {
temp := 20
count := 0
for {
if count == 20 {
fmt.Println("恭喜你找到了", temp)
break
} else {
count++
}
}
}
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 100; i++ {
if i == 88 {
fmt.Println("我找到了88")
break
}
fmt.Println("现在的数值是:", i)
}
time.Sleep(time.Second * 3)
for i := 0; i < 50; i++ {
if i == 33 {
fmt.Println("我找到了33")
continue
}
fmt.Println("现在的数值是:", i)
}
}
数组
package main
import "fmt"
func main() {
//数组定义:一组具有相同类型并且长度固定的一个数据集合
//var 数组名 = [长度]类型{value1,value2}
var name = [3]string{"小明", "华子", "戈兄"}
fmt.Println(name) //[小明 华子 戈兄]
fmt.Println(name[0])
//修改
name[0] = "小华"
fmt.Println(name)
//for循环访问
for i := 0; i < 3; i++ {
fmt.Println(name[i])
}
//求数组的长度
length := len(name)
fmt.Println(length)
//range使用
for i, v := range name {
fmt.Println(i, v)
}
//自动推断长度
array3 := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(len(array3))
}
切片
package main
import "fmt"
func main() {
// 切片:切片的长度是不固定的,可以扩容
// var 切片名称 = []切片类型{value1, value2...}
var slice1 []int //先定义切片
fmt.Println(slice1) // []
// 切片默认的两个属性:1.切片的长度,表示切片中有多少个元素 2.切片的容量,表示切片中最大可以放多少个元素
fmt.Println("切片的默认长度是:", len(slice1)) // 0
fmt.Println("切片的默认容量:", cap(slice1)) // 0
// 添加元素
slice1 = append(slice1, 1, 2, 3, 4)
fmt.Println(slice1) // [1 2 3 4]
fmt.Println("长度:", len(slice1)) // 4
fmt.Println("容量:", cap(slice1)) // 4
// 修改数据
slice1[0] = 88
fmt.Println(slice1) // [88 2 3 4]
//定义切片并初始化
var slice2 = []string{"切片1", "切片2", "切片3"}
fmt.Println(slice2) // [0 0 0 0 0]
fmt.Println("切片的长度:", len(slice2)) // 3
fmt.Println("切片的容量:", cap(slice2)) // 3
slice2 = append(slice2, "切片4", "切片5")
fmt.Println(slice2) // [0 0 0 0 0]
//第二种声明方式,指定长度
slice3 := make([]int, 5, 10) //5个长度,10个容量
fmt.Println(slice3) // [0 0 0 0 0]
fmt.Println("切片的默认长度:", len(slice3)) // 5
fmt.Println("切片的默认容量:", cap(slice3)) // 10
slice3 = append(slice3, 1, 2, 3)
fmt.Println(slice3) // [0 0 0 0 0 1 2 3]
slice4 := make([]string, 3, 5)
fmt.Println(slice4) // 默认初始化一个空格
slice4 = append(slice4, "hua")
fmt.Println(slice4)
// for循环遍历
for i, v := range slice2 {
fmt.Println(i, v)
}
}
数组和切片在声明时的区别:数组有长度,切片没有长度
切片截取和元素删除
package main
import "fmt"
func main() {
var s = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println("最初的数据是:", s)
fmt.Println("前4位数据:", s[0:4]) //包头不包尾
fmt.Println("从第5个数据开始:", s[4:])
//删除一个元素:go语言中删除切片中的元素是通过截取的方式实现的
s = s[1:] // 删除第一个元素
fmt.Println(s)
s = s[:len(s)-1] // 删除最后一个元素
fmt.Println(s)
s = append(s[:2], s[3:]...) // 删除第3个元素
fmt.Println(s)
}
深拷贝与浅拷贝
package main
import "fmt"
func main() {
str1 := "huazi"
str2 := str1 //深拷贝
fmt.Println(str1, str2) //huazi huazi
str2 = "hua"
fmt.Println(str1, str2) //huazi hua
//定义一个切片
slice1 := []int{1, 2, 3, 4, 5, 6}
slice2 := slice1 //浅拷贝
fmt.Println(slice1, slice2) //[1 2 3 4 5 6] [1 2 3 4 5 6]
slice2[0] = 88
fmt.Println(slice1, slice2) //[88 2 3 4 5 6] [88 2 3 4 5 6]
}
深拷贝:复制一个变量时,会创建一个全新的变量,并且将原始数据复制给新变量,新变量在内存中会是一个新的地址,并且两个变量修改时不会影响其他变量
浅拷贝:复制一个变量时,也会创建一个新的变量,但是两个变量共享底层的数据,也就是新旧变量会指向同一个数据的内存地址,实际上算是引用同一个数据,也就是意味着任意一个变量发生变更,其他变量也会被修改
值类型:复制变量的时候是深拷贝,值类型包括(int,float,string,struct,array,bool)
引用类型:复制变量的时候是浅拷贝,引用类型包括:(slice,map,channel,interface)
package main
import (
"fmt"
"unsafe"
)
func main() {
str1 := "huazi"
str2 := str1 //深拷贝
fmt.Println(str1, str2)
str2 = "hua"
fmt.Println(str1, str2)
//定义一个切片
slice1 := []int{1, 2, 3, 4, 5, 6}
//如何对切片进行深拷贝呢
slice2 := make([]int, len(slice1), cap(slice1))
copy(slice2, slice1) //深拷贝
slice2[0] = 88 //对slice2的修改不影响slice1
fmt.Println(slice1, slice2)
//打印内存地址
fmt.Println("slice1的内存地址", unsafe.Pointer(&slice1)) //1
fmt.Println("slice1[0]的内存地址", unsafe.Pointer(&slice1[0])) //2
fmt.Println("slice2的内存地址", unsafe.Pointer(&slice2)) //3
fmt.Println("slice2[0]的内存地址", unsafe.Pointer(&slice2[0])) //4
}
map映射
package main
import "fmt"
func main() {
// var map名 = map[type]type{key1:value1, key2:value2......}
teacherAge := make(map[string]int)
fmt.Println("map的初始化值", teacherAge) //map[]
teacherAge["huazi"] = 18
teacherAge["xiaoming"] = 20
teacherAge["huazi"] = 22
fmt.Println("赋值后的值", teacherAge) //map[huazi:22 xiaoming:20]
//在声明变量的时候直接进行赋值操作
teacherAge1 := map[string]int{
"d1": 2,
"d2": 3,
}
fmt.Println(teacherAge1) //map[d1:2 d2:3]
//先用var声明
var teacherAddress map[string]string
//再用make声明内存空间
teacherAddress = make(map[string]string)
teacherAddress["huazi"] = "洋县"
teacherAddress["hua"] = "汉川"
fmt.Println(teacherAddress) //map[hua:汉川 huazi:洋县]
//访问
fmt.Println("huazi老师的地址", teacherAddress["huazi"])
searchName := "hua"
fmt.Printf("%s老师的地址:%s\n", searchName, teacherAddress[searchName])
//for range
for k, v := range teacherAddress {
fmt.Printf("%s老师的地址:%s\n", k, v)
}
fmt.Println("取一个不存在的值:", teacherAddress["hhhh"]) //空字符串
//map中的ok判断
value, ok := teacherAddress["hua"] //如果hua这个键不存在返回false,存在返回true
if ok {
fmt.Printf("能查看到hua的地址:%s\n", value)
} else {
fmt.Printf("不能查到hua的地址\n")
}
// 修改值
teacherAddress["hua"] = "北京"
fmt.Println("修改后的值:", teacherAddress["hua"])
// 删除值
delete(teacherAddress, "huazi")
fmt.Println("删除后的map:", teacherAddress)
}
切片中嵌套map对象
var slice = []map[type]type
package main
import "fmt"
func main() {
order1 := map[string]int{
"宫保鸡丁": 99,
"糖酸鱼": 88,
}
order2 := map[string]int{
"回锅肉": 66,
"鱼香肉丝": 89,
}
order3 := map[string]int{
"奶茶": 18,
"可乐": 3,
}
var menu []map[string]int //map[string]int是一个整体
menu = append(menu, order1, order2, order3)
fmt.Println(menu) //[map[宫保鸡丁:99 糖酸鱼:88] map[回锅肉:66 鱼香肉丝:89] map[可乐:3 奶茶:18]]
for i, v := range menu { //i是下标,v是map对象
fmt.Printf("第%d天的菜单是:\n", i+1)
for name, price := range v {
fmt.Printf("\t菜名:%s,价格:%d\n", name, price)
}
}
}
map对象嵌套map对象
var map11 = map[type]map[type]type
package main
import (
"fmt"
)
func main() {
//map对象嵌套map对象
// map[string]map[string]string
address1 := map[string]string{
"汉中市": "洋县",
"宝鸡市": "凤县",
}
address2 := map[string]string{
"武功市": "好县",
"花市": "中等县",
}
var country map[string]map[string]string
country = make(map[string]map[string]string)
country["陕西省"] = address1
country["浙江省"] = address2
fmt.Println(country)
for province, city_map := range country {
fmt.Printf("%s:\n", province)
for city, county := range city_map {
fmt.Printf("\t%s %s\n", city, county)
}
}
}
类型转换
package main
import (
"fmt"
"math"
)
// 参数类型为float64,返回值类型为float64
func area(r float64) float64 {
s := math.Pi * r * r
return s
}
func main() {
r := 5 //此时r默认为int类型
s := area(float64(r)) //需要使用float64()把int转为float64
fmt.Println("面积是:", s)
}
package main
import (
"fmt"
"reflect"
"strconv"
)
func main() {
i1 := 10
//将int类型转为string类型
str1 := strconv.Itoa(i1)
fmt.Println(str1, reflect.TypeOf(str1))
str2 := "1001"
//将string类型转为int类型
i2, _ := strconv.Atoi(str2) //通过_来规避掉函数的返回值
fmt.Println(i2, reflect.TypeOf(i2))
// str3 := "100"
str3 := "100a"
i3, err := strconv.Atoi(str3) //如果err有值,则转换失败,err为nil则转换成功
if err != nil {
// 转换失败
fmt.Println(err)
fmt.Println("转换失败,当前值不能转换为数值")
} else {
//转换成功
fmt.Println(err)
fmt.Println("转换成功", i3)
}
}
package main
import (
"fmt"
"strconv"
)
func main() {
//将0 1 f t float true Float True FLOAT TRUE等布尔型字符串转为bool类型
str1 := "t"
bool1, _ := strconv.ParseBool(str1)
fmt.Println("转换后的布尔值:", bool1)
}
字符串方法
package main
import (
"fmt"
"strings"
)
func main() {
// 字符串的定义
// `` ""
// "\t \n" ``
s := "\t\txxx\n"
fmt.Println("双引号字符串:", s)
s2 := `\t\txxx\n`
fmt.Println("反引号字符串:", s2)
s3 := `
我是杜宽
我主要教授的课程是: 云原生系列,k8s,go,python
`
fmt.Println("多行字符串:", s3)
// 字符长度的计算
s4 := "dukuan"
s5 := "杜宽" //中文占用3个字符
s4Length := len(s4)
s5Length := len(s5)
fmt.Println(s4Length, s5Length)
// 字符串的截取 , 一般
// s6 := s4[2]
fmt.Println("前两位:", s4[:2])
s7 := "dukuan"
// 大小写转换, 一般
fmt.Println("转成大写:", strings.ToUpper(s7))
fmt.Println("首字母大写", strings.Title(s7))
s8 := "dUKUan"
fmt.Println("转成小写:", strings.ToLower(s8))
// 字符串是否包含某个元素 ,还行
fmt.Println("查看字符串是否包含uk这个元素:", strings.Contains(s7, "uk"))
fmt.Println("查看字符串是否包含任意一个字符:", strings.ContainsAny(s7, "uw"))
// 忽略大小写进行比较
fmt.Println("忽略大小写比较:", strings.EqualFold(s7, s8))
// 判断字符串中某个元素有多个个
s9 := "dukuan and dot is me, my age is 18"
fmt.Println("u在字符串中出现了:", strings.Count(s9, "u"))
// 字符串拆分 //很多
s9Split := strings.Split(s9, ",")
fmt.Println("使用逗号拆分字符串:", s9Split)
fmt.Println("拆分后的切片的第一个数据:", s9Split[0])
s9SplitAfter := strings.SplitAfter(s9, ",")
fmt.Println("使用逗号拆分字符串,并且保留逗号:", s9SplitAfter)
// 字符串拼接 //很多
slice1 := []string{"dot", "balo", "du", "kuan"}
fmt.Println("拼接字符串:", strings.Join(slice1, " "))
// 是否是某个元素开头的 ,还行
s10 := "我是一个中国人,我非常热爱中国"
fmt.Println("字符串是以 ‘我’ 开头的:", strings.HasPrefix(s10, "我"))
// 此处的视频出现错误,应该是HasSuffix
fmt.Println("字符串是以 爱 结尾的:", strings.HasSuffix(s10, "爱"))
// 重复字符串
fmt.Println("打印五个我:", strings.Repeat("我", 5))
// 字符串替换 ,还行
s11 := "dsad3afd3rq3adawdwarq3a"
fmt.Println("把3替换为dukuan:", strings.ReplaceAll(s11, "3", "dukuan"))
fmt.Println("把3替换为杜宽:", strings.Replace(s11, "3", "杜宽", 1)) // 0替换所有
// trim 字符串修剪 ,很多
s12 := " dukuan ,"
fmt.Println("去掉字符串的前后空格:", strings.Trim(s12, ","))
}
指针和内存地址
package main
import "fmt"
func updateString(s string) {
s = "这是一个新值"
}
func updateStringWithPointer(s *string) {
*s = "这是一个新值"
}
func main() {
var s string
s = "这是一个字符串"
fmt.Println("变量s的内存地址是:", &s)
// sp: 指针
// 内存地址: 通过&符号进行取值
sp := &s
fmt.Println("指针sp:", sp)
var sp2 *string
fmt.Println("指针sp2:", sp2) // 指针未进行赋值的时候为nil
sp2 = &s
fmt.Println("指针sp2:", sp2)
// 通过指针获取内存地址的值
fmt.Println("指针对应内存地址的值:", *sp2)
updateString(s)
fmt.Println("修改后的s:", s)
updateStringWithPointer(&s)
fmt.Println("真正修改后的s:", s)
}
函数
函数的定义
package main
import "fmt"
/*
函数的定义:
func 函数名(参数1 类型, 参数2 类型) (返回值1 类型,返回值2 类型) {代码块}
func 函数名(参数2,参数2 类型) (返回值1,返回值2) {代码块}
func 函数名(参数2,参数2 类型) 类型 {代码块}
func 函数名(参数2,参数2 类型) {代码块}
func 函数名(参数1 类型, 参数2 类型) (类型, 类型) {代码块}
*/
func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
func main() {
fmt.Println(max(1, 3))
}
package main
import "fmt"
func intSum(a, b int) (sum int) {
sum = a + b
return
}
func main() {
fmt.Println(intSum(1, 3))
}
package main
import "fmt"
func sortInt(a, b int) (min, max int) {
if a < b {
min = a
max = b
} else {
min = b
max = a
}
return
}
func main() {
min, max := sortInt(99, 88)
fmt.Println(min, max)
}
不定长参数
package main
import (
"fmt"
"strings"
)
// 接收不定长度参数的函数
func randomLength(s ...string) string {
//接受到的多个参数会封装成一个切片
fmt.Println(s) //[hua zi]
m := strings.Join(s, "") //huazi
return m
}
func main() {
m := randomLength("hua", "zi")
fmt.Println(m)
}
递归函数
package main
import "fmt"
func factorial(n int) int {
if n == 1 {
return 1
}
return n * factorial(n-1)
}
func main() {
res := factorial(5)
fmt.Println(res) //5
}
递归函数使用场景:
- 多级目录文件遍历
- 多层数据结构数据查找
- 网页菜单自动生成
- 路由管理
- 数值计算:斐波那契数列、阶乘、幂等
匿名函数
package main
import "fmt"
func main() {
//定义匿名函数并调用
func() {
fmt.Println("这个匿名函数")
}()
}
异常处理
go语言中有一种错误类型叫error
package main
import (
"fmt"
"io/ioutil"
)
func main() {
//案例:打开一个文件,文件存不存在呢
f, err := ioutil.ReadFile("./file.txt") //会在同级目录中寻找file.txt这个文件
if err != nil {
//说明文件不存在,出现报错
fmt.Println("打开文件失败:", err.Error())
} else {
fmt.Println("文件打开成功")
fmt.Println(string(f))
}
}
go语言中通过自定义err来实现异常处理
//自定义err的两种方式
package main
import (
"errors"
"fmt"
)
func main() {
//自定义err的两种方式
err1 := errors.New("这是一个自定义错误")
//fmt.Println(err1.Error())
fmt.Println(err1) //要不要Error()都可以
err2 := fmt.Errorf("这是一个自定义错误:%s,它是使用fmt生成的", "fmt定义的错误")
//fmt.Println(err2.Error())
fmt.Println(err2)
}
//案例:除法
package main
import (
"errors"
"fmt"
)
// 定义一个函数,来实现除法
func division(num1, num2 float64) (res float64, err error) {
fmt.Println("需要计算的数字是:", num1, num2)
if num2 == 0 {
return 0, errors.New("输入的分母不能为0")
} else {
res = num1 / num2
return res, nil
}
}
func main() {
res, err := division(2, 0)
if err != nil {
fmt.Println("计算错误", err.Error())
} else {
fmt.Println("计算结果", res)
}
}
但是以上定义的err,如果出现了错误,程序也不会停止,会一直运行
那么如何在出现错误时终止程序呢
package main
import (
"errors"
"fmt"
"time"
)
func connectDatabase(address string, port int) (string, error) {
//如果address和port为空
if address == "" || port == 0 {
return "", errors.New("无法链接数据库")
} else {
return "数据库链接成功", nil
}
}
func main() {
// panic:可以在异常的时候让程序终止执行,退出程序。或者是程序所强依赖的基础组件不可用
// 此时程序已经无法继续正常工作,此时可以使用panic抛出异常,并且把程序退出
s, err := connectDatabase("", 0)
for {
time.Sleep(time.Second * 5)
if err != nil {
//说明无法链接数据库
fmt.Println(err)
panic(err) //此时就会退出程序
} else {
//说明链接成功
fmt.Println(s)
}
}
}
那么终止程序后,我们需不需要做一些处理呢,比如终止程序后,我们需要发一些消息给前端
这时就可以使用到defer:是go语言中的一种延迟调用机制,defer里面的内容可以在函数return之前或者是程序panic之前执行
defer是可以有多个的,采用先进后出的机制;一般用于资源回收和数据返回,defer也可以用于异常时的恢复
defer后跟函数调用
package main
import (
"errors"
"fmt"
)
// 实现数据库的链接
func connectDatabase(address string, port int) (string, error) {
// 如果address和port为空
if address == "" || port == 0 {
return "", errors.New("无法链接数据库")
} else {
return "数据库链接成功", nil
}
}
// 返回数据给前端
func returnDataToFrontend(msg string) {
fmt.Println("返回给前端的数据是:", msg)
}
func main() {
msg := "返回给前端的数据"
defer returnDataToFrontend(msg) // defer不会真正的执行
_, err := connectDatabase("", 0)
if err != nil {
fmt.Println(err)
// defer会在这一步执行
panic(err)
}
}
以上是终止程序后,将终止信息返回给管理员,那么我们又该如果将捕获到的错误信息进行相应的处理,从而让程序继续执行,不中断
package main
import "fmt"
func printSliceData(s []string) {
//使用recover进行异常捕获
defer func() {
fmt.Println("程序执行失败,捕获异常")
if err := recover(); err != nil {
// recover是用来捕获panic的报错的,尝试恢复,防止程序异常退出
fmt.Println("捕获一个错误:", err)
}
}()
fmt.Println("切片的内容:", s)
fmt.Println("切片的第三个值:", s[2]) //这里由于下标超出,程序会自己生成一个panic,从而终止程序
}
func main() {
s := []string{"a", "b"}
printSliceData(s)
}
Go自定义类型
结构体
go语言中的结构体是一种自定义数据类型,可以将不同类型的数据组合在一起形成一个单独的实体
package main
import "fmt"
/*
type 结构体名 struct {
属性1 类型
属性2 类型
}
var 变量名 结构体名{
属性1: 值1
属性2: 值2
}
*/
func main() {
//定义一个名为People的结构体
type People struct {
name string
age int
address string
}
//先定义再赋值
var p1 People
p1.name = "华子"
p1.age = 18
p1.address = "陕西省汉中市"
fmt.Println(p1) //{华子 18 陕西省汉中市}
//直接声明
p2 := People{
name: "huazi",
age: 19,
address: "陕西省汉中市",
}
fmt.Println(p2) //{huazi 19 陕西省汉中市}
//第三种方式
var p3 People = People{"hua", 20, "陕西省汉中市"}
fmt.Println(p3) //{hua 20 陕西省汉中市}
//使用
fmt.Printf("名字:%s 年龄:%d\n", p2.name, p2.age)
//修改值
p2.name = "华"
fmt.Printf("名字:%s 年龄:%d\n", p2.name, p2.age)
//赋值
p4 := p2
fmt.Println(p4)
//判断自定义变量是否相等
fmt.Println(p4 == p2)
}
属性是这样定义的,那么方法该如何定义呢
package main
import "fmt"
//定义一个名为People的结构体
type People struct {
name string
age int
address string
}
//定义结构体的方式函数
func (p *People) getInfo() string {
return fmt.Sprintf("当前用户名:%s 年龄:%d 地址:%s\n",
p.name, p.age, p.address)
}
func (p *People) Eat(food string) {
fmt.Printf("%s今天吃了%s\n", p.name, food)
}
func main() {
var p1 People = People{"华子", 18, "陕西省汉中市"}
//调用方法:
//变量.方法名()
fmt.Println(p1.getInfo())
p1.Eat("鸡公煲")
}
go变量大小写特性
getInfo小写开头的变量名,是私有元素,只能在本包内使用
Eat大写开头的变量名,是公开元素,可以被外部的包调用
指针类型和值类型的方法
- 指针类型的方法
package main
import "fmt"
//定义一个名为People的结构体
type People struct {
name string
age int
address string
}
/*
自定义类型添加方法使用值类型和指针类型的区别
p:接受者,方法的参数
People:接受者的类型:分为值类型和指针类型
*/
//定义结构体的方式函数(指针类型)
func (p *People) getInfo() string {
p.age = 99
return fmt.Sprintf("当前用户名:%s 年龄:%d 地址:%s\n",
p.name, p.age, p.address)
}
func main() {
var p1 People = People{"华子", 18, "陕西省汉中市"}
//调用方法:
//变量.方法名()
fmt.Println(p1.getInfo()) //当前用户名:华子 年龄:99 地址:陕西省汉中市
fmt.Println(p1) //{华子 99 陕西省汉中市}
}
- 值类型的方法
package main
import "fmt"
//定义一个名为People的结构体
type People struct {
name string
age int
address string
}
/*
自定义类型添加方法使用值类型和指针类型的区别
p:接受者,方法的参数
People:接受者的类型:分为值类型和指针类型
*/
//定义结构体的方式函数(值类型)
func (p People) getInfo() string {
p.age = 99
return fmt.Sprintf("当前用户名:%s 年龄:%d 地址:%s\n",
p.name, p.age, p.address)
}
func main() {
var p1 People = People{"华子", 18, "陕西省汉中市"}
//调用方法:
//变量.方法名()
fmt.Println(p1.getInfo()) //当前用户名:华子 年龄:99 地址:陕西省汉中市
fmt.Println(p1) //{华子 18 陕西省汉中市}
}
嵌套
- 第一种嵌套方式
package main
import "fmt"
type Phone struct {
mode string
price int
}
type People struct {
name string
age int
address string
mobile Phone //People结构体中嵌套Phone
}
func (p *People) getInfo() string {
return fmt.Sprintf("姓名:%s 手机:%s", p.name, p.mobile.mode)
}
func main() {
var p1 People
p1.name = "华子"
p1.age = 18
p1.address = "陕西省汉中市"
p1.mobile.mode = "小米17"
p1.mobile.price = 1999
fmt.Println(p1.getInfo())
}
- 第二种嵌套方式
package main
import "fmt"
type Phone struct {
mode string
price int
}
type People struct {
name string
age int
address string
}
type info struct { //再写一个info结构体,将People和Phone合起来
Phone
People
}
func (p *info) getInfo() string {
return fmt.Sprintf("姓名:%s 手机:%s", p.name, p.mode)
}
func main() {
var p1 info
p1.name = "华子"
p1.age = 20
p1.address = "陕西省汉中市"
p1.mode = "小米"
p1.price = 1999
fmt.Println(p1.getInfo())
}
如果多个结构体上有相同的变量名,就不能直接进行赋值了
//如果多个结构体上有相同的变量名,就不能直接进行赋值了
package main
import "fmt"
type Phone struct {
name string
price int
}
type People struct {
name string
age int
address string
}
type info struct { //再写一个info结构体,将People和Phone合起来
Phone
People
}
func (p *info) getInfo() string {
return fmt.Sprintf("姓名:%s 手机:%s", p.People.name, p.Phone.name)
}
func main() {
var p1 info
p1.People.name = "华子"
p1.age = 20
p1.address = "陕西省汉中市"
p1.Phone.name = "小米"
p1.price = 1999
fmt.Println(p1.getInfo())
}
interface接口类型
go语言的接口是一种类型,定义了一组方法的集合,但是接口又不需要去实现他们,这些方法可以被不同的类型实现,进而就是这个类型实现了这个接口。
在go语言中,接口是一个重要的概念,接口被广泛应用于许多标准库和框架中。通过接口,可以使不同的类型之间实现相同的行为,从而达到代码复用和扩展的目的,并且可以实现不同类型之间的无缝切换。
- 不用接口之前
//对每个数据库的操作都要写不同的操作语句
package main
import "fmt"
//创建一个数据库的结构体,用来存放数据库的连接信息
type DBConfig struct {
User string
Password string
Host string
Port int
Database string
}
func main() {
//声明一个mysql数据库实例
db := DBConfig{"root", "password", "127.0.0.1", 3306, "interface_test"}
fmt.Println("mysql数据库的配置:", db)
//插入一条数据
fmt.Println("在mysql中插入一条数据:db.row('insert xxx to user').Rows()")
//换成pg
//生成pg数据库的连接实例
dbPg := DBConfig{"root", "password", "127.0.0.1", 3306, "interface_test"}
fmt.Println("pg数据库的配置:", dbPg)
//在pg中插入一条数据
fmt.Println("在pg中插入一条数据:db.QueryRow('insert xxx to user')")
}
- 使用接口之后
package main
import "fmt"
/*
type 接口名 interface {
方法名(参数) 返回值
方法名(参数) 返回值
方法名(参数) 返回值
}
*/
type DBCommon interface {
Insert(string) error
Update(string) error
Delete(string) error
}
//定义结构体存储数据库的信息
type DBConfig struct {
User string
Password string
Host string
Port int
Database string
}
//定义一个类型去实现这个接口
type MySQL struct {
config DBConfig
charSet string
}
func (m MySQL) Insert(data string) error {
fmt.Println("插入数据到MySQL:", data)
return nil
}
func (m MySQL) Update(data string) error {
fmt.Println("更新数据到MySQL:", data)
return nil
}
func (m MySQL) Delete(data string) error {
fmt.Println("删除数据到MySQL:", data)
return nil
}
func main() {
db := DBConfig{"root", "password", "127.0.0.1", 3306, "interface_test"}
//声明一个接口的变量
var dbCommonInterface DBCommon
var m MySQL
m.config = db
m.charSet = "utf-8"
dbCommonInterface = m //将结构体变量赋值给接口变量
dbCommonInterface.Insert("insert") //调用接口中的定义的函数
dbCommonInterface.Update("update")
dbCommonInterface.Delete("delete")
}
package main
import "fmt"
/*
type 接口名 interface {
方法名(参数) 返回值
方法名(参数) 返回值
方法名(参数) 返回值
}
*/
type DBCommon interface {
Insert(string) error
Update(string) error
Delete(string) error
}
//定义结构体存储数据库的信息
type DBConfig struct {
User string
Password string
Host string
Port int
Database string
}
//定义一个类型去实现这个接口
type MySQL struct {
config DBConfig
charSet string
}
func (m MySQL) Insert(data string) error {
fmt.Println("插入数据到MySQL:", data)
return nil
}
func (m MySQL) Update(data string) error {
fmt.Println("更新数据到MySQL:", data)
return nil
}
func (m MySQL) Delete(data string) error {
fmt.Println("删除数据到MySQL:", data)
return nil
}
type PostgreSQL struct {
config DBConfig
charSet string
}
func (m PostgreSQL) Insert(data string) error {
fmt.Println("插入数据到PostgreSQL:", data)
return nil
}
func (m PostgreSQL) Update(data string) error {
fmt.Println("更新数据到PostgreSQL:", data)
return nil
}
func (m PostgreSQL) Delete(data string) error {
fmt.Println("删除数据到PostgreSQL:", data)
return nil
}
func main() {
db := DBConfig{"root", "password", "127.0.0.1", 3306, "interface_test"}
//声明一个接口的变量
var dbCommonInterface DBCommon
//var m MySQL
var m PostgreSQL
m.config = db
m.charSet = "utf-8"
dbCommonInterface = m
dbCommonInterface.Insert("insert")
dbCommonInterface.Update("update")
dbCommonInterface.Delete("delete")
}
package main
import "fmt"
/*
type 接口名 interface {
方法名(参数) 返回值
方法名(参数) 返回值
方法名(参数) 返回值
}
*/
type DBCommon interface {
Insert(string) error
Update(string) error
Delete(string) error
}
//定义结构体存储数据库的信息
type DBConfig struct {
User string
Password string
Host string
Port int
Database string
}
//定义一个类型去实现这个接口
type MySQL struct {
config DBConfig
charSet string
}
func (m MySQL) Insert(data string) error {
fmt.Println("插入数据到MySQL:", data)
return nil
}
func (m MySQL) Update(data string) error {
fmt.Println("更新数据到MySQL:", data)
return nil
}
func (m MySQL) Delete(data string) error {
fmt.Println("删除数据到MySQL:", data)
return nil
}
type PostgreSQL struct {
config DBConfig
charSet string
}
func (m PostgreSQL) Insert(data string) error {
fmt.Println("插入数据到PostgreSQL:", data)
return nil
}
func (m PostgreSQL) Update(data string) error {
fmt.Println("更新数据到PostgreSQL:", data)
return nil
}
func (m PostgreSQL) Delete(data string) error {
fmt.Println("删除数据到PostgreSQL:", data)
return nil
}
type sqlServer struct {
config DBConfig
charSet string
}
func (m sqlServer) Insert(data string) error {
fmt.Println("插入数据到sqlServer:", data)
return nil
}
func (m sqlServer) Update(data string) error {
fmt.Println("更新数据到sqlServer:", data)
return nil
}
func (m sqlServer) Delete(data string) error {
fmt.Println("删除数据到sqlServer:", data)
return nil
}
func main() {
dbType := "sqlserver"
//声明一个接口的变量
var dbCommonInterface DBCommon
if dbType == "mysql" {
var m MySQL
dbCommonInterface = m
} else if dbType == "PostgreSQL" {
var pg PostgreSQL
dbCommonInterface = pg
} else {
var sqlS sqlServer
dbCommonInterface = sqlS
}
dbCommonInterface.Insert("insert")
dbCommonInterface.Update("update")
dbCommonInterface.Delete("delete")
}
空接口
- 方法1
package main
import "fmt"
/*
空接口:空接口不会定义任何的方法
空接口是可以接受任何类型的参数值
*/
//定义一个空接口
type EmptyInterface interface{}
func main() {
var ei EmptyInterface //ei是一个空接口类型的变量,可以接受任何类型的赋值
s1 := "这是一个字符串"
i1 := 72578
ei = s1
fmt.Println(ei)
ei = i1
fmt.Println(ei)
}
- 方法2
package main
import "fmt"
/*
空接口:空接口不会定义任何的方法
空接口是可以接受任何类型的参数值
*/
func main() {
s1 := "这是一个字符串"
i1 := 18
//定义空接口方法2
var ei1 interface{}
ei1 = s1
fmt.Println(ei1)
ei1 = i1
fmt.Println(ei1)
}
package main
import "fmt"
/*
空接口:空接口不会定义任何的方法
空接口是可以接受任何类型的参数值
*/
func main() {
//map记录联系方式:手机号(int) 座机号(010-123-789)string类型
contacts := make(map[string]interface{}) //在map中定义空接口可以接受不同类型的值
contacts["huazi"] = 13289446832
contacts["dot"] = "010-123-789"
fmt.Println(contacts)
}
接口类型断言和类型判断
package main
import "fmt"
/*
类型断言:大致知道了接口可能是某种类型,然后使用t,ok := 变量.(类型)
类型判断:switch t := 变量.(type){}
*/
func dealData(data interface{}) {
t, ok := data.(string) //如果data是string类型,则返回true给ok,t接受data的值,否则t接受空值
if ok {
fmt.Println("当前类型为string,变量的值是:", t)
} else {
fmt.Println("data不是字符串")
fmt.Println("当前t的值:", t)
}
}
func main() {
s := "这是一个字符串"
dealData(s)
i := 123456
dealData(i)
}
package main
import "fmt"
func getType(i interface{}) {
switch t := i.(type) {
case int:
fmt.Println("当前值为int类型:", t)
case string:
fmt.Println("当前值为string类型:", t)
case bool:
fmt.Println("当前值为bool类型:", t)
default:
fmt.Println("当前类型不在处理范围")
}
}
func main() {
s := "这个一个字符串"
getType(s)
i := 123
getType(i)
var emtry interface{}
getType(emtry)
}
接口嵌套和继承
package main
import "fmt"
type Generic_i interface {
Login()
LogOut()
}
type VIP_i interface {
Consult()
}
type User_i interface {
Generic_i
}
type VIPUser_i interface {
Generic_i
VIP_i
}
type Userordinary_s struct {
Name string
}
func (u Userordinary_s) Login() {
fmt.Println("用户已登录:", u.Name)
}
func (u Userordinary_s) LogOut() {
fmt.Println("用户已退出:", u.Name)
}
type Uservip_s struct {
Name string
}
func (u Uservip_s) Login() {
fmt.Println("vip用户已登录:", u.Name)
}
func (u Uservip_s) LogOut() {
fmt.Println("vip用户已退出:", u.Name)
}
func (u Uservip_s) Consult() {
fmt.Println("vip用户可以进行咨询:", u.Name)
}
func main() {
var userinterface User_i
u := Userordinary_s{
Name: "华子",
}
userinterface = u
userinterface.Login()
userinterface.LogOut()
var vipuserinterface VIPUser_i
vipu := Uservip_s{
Name: "戈兄",
}
vipuserinterface = vipu
vipuserinterface.Login()
vipuserinterface.LogOut()
vipuserinterface.Consult()
}
go相关命令
//对go源码进行编译,生成.exe文件
go build go文件名
//直接运行go源码(生成.exe文件执行后,又删除.exe文件)
go run go文件名
go中的package和import
/package:用来声明这个文件是属于哪个包的/
package main
/import:导入包,对包中的函数进行引用和复用/
import “fmt”
//main函数是程序的入口
func main() {
fmt.Println(“hello world”) //自带换行
fmt.Printf(“占位符格式化打印,传递过来的值,这个值用 %s 占位”, “运维开发”)
fmt.Print(“hello world”) //不换行
fmt.Println(“hello”, “world”) //类似于python的拼接
fmtEnv := fmt.Sprintf(“你好%s,大小%d”, “hello”, 5) //将拼接后的字符串赋值给变量
fmt.Println(fmtEnv) //打印变量
fmt.Println(3.1415926) //打印整数
}
什么是包:可以理解为go源码的集合,也是一种比较高级的代码复用方案
我们可以把一些复用的代码或功能函数封装在一起,然后形成一个包,可以被其他包或go文件进行引用
什么是main包:main是一个特殊的包,一个可执行的程序有且只有一个main包
什么是main函数:main函数是整个程序的入口,如果一个程序没有main函数是无法正常执行程序的
花括号:表示一个代码块,花括号内的代码属于同一个块内,也可以说属于同一个域内
函数使用func进行声明
人机交互
package main
import “fmt”
func main() {
//方式1:fmt.Scanln(指针)
var name string
var age byte
var salary float32
var isPass bool
//当程序执行到fmt.Scanln(&name),程序会停止在这里,等待用户输入,并回车
fmt.Printf(“请输入姓名:”)
fmt.Scanln(&name)
fmt.Printf("请输入年龄:")
fmt.Scanln(&age)
fmt.Printf("请输入薪水:")
fmt.Scanln(&salary)
fmt.Printf("是否通过考试(true or flase):")
fmt.Scanln(&isPass)
fmt.Printf("名字:%v\n年龄:%v\n薪水:%v\n是否通过考试:%v", name, age, salary, isPass)
}
package main
import “fmt”
func main() {
//方式2:fmt.Scanf()
var name string
var age byte
var salary float32
var isPass bool
fmt.Printf(“输入姓名,年龄,薪水,是否通过考试:”)
fmt.Scanf(“%s %d %f %t”, &name, &age, &salary, &isPass)
fmt.Printf(“名字:%v\n年龄:%v\n薪水:%v\n是否通过考试:%v”, name, age, salary, isPass)
}
变量
在这里插入图片描述
package main
import “fmt”
//函数外赋值
var address string = “172.25.254.80”
add := “gaga” //会报错
func main() {
//var 变量名 变量类型
var name1 string //先定义
name1 = “huazi” //再赋值
fmt.Println(name1)
//一次性定义多个相同类型的变量
var name2, user string = "hua", "huazi"
fmt.Println(name2, user)
//一次性定义多个不同类型的变量
var (
age1 int = 18
name3 string = "huazi"
)
fmt.Println(name3, age1)
//当省略类型后,go会自动判断,自动转换为强数据类型
var (
name4 = "huazi"
age2 = 18
)
fmt.Println(name4, age2)
//当省略了var,只能再函数内使用,要用:= ,称之为语法糖
//相当于var变成了:
name5 := "huazi" //我们成为语法糖写法
age3 := 18
fmt.Println(name5, age3)
//交换变量的值
name6 := "hua"
name7 := "huazi"
name6, name7 = name7, name6
fmt.Println(name6, name7)
//reflect.TypeOf(变量名) 打印变量的类型或者使用%T来表示类型
fmt.Println(reflect.TypeOf(name6))
}
在函数外定义并赋值的变量是包级别的变量,这种变量可以不对其进行使用。但是在函数中定义的变量必须得到使用
函数外不能使用语法糖来定义变量
常量
package main
import (
“fmt”
)
const ADDRESS string = “172.25.254.80”
func main() {
fmt.Println(ADDRESS)
//一次性定义多个相同类型的变量
const name1, name2 string = "hua", "huazi"
fmt.Println(name1, name2)
//一次性定义多个不同类型的常量
const (
name3 string = "yao"
age int = 18
)
fmt.Println(name3, age)
//常量可以省略类型,但是不能省略const
const (
name4 = "ze"
age1 = 18
)
fmt.Println(name4, age1)
age1 = 20 //会报错,因为常量不能修改
const (
num1 int = 1
num2
num3
str1 string = "hua"
str2
str3
)
fmt.Println(num1, num2, num3) //1 1 1
fmt.Println(str1, str2, str3) //hua hua hua
//枚举(const+iota),iota的基数是0
const (
e1 int = (1 + iota) * 10
e2
e3
)
fmt.Println(e1, e2, e3) //10 20 30
}
常量在函数内外都是可以不进行使用的
%v
在 Go 语言中,%v 是一个通用的格式化动词(format verb),用于 fmt 包中的格式化函数,如 fmt.Printf、fmt.Sprintf 和 fmt.Fprintf 等。%v 可以用于不同类型的值,并会根据值的类型输出其默认的字符串表示。
以下是一些示例,展示了 %v 如何处理不同类型的数据:
整数:
fmt.Printf(“%v\n”, 42) // 输出: 42
浮点数:
fmt.Printf(“%v\n”, 3.14) // 输出: 3.14
字符串:
fmt.Printf(“%v\n”, “hello”) // 输出: hello
布尔值:
fmt.Printf(“%v\n”, true) // 输出: true
指针:
var p *int = &age
fmt.Printf(“%v\n”, p) // 输出: 0xc0000180b0(或类似的内存地址)
结构体:
type Person struct {
Name string
Age int
}
p := Person{Name: “Alice”, Age: 30}
fmt.Printf(“%v\n”, p) // 输出: {Alice 30}
切片:
slice := []int{1, 2, 3}
fmt.Printf(“%v\n”, slice) // 输出: [1 2 3]
映射:
m := map[string]int{“a”: 1, “b”: 2}
fmt.Printf(“%v\n”, m) // 输出: map[a:1 b:2]
接口:
var i interface{} = “world”
fmt.Printf(“%v\n”, i) // 输出: world
使用 %v 时,Go会根据值的实际类型选择适当的字符串表示,因此它是一个方便且通用的格式化动词。然而,对于某些特定类型,你可能希望使用更具体的格式化动词以获得更精细的控制。例如,对于整数可以使用 %d,对于浮点数可以使用 %f,对于字符串可以使用 %s 等等。
作用域
在go中使用{}来定义作用域的范围
使用原则:子语句块中可以使用父语句块中的标识符,父不能使用子的
package main
import (
“fmt”
)
func main() {
num1 := 1
{
num2 := 2
fmt.Println(num1)
fmt.Println(num2)
}
//fmt.Println(num2) //报错
}
package main
import (
“fmt”
)
func printAll() {
fmt.Println(“今天是个好日子”)
fmt.Println(“hello world”)
num1 := 1
fmt.Println(num1)
}
func main() {
printAll() //调用函数
//fmt.Println(num1) 报错
}
运算符
算术运算
package main
import “fmt”
//定义参数
func Calculate(num1 int, num2 int) {
fmt.Printf(“num1+num2=%d\n”, num1+num2)
fmt.Printf(“num1-num2=%d\n”, num1-num2)
fmt.Printf("num1num2=%d\n", num1num2)
fmt.Printf(“num1/num2=%d\n”, num1/num2) //取整
fmt.Printf(“num1取余num2=%d\n”, num1%num2)
}
func main() {
num1 := 1
num2 := 2
Calculate(num1, num2) //值传递
}
字符串拼接
package main
import “fmt”
func stringCalculate(str1 string, str2 string) {
fmt.Printf(“str1+str2=%s\n”, str1+str2)
word := fmt.Sprintf(“str1+str2=%s”, str1+str2)
fmt.Println(word)
}
func main() {
str1 := “hello”
str2 := “world”
stringCalculate(str1, str2)
}
//自增自减
p1 := 8
p1++
fmt.Println(“p1 =”, p1)
p1–
fmt.Println(“p1 =”, p1)
package main
import “fmt”
func main() {
a := 3
b := 2
fmt.Println(a / b) //取整 1
fmt.Println(float64(a) / float64(b)) // 1.5小数运算
}
浮点数不能和整数一起运算
float32类型的和float64类型的不能运算
int类型和float类型
package main
import (
“fmt”
“math”
“reflect”
)
func main() {
// 数值类型:int int8 int16 int32 int64 uint
// int: 正负数 uint:不带符号的数字 // int
defaultIntType := 1
fmt.Println(“默认的数值类型是:”, reflect.TypeOf(defaultIntType))
// int和操作系统是有关系的
// 64位的,int64 32位的 int32
var int64Num int64 = 1
fmt.Println(“int64Num的数值类型是:”, reflect.TypeOf(int64Num))
var uintNum uint = 1
fmt.Println(“uintNum的数值类型是:”, reflect.TypeOf(uintNum))
fmt.Println(“int的取值范围:”, math.MinInt, math.MaxInt)
fmt.Println(“uint的取值范围:”, uint(math.MaxUint))
fmt.Println(18446744073709551615 > 9223372036854775807)
// float float32和float64
var floatNum1 float64 = 3.14
var floatNum2 float32 = 3.15
// floatSum := floatNum1 + floatNum2
fmt.Println(floatNum1, floatNum2)
}
关系与逻辑运算符
package main
import (
“fmt”
“reflect”
)
func main() {
// 大于, 小于
fmt.Println(727585 > 727588) // false
fmt.Println(727585 < 727588) // true
// 是否相等, =和==的区别
fmt.Println(“a” == “b”) // false
fmt.Println(3.14 == 3.14)
s1 := "dukuan"
s2 := "dotbalo"
// xxx := s1 == s2
fmt.Println("s1和s2相等: ", s1 == s2)
fmt.Println("s1和s2不相等: ", s1 != s2)
fmt.Println("s1 > s2:", s1 > s2)
fmt.Println("s2 > s1:", s2 > s1) //字符串是可以比较大小的, ASCII
// 逻辑与和逻辑或 && ||
n1 := 1
n2 := 1
n3 := 2
// 与: 所有的表达式都为true,最终的结果就为true
fmt.Println(n1 == n2 && n2 == n3) // true false => false
// 或:任意一个为true,最终结果就为true
fmt.Println(n1 == n2 || reflect.TypeOf(n3).Kind() == reflect.String)
}
if-else
package main
import (
“fmt”
)
func printWeather(weather string) {
if weather == “sunny” {
fmt.Println(“今天是晴天”)
} else if weather == “rain” {
fmt.Println(“今天是雨天”)
} else {
fmt.Println(“今天气候不明”)
}
}
func main() {
weather := “rain”
printWeather(weather)
}
if的特殊用法
package main
import “fmt”
func backBool() bool {
return true
}
func main() {
a := false
if res := backBool(); res != a { //在判断前,先进行运算
fmt.Println(“true”)
} else {
fmt.Println(“false”)
}
}
switch
package main
import (
“fmt”
)
func printScore(score int) {
sc := score / 10
switch sc {
case 10, 9:
fmt.Println(“优秀”)
break
case 8:
fmt.Println(“良好”)
case 7:
fmt.Println(“中等”)
case 6:
fmt.Println(“及格”)
default:
fmt.Println(“不及格”)
}
}
func main() {
printScore(66)
}
break默认可以省略不写
for
package main
import (
“fmt”
“time”
)
func main() {
count := 0 //记录偶数的个数
for num := 0; num < 100; num++ {
if num%2 == 0 {
fmt.Println(“发现一个偶数:”, num)
count++
}
time.Sleep(time.Second) //停顿1秒,second是秒的意思
}
fmt.Printf(“一共有%d个偶数”, count)
}
for实现死循环
package main
import (
“fmt”
“time”
)
func main() {
for {
timeNow := time.Now() //获取当前时间
// 2006-01-02 15:04:05 go语言的诞生时间
fmt.Println(“当前时间是:”, timeNow.Format(“2006-01-02 15:04:05”))
time.Sleep(time.Second * 3) //停顿3秒
}
}
break与continue
package main
import (
“fmt”
)
func main() {
temp := 20
count := 0
for {
if count == 20 {
fmt.Println(“恭喜你找到了”, temp)
break
} else {
count++
}
}
}
package main
import (
“fmt”
“time”
)
func main() {
for i := 0; i < 100; i++ {
if i == 88 {
fmt.Println(“我找到了88”)
break
}
fmt.Println(“现在的数值是:”, i)
}
time.Sleep(time.Second * 3)
for i := 0; i < 50; i++ {
if i == 33 {
fmt.Println(“我找到了33”)
continue
}
fmt.Println(“现在的数值是:”, i)
}
}
数组
package main
import “fmt”
func main() {
//数组定义:一组具有相同类型并且长度固定的一个数据集合
//var 数组名 = [长度]类型{value1,value2}
var name = [3]string{“小明”, “华子”, “戈兄”}
fmt.Println(name) //[小明 华子 戈兄]
fmt.Println(name[0])
//修改
name[0] = “小华”
fmt.Println(name)
//for循环访问
for i := 0; i < 3; i++ {
fmt.Println(name[i])
}
//求数组的长度
length := len(name)
fmt.Println(length)
//range使用
for i, v := range name {
fmt.Println(i, v)
}
//自动推断长度
array3 := [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(len(array3))
}
切片
package main
import “fmt”
func main() {
// 切片:切片的长度是不固定的,可以扩容
// var 切片名称 = []切片类型{value1, value2…}
var slice1 []int //先定义切片
fmt.Println(slice1) // []
// 切片默认的两个属性:1.切片的长度,表示切片中有多少个元素 2.切片的容量,表示切片中最大可以放多少个元素
fmt.Println(“切片的默认长度是:”, len(slice1)) // 0
fmt.Println(“切片的默认容量:”, cap(slice1)) // 0
// 添加元素
slice1 = append(slice1, 1, 2, 3, 4)
fmt.Println(slice1) // [1 2 3 4]
fmt.Println("长度:", len(slice1)) // 4
fmt.Println("容量:", cap(slice1)) // 4
// 修改数据
slice1[0] = 88
fmt.Println(slice1) // [88 2 3 4]
//定义切片并初始化
var slice2 = []string{"切片1", "切片2", "切片3"}
fmt.Println(slice2) // [0 0 0 0 0]
fmt.Println("切片的长度:", len(slice2)) // 3
fmt.Println("切片的容量:", cap(slice2)) // 3
slice2 = append(slice2, "切片4", "切片5")
fmt.Println(slice2) // [0 0 0 0 0]
//第二种声明方式,指定长度
slice3 := make([]int, 5, 10) //5个长度,10个容量
fmt.Println(slice3) // [0 0 0 0 0]
fmt.Println("切片的默认长度:", len(slice3)) // 5
fmt.Println("切片的默认容量:", cap(slice3)) // 10
slice3 = append(slice3, 1, 2, 3)
fmt.Println(slice3) // [0 0 0 0 0 1 2 3]
slice4 := make([]string, 3, 5)
fmt.Println(slice4) // 默认初始化一个空格
slice4 = append(slice4, "hua")
fmt.Println(slice4)
// for循环遍历
for i, v := range slice2 {
fmt.Println(i, v)
}
}
数组和切片在声明时的区别:数组有长度,切片没有长度
切片截取和元素删除
package main
import “fmt”
func main() {
var s = []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(“最初的数据是:”, s)
fmt.Println(“前4位数据:”, s[0:4]) //包头不包尾
fmt.Println(“从第5个数据开始:”, s[4:])
//删除一个元素:go语言中删除切片中的元素是通过截取的方式实现的
s = s[1:] // 删除第一个元素
fmt.Println(s)
s = s[:len(s)-1] // 删除最后一个元素
fmt.Println(s)
s = append(s[:2], s[3:]...) // 删除第3个元素
fmt.Println(s)
}
深拷贝与浅拷贝
package main
import “fmt”
func main() {
str1 := “huazi”
str2 := str1 //深拷贝
fmt.Println(str1, str2) //huazi huazi
str2 = “hua”
fmt.Println(str1, str2) //huazi hua
//定义一个切片
slice1 := []int{1, 2, 3, 4, 5, 6}
slice2 := slice1 //浅拷贝
fmt.Println(slice1, slice2) //[1 2 3 4 5 6] [1 2 3 4 5 6]
slice2[0] = 88
fmt.Println(slice1, slice2) //[88 2 3 4 5 6] [88 2 3 4 5 6]
}
深拷贝:复制一个变量时,会创建一个全新的变量,并且将原始数据复制给新变量,新变量在内存中会是一个新的地址,并且两个变量修改时不会影响其他变量
浅拷贝:复制一个变量时,也会创建一个新的变量,但是两个变量共享底层的数据,也就是新旧变量会指向同一个数据的内存地址,实际上算是引用同一个数据,也就是意味着任意一个变量发生变更,其他变量也会被修改
值类型:复制变量的时候是深拷贝,值类型包括(int,float,string,struct,array,bool)
引用类型:复制变量的时候是浅拷贝,引用类型包括:(slice,map,channel,interface)
package main
import (
“fmt”
“unsafe”
)
func main() {
str1 := “huazi”
str2 := str1 //深拷贝
fmt.Println(str1, str2)
str2 = “hua”
fmt.Println(str1, str2)
//定义一个切片
slice1 := []int{1, 2, 3, 4, 5, 6}
//如何对切片进行深拷贝呢
slice2 := make([]int, len(slice1), cap(slice1))
copy(slice2, slice1) //深拷贝
slice2[0] = 88 //对slice2的修改不影响slice1
fmt.Println(slice1, slice2)
//打印内存地址
fmt.Println("slice1的内存地址", unsafe.Pointer(&slice1)) //1
fmt.Println("slice1[0]的内存地址", unsafe.Pointer(&slice1[0])) //2
fmt.Println("slice2的内存地址", unsafe.Pointer(&slice2)) //3
fmt.Println("slice2[0]的内存地址", unsafe.Pointer(&slice2[0])) //4
}
map映射
package main
import “fmt”
func main() {
// var map名 = map[type]type{key1:value1, key2:value2…}
teacherAge := make(map[string]int)
fmt.Println(“map的初始化值”, teacherAge) //map[]
teacherAge[“huazi”] = 18
teacherAge[“xiaoming”] = 20
teacherAge[“huazi”] = 22
fmt.Println(“赋值后的值”, teacherAge) //map[huazi:22 xiaoming:20]
//在声明变量的时候直接进行赋值操作
teacherAge1 := map[string]int{
"d1": 2,
"d2": 3,
}
fmt.Println(teacherAge1) //map[d1:2 d2:3]
//先用var声明
var teacherAddress map[string]string
//再用make声明内存空间
teacherAddress = make(map[string]string)
teacherAddress["huazi"] = "洋县"
teacherAddress["hua"] = "汉川"
fmt.Println(teacherAddress) //map[hua:汉川 huazi:洋县]
//访问
fmt.Println("huazi老师的地址", teacherAddress["huazi"])
searchName := "hua"
fmt.Printf("%s老师的地址:%s\n", searchName, teacherAddress[searchName])
//for range
for k, v := range teacherAddress {
fmt.Printf("%s老师的地址:%s\n", k, v)
}
fmt.Println("取一个不存在的值:", teacherAddress["hhhh"]) //空字符串
//map中的ok判断
value, ok := teacherAddress["hua"] //如果hua这个键不存在返回false,存在返回true
if ok {
fmt.Printf("能查看到hua的地址:%s\n", value)
} else {
fmt.Printf("不能查到hua的地址\n")
}
// 修改值
teacherAddress["hua"] = "北京"
fmt.Println("修改后的值:", teacherAddress["hua"])
// 删除值
delete(teacherAddress, "huazi")
fmt.Println("删除后的map:", teacherAddress)
}
切片中嵌套map对象
var slice = []map[type]type
package main
import “fmt”
func main() {
order1 := map[string]int{
“宫保鸡丁”: 99,
“糖酸鱼”: 88,
}
order2 := map[string]int{
“回锅肉”: 66,
“鱼香肉丝”: 89,
}
order3 := map[string]int{
“奶茶”: 18,
“可乐”: 3,
}
var menu []map[string]int //map[string]int是一个整体
menu = append(menu, order1, order2, order3)
fmt.Println(menu) //[map[宫保鸡丁:99 糖酸鱼:88] map[回锅肉:66 鱼香肉丝:89] map[可乐:3 奶茶:18]]
for i, v := range menu { //i是下标,v是map对象
fmt.Printf(“第%d天的菜单是:\n”, i+1)
for name, price := range v {
fmt.Printf(“\t菜名:%s,价格:%d\n”, name, price)
}
}
}
map对象嵌套map对象
var map11 = map[type]map[type]type
package main
import (
“fmt”
)
func main() {
//map对象嵌套map对象
// map[string]map[string]string
address1 := map[string]string{
“汉中市”: “洋县”,
“宝鸡市”: “凤县”,
}
address2 := map[string]string{
“武功市”: “好县”,
“花市”: “中等县”,
}
var country map[string]map[string]string
country = make(map[string]map[string]string)
country[“陕西省”] = address1
country[“浙江省”] = address2
fmt.Println(country)
for province, city_map := range country {
fmt.Printf(“%s:\n”, province)
for city, county := range city_map {
fmt.Printf(“\t%s %s\n”, city, county)
}
}
}
类型转换
package main
import (
“fmt”
“math”
)
// 参数类型为float64,返回值类型为float64
func area(r float64) float64 {
s := math.Pi * r * r
return s
}
func main() {
r := 5 //此时r默认为int类型
s := area(float64®) //需要使用float64()把int转为float64
fmt.Println(“面积是:”, s)
}
在这里插入图片描述
package main
import (
“fmt”
“reflect”
“strconv”
)
func main() {
i1 := 10
//将int类型转为string类型
str1 := strconv.Itoa(i1)
fmt.Println(str1, reflect.TypeOf(str1))
str2 := "1001"
//将string类型转为int类型
i2, _ := strconv.Atoi(str2) //通过_来规避掉函数的返回值
fmt.Println(i2, reflect.TypeOf(i2))
// str3 := "100"
str3 := "100a"
i3, err := strconv.Atoi(str3) //如果err有值,则转换失败,err为nil则转换成功
if err != nil {
// 转换失败
fmt.Println(err)
fmt.Println("转换失败,当前值不能转换为数值")
} else {
//转换成功
fmt.Println(err)
fmt.Println("转换成功", i3)
}
}
package main
import (
“fmt”
“strconv”
)
func main() {
//将0 1 f t float true Float True FLOAT TRUE等布尔型字符串转为bool类型
str1 := “t”
bool1, _ := strconv.ParseBool(str1)
fmt.Println(“转换后的布尔值:”, bool1)
}
字符串方法
package main
import (
“fmt”
“strings”
)
func main() {
// 字符串的定义
// "" // "\t \n"
s := “\t\txxx\n”
fmt.Println(“双引号字符串:”, s)
s2 := \t\txxx\n
fmt.Println(“反引号字符串:”, s2)
s3 := 我是杜宽 我主要教授的课程是: 云原生系列,k8s,go,python
fmt.Println(“多行字符串:”, s3)
// 字符长度的计算
s4 := “dukuan”
s5 := “杜宽” //中文占用3个字符
s4Length := len(s4)
s5Length := len(s5)
fmt.Println(s4Length, s5Length)
// 字符串的截取 , 一般
// s6 := s4[2]
fmt.Println(“前两位:”, s4[:2])
s7 := “dukuan”
// 大小写转换, 一般
fmt.Println(“转成大写:”, strings.ToUpper(s7))
fmt.Println(“首字母大写”, strings.Title(s7))
s8 := “dUKUan”
fmt.Println(“转成小写:”, strings.ToLower(s8))
// 字符串是否包含某个元素 ,还行
fmt.Println(“查看字符串是否包含uk这个元素:”, strings.Contains(s7, “uk”))
fmt.Println(“查看字符串是否包含任意一个字符:”, strings.ContainsAny(s7, “uw”))
// 忽略大小写进行比较
fmt.Println(“忽略大小写比较:”, strings.EqualFold(s7, s8))
// 判断字符串中某个元素有多个个
s9 := “dukuan and dot is me, my age is 18”
fmt.Println(“u在字符串中出现了:”, strings.Count(s9, “u”))
// 字符串拆分 //很多
s9Split := strings.Split(s9, “,”)
fmt.Println(“使用逗号拆分字符串:”, s9Split)
fmt.Println(“拆分后的切片的第一个数据:”, s9Split[0])
s9SplitAfter := strings.SplitAfter(s9, “,”)
fmt.Println(“使用逗号拆分字符串,并且保留逗号:”, s9SplitAfter)
// 字符串拼接 //很多
slice1 := []string{“dot”, “balo”, “du”, “kuan”}
fmt.Println(“拼接字符串:”, strings.Join(slice1, " “))
// 是否是某个元素开头的 ,还行
s10 := “我是一个中国人,我非常热爱中国”
fmt.Println(“字符串是以 ‘我’ 开头的:”, strings.HasPrefix(s10, “我”))
// 此处的视频出现错误,应该是HasSuffix
fmt.Println(“字符串是以 爱 结尾的:”, strings.HasSuffix(s10, “爱”))
// 重复字符串
fmt.Println(“打印五个我:”, strings.Repeat(“我”, 5))
// 字符串替换 ,还行
s11 := “dsad3afd3rq3adawdwarq3a”
fmt.Println(“把3替换为dukuan:”, strings.ReplaceAll(s11, “3”, “dukuan”))
fmt.Println(“把3替换为杜宽:”, strings.Replace(s11, “3”, “杜宽”, 1)) // 0替换所有
// trim 字符串修剪 ,很多
s12 := " dukuan ,”
fmt.Println(“去掉字符串的前后空格:”, strings.Trim(s12, “,”))
}
指针和内存地址
package main
import “fmt”
func updateString(s string) {
s = “这是一个新值”
}
func updateStringWithPointer(s *string) {
*s = “这是一个新值”
}
func main() {
var s string
s = “这是一个字符串”
fmt.Println(“变量s的内存地址是:”, &s)
// sp: 指针
// 内存地址: 通过&符号进行取值
sp := &s
fmt.Println(“指针sp:”, sp)
var sp2 *string
fmt.Println(“指针sp2:”, sp2) // 指针未进行赋值的时候为nil
sp2 = &s
fmt.Println(“指针sp2:”, sp2)
// 通过指针获取内存地址的值
fmt.Println(“指针对应内存地址的值:”, *sp2)
updateString(s)
fmt.Println("修改后的s:", s)
updateStringWithPointer(&s)
fmt.Println("真正修改后的s:", s)
}
函数
函数的定义
package main
import “fmt”
/*
函数的定义:
func 函数名(参数1 类型, 参数2 类型) (返回值1 类型,返回值2 类型) {代码块}
func 函数名(参数2,参数2 类型) (返回值1,返回值2) {代码块}
func 函数名(参数2,参数2 类型) 类型 {代码块}
func 函数名(参数2,参数2 类型) {代码块}
func 函数名(参数1 类型, 参数2 类型) (类型, 类型) {代码块}
*/
func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
func main() {
fmt.Println(max(1, 3))
}
package main
import “fmt”
func intSum(a, b int) (sum int) {
sum = a + b
return
}
func main() {
fmt.Println(intSum(1, 3))
}
package main
import “fmt”
func sortInt(a, b int) (min, max int) {
if a < b {
min = a
max = b
} else {
min = b
max = a
}
return
}
func main() {
min, max := sortInt(99, 88)
fmt.Println(min, max)
}
不定长参数
package main
import (
“fmt”
“strings”
)
// 接收不定长度参数的函数
func randomLength(s …string) string {
//接受到的多个参数会封装成一个切片
fmt.Println(s) //[hua zi]
m := strings.Join(s, “”) //huazi
return m
}
func main() {
m := randomLength(“hua”, “zi”)
fmt.Println(m)
}
递归函数
package main
import “fmt”
func factorial(n int) int {
if n == 1 {
return 1
}
return n * factorial(n-1)
}
func main() {
res := factorial(5)
fmt.Println(res) //5
}
递归函数使用场景:
多级目录文件遍历
多层数据结构数据查找
网页菜单自动生成
路由管理
数值计算:斐波那契数列、阶乘、幂等
匿名函数
package main
import “fmt”
func main() {
//定义匿名函数并调用
func() {
fmt.Println(“这个匿名函数”)
}()
}
异常处理
go语言中有一种错误类型叫error
package main
import (
“fmt”
“io/ioutil”
)
func main() {
//案例:打开一个文件,文件存不存在呢
f, err := ioutil.ReadFile(“./file.txt”) //会在同级目录中寻找file.txt这个文件
if err != nil {
//说明文件不存在,出现报错
fmt.Println(“打开文件失败:”, err.Error())
} else {
fmt.Println(“文件打开成功”)
fmt.Println(string(f))
}
}
go语言中通过自定义err来实现异常处理
//自定义err的两种方式
package main
import (
“errors”
“fmt”
)
func main() {
//自定义err的两种方式
err1 := errors.New(“这是一个自定义错误”)
//fmt.Println(err1.Error())
fmt.Println(err1) //要不要Error()都可以
err2 := fmt.Errorf(“这是一个自定义错误:%s,它是使用fmt生成的”, “fmt定义的错误”)
//fmt.Println(err2.Error())
fmt.Println(err2)
}
//案例:除法
package main
import (
“errors”
“fmt”
)
// 定义一个函数,来实现除法
func division(num1, num2 float64) (res float64, err error) {
fmt.Println(“需要计算的数字是:”, num1, num2)
if num2 == 0 {
return 0, errors.New(“输入的分母不能为0”)
} else {
res = num1 / num2
return res, nil
}
}
func main() {
res, err := division(2, 0)
if err != nil {
fmt.Println(“计算错误”, err.Error())
} else {
fmt.Println(“计算结果”, res)
}
}
但是以上定义的err,如果出现了错误,程序也不会停止,会一直运行
那么如何在出现错误时终止程序呢
package main
import (
“errors”
“fmt”
“time”
)
func connectDatabase(address string, port int) (string, error) {
//如果address和port为空
if address == “” || port == 0 {
return “”, errors.New(“无法链接数据库”)
} else {
return “数据库链接成功”, nil
}
}
func main() {
// panic:可以在异常的时候让程序终止执行,退出程序。或者是程序所强依赖的基础组件不可用
// 此时程序已经无法继续正常工作,此时可以使用panic抛出异常,并且把程序退出
s, err := connectDatabase(“”, 0)
for {
time.Sleep(time.Second * 5)
if err != nil {
//说明无法链接数据库
fmt.Println(err)
panic(err) //此时就会退出程序
} else {
//说明链接成功
fmt.Println(s)
}
}
}
那么终止程序后,我们需不需要做一些处理呢,比如终止程序后,我们需要发一些消息给前端
这时就可以使用到defer:是go语言中的一种延迟调用机制,defer里面的内容可以在函数return之前或者是程序panic之前执行
defer是可以有多个的,采用先进后出的机制;一般用于资源回收和数据返回,defer也可以用于异常时的恢复
defer后跟函数调用
package main
import (
“errors”
“fmt”
)
// 实现数据库的链接
func connectDatabase(address string, port int) (string, error) {
// 如果address和port为空
if address == “” || port == 0 {
return “”, errors.New(“无法链接数据库”)
} else {
return “数据库链接成功”, nil
}
}
// 返回数据给前端
func returnDataToFrontend(msg string) {
fmt.Println(“返回给前端的数据是:”, msg)
}
func main() {
msg := “返回给前端的数据”
defer returnDataToFrontend(msg) // defer不会真正的执行
_, err := connectDatabase("", 0)
if err != nil {
fmt.Println(err)
// defer会在这一步执行
panic(err)
}
}
以上是终止程序后,将终止信息返回给管理员,那么我们又该如果将捕获到的错误信息进行相应的处理,从而让程序继续执行,不中断
package main
import “fmt”
func printSliceData(s []string) {
//使用recover进行异常捕获
defer func() {
fmt.Println(“程序执行失败,捕获异常”)
if err := recover(); err != nil {
// recover是用来捕获panic的报错的,尝试恢复,防止程序异常退出
fmt.Println(“捕获一个错误:”, err)
}
}()
fmt.Println("切片的内容:", s)
fmt.Println("切片的第三个值:", s[2]) //这里由于下标超出,程序会自己生成一个panic,从而终止程序
}
func main() {
s := []string{“a”, “b”}
printSliceData(s)
}
Go自定义类型
结构体
go语言中的结构体是一种自定义数据类型,可以将不同类型的数据组合在一起形成一个单独的实体
package main
import “fmt”
/*
type 结构体名 struct {
属性1 类型
属性2 类型
}
var 变量名 结构体名{
属性1: 值1
属性2: 值2
}
*/
func main() {
//定义一个名为People的结构体
type People struct {
name string
age int
address string
}
//先定义再赋值
var p1 People
p1.name = “华子”
p1.age = 18
p1.address = “陕西省汉中市”
fmt.Println(p1) //{华子 18 陕西省汉中市}
//直接声明
p2 := People{
name: “huazi”,
age: 19,
address: “陕西省汉中市”,
}
fmt.Println(p2) //{huazi 19 陕西省汉中市}
//第三种方式
var p3 People = People{“hua”, 20, “陕西省汉中市”}
fmt.Println(p3) //{hua 20 陕西省汉中市}
//使用
fmt.Printf("名字:%s 年龄:%d\n", p2.name, p2.age)
//修改值
p2.name = "华"
fmt.Printf("名字:%s 年龄:%d\n", p2.name, p2.age)
//赋值
p4 := p2
fmt.Println(p4)
//判断自定义变量是否相等
fmt.Println(p4 == p2)
}
属性是这样定义的,那么方法该如何定义呢
package main
import “fmt”
//定义一个名为People的结构体
type People struct {
name string
age int
address string
}
//定义结构体的方式函数
func (p *People) getInfo() string {
return fmt.Sprintf(“当前用户名:%s 年龄:%d 地址:%s\n”,
p.name, p.age, p.address)
}
func (p *People) Eat(food string) {
fmt.Printf(“%s今天吃了%s\n”, p.name, food)
}
func main() {
var p1 People = People{“华子”, 18, “陕西省汉中市”}
//调用方法:
//变量.方法名()
fmt.Println(p1.getInfo())
p1.Eat(“鸡公煲”)
}
go变量大小写特性
getInfo小写开头的变量名,是私有元素,只能在本包内使用
Eat大写开头的变量名,是公开元素,可以被外部的包调用
指针类型和值类型的方法
指针类型的方法
package main
import “fmt”
//定义一个名为People的结构体
type People struct {
name string
age int
address string
}
/*
自定义类型添加方法使用值类型和指针类型的区别
p:接受者,方法的参数
People:接受者的类型:分为值类型和指针类型
*/
//定义结构体的方式函数(指针类型)
func (p *People) getInfo() string {
p.age = 99
return fmt.Sprintf(“当前用户名:%s 年龄:%d 地址:%s\n”,
p.name, p.age, p.address)
}
func main() {
var p1 People = People{“华子”, 18, “陕西省汉中市”}
//调用方法:
//变量.方法名()
fmt.Println(p1.getInfo()) //当前用户名:华子 年龄:99 地址:陕西省汉中市
fmt.Println(p1) //{华子 99 陕西省汉中市}
}
值类型的方法
package main
import “fmt”
//定义一个名为People的结构体
type People struct {
name string
age int
address string
}
/*
自定义类型添加方法使用值类型和指针类型的区别
p:接受者,方法的参数
People:接受者的类型:分为值类型和指针类型
*/
//定义结构体的方式函数(值类型)
func (p People) getInfo() string {
p.age = 99
return fmt.Sprintf(“当前用户名:%s 年龄:%d 地址:%s\n”,
p.name, p.age, p.address)
}
func main() {
var p1 People = People{“华子”, 18, “陕西省汉中市”}
//调用方法:
//变量.方法名()
fmt.Println(p1.getInfo()) //当前用户名:华子 年龄:99 地址:陕西省汉中市
fmt.Println(p1) //{华子 18 陕西省汉中市}
}
嵌套
第一种嵌套方式
package main
import “fmt”
type Phone struct {
mode string
price int
}
type People struct {
name string
age int
address string
mobile Phone //People结构体中嵌套Phone
}
func (p *People) getInfo() string {
return fmt.Sprintf(“姓名:%s 手机:%s”, p.name, p.mobile.mode)
}
func main() {
var p1 People
p1.name = “华子”
p1.age = 18
p1.address = “陕西省汉中市”
p1.mobile.mode = “小米17”
p1.mobile.price = 1999
fmt.Println(p1.getInfo())
}
第二种嵌套方式
package main
import “fmt”
type Phone struct {
mode string
price int
}
type People struct {
name string
age int
address string
}
type info struct { //再写一个info结构体,将People和Phone合起来
Phone
People
}
func (p *info) getInfo() string {
return fmt.Sprintf(“姓名:%s 手机:%s”, p.name, p.mode)
}
func main() {
var p1 info
p1.name = “华子”
p1.age = 20
p1.address = “陕西省汉中市”
p1.mode = “小米”
p1.price = 1999
fmt.Println(p1.getInfo())
}
如果多个结构体上有相同的变量名,就不能直接进行赋值了
//如果多个结构体上有相同的变量名,就不能直接进行赋值了
package main
import “fmt”
type Phone struct {
name string
price int
}
type People struct {
name string
age int
address string
}
type info struct { //再写一个info结构体,将People和Phone合起来
Phone
People
}
func (p *info) getInfo() string {
return fmt.Sprintf(“姓名:%s 手机:%s”, p.People.name, p.Phone.name)
}
func main() {
var p1 info
p1.People.name = “华子”
p1.age = 20
p1.address = “陕西省汉中市”
p1.Phone.name = “小米”
p1.price = 1999
fmt.Println(p1.getInfo())
}
interface接口类型
go语言的接口是一种类型,定义了一组方法的集合,但是接口又不需要去实现他们,这些方法可以被不同的类型实现,进而就是这个类型实现了这个接口。
在go语言中,接口是一个重要的概念,接口被广泛应用于许多标准库和框架中。通过接口,可以使不同的类型之间实现相同的行为,从而达到代码复用和扩展的目的,并且可以实现不同类型之间的无缝切换。
在这里插入图片描述
不用接口之前
//对每个数据库的操作都要写不同的操作语句
package main
import “fmt”
//创建一个数据库的结构体,用来存放数据库的连接信息
type DBConfig struct {
User string
Password string
Host string
Port int
Database string
}
func main() {
//声明一个mysql数据库实例
db := DBConfig{“root”, “password”, “127.0.0.1”, 3306, “interface_test”}
fmt.Println(“mysql数据库的配置:”, db)
//插入一条数据
fmt.Println(“在mysql中插入一条数据:db.row(‘insert xxx to user’).Rows()”)
//换成pg
//生成pg数据库的连接实例
dbPg := DBConfig{"root", "password", "127.0.0.1", 3306, "interface_test"}
fmt.Println("pg数据库的配置:", dbPg)
//在pg中插入一条数据
fmt.Println("在pg中插入一条数据:db.QueryRow('insert xxx to user')")
}
使用接口之后
package main
import “fmt”
/*
type 接口名 interface {
方法名(参数) 返回值
方法名(参数) 返回值
方法名(参数) 返回值
}
*/
type DBCommon interface {
Insert(string) error
Update(string) error
Delete(string) error
}
//定义结构体存储数据库的信息
type DBConfig struct {
User string
Password string
Host string
Port int
Database string
}
//定义一个类型去实现这个接口
type MySQL struct {
config DBConfig
charSet string
}
func (m MySQL) Insert(data string) error {
fmt.Println(“插入数据到MySQL:”, data)
return nil
}
func (m MySQL) Update(data string) error {
fmt.Println(“更新数据到MySQL:”, data)
return nil
}
func (m MySQL) Delete(data string) error {
fmt.Println(“删除数据到MySQL:”, data)
return nil
}
func main() {
db := DBConfig{“root”, “password”, “127.0.0.1”, 3306, “interface_test”}
//声明一个接口的变量
var dbCommonInterface DBCommon
var m MySQL
m.config = db
m.charSet = “utf-8”
dbCommonInterface = m //将结构体变量赋值给接口变量
dbCommonInterface.Insert(“insert”) //调用接口中的定义的函数
dbCommonInterface.Update(“update”)
dbCommonInterface.Delete(“delete”)
}
package main
import “fmt”
/*
type 接口名 interface {
方法名(参数) 返回值
方法名(参数) 返回值
方法名(参数) 返回值
}
*/
type DBCommon interface {
Insert(string) error
Update(string) error
Delete(string) error
}
//定义结构体存储数据库的信息
type DBConfig struct {
User string
Password string
Host string
Port int
Database string
}
//定义一个类型去实现这个接口
type MySQL struct {
config DBConfig
charSet string
}
func (m MySQL) Insert(data string) error {
fmt.Println(“插入数据到MySQL:”, data)
return nil
}
func (m MySQL) Update(data string) error {
fmt.Println(“更新数据到MySQL:”, data)
return nil
}
func (m MySQL) Delete(data string) error {
fmt.Println(“删除数据到MySQL:”, data)
return nil
}
type PostgreSQL struct {
config DBConfig
charSet string
}
func (m PostgreSQL) Insert(data string) error {
fmt.Println(“插入数据到PostgreSQL:”, data)
return nil
}
func (m PostgreSQL) Update(data string) error {
fmt.Println(“更新数据到PostgreSQL:”, data)
return nil
}
func (m PostgreSQL) Delete(data string) error {
fmt.Println(“删除数据到PostgreSQL:”, data)
return nil
}
func main() {
db := DBConfig{“root”, “password”, “127.0.0.1”, 3306, “interface_test”}
//声明一个接口的变量
var dbCommonInterface DBCommon
//var m MySQL
var m PostgreSQL
m.config = db
m.charSet = “utf-8”
dbCommonInterface = m
dbCommonInterface.Insert(“insert”)
dbCommonInterface.Update(“update”)
dbCommonInterface.Delete(“delete”)
}
package main
import “fmt”
/*
type 接口名 interface {
方法名(参数) 返回值
方法名(参数) 返回值
方法名(参数) 返回值
}
*/
type DBCommon interface {
Insert(string) error
Update(string) error
Delete(string) error
}
//定义结构体存储数据库的信息
type DBConfig struct {
User string
Password string
Host string
Port int
Database string
}
//定义一个类型去实现这个接口
type MySQL struct {
config DBConfig
charSet string
}
func (m MySQL) Insert(data string) error {
fmt.Println(“插入数据到MySQL:”, data)
return nil
}
func (m MySQL) Update(data string) error {
fmt.Println(“更新数据到MySQL:”, data)
return nil
}
func (m MySQL) Delete(data string) error {
fmt.Println(“删除数据到MySQL:”, data)
return nil
}
type PostgreSQL struct {
config DBConfig
charSet string
}
func (m PostgreSQL) Insert(data string) error {
fmt.Println(“插入数据到PostgreSQL:”, data)
return nil
}
func (m PostgreSQL) Update(data string) error {
fmt.Println(“更新数据到PostgreSQL:”, data)
return nil
}
func (m PostgreSQL) Delete(data string) error {
fmt.Println(“删除数据到PostgreSQL:”, data)
return nil
}
type sqlServer struct {
config DBConfig
charSet string
}
func (m sqlServer) Insert(data string) error {
fmt.Println(“插入数据到sqlServer:”, data)
return nil
}
func (m sqlServer) Update(data string) error {
fmt.Println(“更新数据到sqlServer:”, data)
return nil
}
func (m sqlServer) Delete(data string) error {
fmt.Println(“删除数据到sqlServer:”, data)
return nil
}
func main() {
dbType := “sqlserver”
//声明一个接口的变量
var dbCommonInterface DBCommon
if dbType == "mysql" {
var m MySQL
dbCommonInterface = m
} else if dbType == "PostgreSQL" {
var pg PostgreSQL
dbCommonInterface = pg
} else {
var sqlS sqlServer
dbCommonInterface = sqlS
}
dbCommonInterface.Insert("insert")
dbCommonInterface.Update("update")
dbCommonInterface.Delete("delete")
}
空接口
方法1
package main
import “fmt”
/*
空接口:空接口不会定义任何的方法
空接口是可以接受任何类型的参数值
*/
//定义一个空接口
type EmptyInterface interface{}
func main() {
var ei EmptyInterface //ei是一个空接口类型的变量,可以接受任何类型的赋值
s1 := “这是一个字符串”
i1 := 72578
ei = s1
fmt.Println(ei)
ei = i1
fmt.Println(ei)
}
方法2
package main
import “fmt”
/*
空接口:空接口不会定义任何的方法
空接口是可以接受任何类型的参数值
*/
func main() {
s1 := “这是一个字符串”
i1 := 18
//定义空接口方法2
var ei1 interface{}
ei1 = s1
fmt.Println(ei1)
ei1 = i1
fmt.Println(ei1)
}
package main
import “fmt”
/*
空接口:空接口不会定义任何的方法
空接口是可以接受任何类型的参数值
*/
func main() {
//map记录联系方式:手机号(int) 座机号(010-123-789)string类型
contacts := make(map[string]interface{}) //在map中定义空接口可以接受不同类型的值
contacts[“huazi”] = 13289446832
contacts[“dot”] = “010-123-789”
fmt.Println(contacts)
}
在这里插入图片描述
接口类型断言和类型判断
package main
import “fmt”
/*
类型断言:大致知道了接口可能是某种类型,然后使用t,ok := 变量.(类型)
类型判断:switch t := 变量.(type){}
*/
func dealData(data interface{}) {
t, ok := data.(string) //如果data是string类型,则返回true给ok,t接受data的值,否则t接受空值
if ok {
fmt.Println(“当前类型为string,变量的值是:”, t)
} else {
fmt.Println(“data不是字符串”)
fmt.Println(“当前t的值:”, t)
}
}
func main() {
s := “这是一个字符串”
dealData(s)
i := 123456
dealData(i)
}
package main
import “fmt”
func getType(i interface{}) {
switch t := i.(type) {
case int:
fmt.Println(“当前值为int类型:”, t)
case string:
fmt.Println(“当前值为string类型:”, t)
case bool:
fmt.Println(“当前值为bool类型:”, t)
default:
fmt.Println(“当前类型不在处理范围”)
}
}
func main() {
s := “这个一个字符串”
getType(s)
i := 123
getType(i)
var emtry interface{}
getType(emtry)
}
接口嵌套和继承
package main
import “fmt”
type Generic_i interface {
Login()
LogOut()
}
type VIP_i interface {
Consult()
}
type User_i interface {
Generic_i
}
type VIPUser_i interface {
Generic_i
VIP_i
}
type Userordinary_s struct {
Name string
}
func (u Userordinary_s) Login() {
fmt.Println(“用户已登录:”, u.Name)
}
func (u Userordinary_s) LogOut() {
fmt.Println(“用户已退出:”, u.Name)
}
type Uservip_s struct {
Name string
}
func (u Uservip_s) Login() {
fmt.Println(“vip用户已登录:”, u.Name)
}
func (u Uservip_s) LogOut() {
fmt.Println(“vip用户已退出:”, u.Name)
}
func (u Uservip_s) Consult() {
fmt.Println(“vip用户可以进行咨询:”, u.Name)
}
func main() {
var userinterface User_i
u := Userordinary_s{
Name: “华子”,
}
userinterface = u
userinterface.Login()
userinterface.LogOut()
var vipuserinterface VIPUser_i
vipu := Uservip_s{
Name: ".",
}
vipuserinterface = vipu
vipuserinterface.Login()
vipuserinterface.LogOut()
vipuserinterface.Consult()
}