1、Println、Print、Printf
Println 打印内容会换行
Print 打印内容不会换行
Printf 打印可以使用%v占位符动态展示数据
package main
import "fmt"
func main() {
a := "aaa"
b := "bbb"
c := "ccc"
// fmt.Println("a=", a, "b=", b, "c=", c) //换行
// fmt.Print("Hello golang") //不换行
fmt.Printf("a=%v,b=%v,c=%v", a, b, c)
}
2、变量声明
var声明可用作全局,可先声明不赋值,但需要规定类型。也可声明赋值,同时声明多个类型使用()包裹
:= 局部声明,该方法只适用于局部变量赋值,不允许全局使用。
_为匿名变量,不需要接受的内容用_,它不会分配内存,也不会存在重复声明变量问题。
package main
import (
"fmt"
)
func getUser() (string, int) { //返回多个值
return "xiaoluo", 12
}
func main() {
// var 定义类型
var a string //初始化参数 需要追加类型
fmt.Println(a) //变量声明没赋值 为空
a = "hello"
fmt.Println(a)
var b int = 100 //如果不写int 也会类型推导出 b 为 int类型
fmt.Println(b)
var a1, a2 string // 同时声明多个类型
a1 = "hello"
a2 = "world"
fmt.Println(a1, a2)
var (
name string
age int
hobbies []string
) // 声明多个变量 类型不同
name = "test"
age = 10
hobbies = []string{"coding", "eating", "sleeping"}
fmt.Println(name, age, hobbies)
var (
name2 string = "xiaoluo"
age2 int = 21
hobbies2 []string = []string{"sleep", "code", "moive"}
) //声明并赋值
fmt.Println(name2, age2, hobbies2)
// 短变量声明 := 创建变量c并赋值 仅适用于声明局部变量,不能用做全局
c := "test"
fmt.Println(c)
d1, d2, d3 := 11, 22, "33" //短变量声明多个变量赋值
fmt.Printf("d1类型%T,d2类型%T,d3类型%T", d1, d2, d3)
var username, _ = getUser() //只需要username,_下划线为匿名变量,不会分配内存,不存在重复声明问题
var _, ages = getUser()
fmt.Println(username, ages)
}
3、常量
const 声明 定义就要赋值 不能改变
同时赋值多个 const ( ) 括号包住
iota配合const使用, 可出现自增情况
golang是严格区分变量大小写的 age | AGE
package main
import "fmt"
func main() {
//const 常量
const pi = 3.1415926 //定义就需要赋值,后续不能改变
fmt.Println(pi)
const ( //同时声明多个常量
A = "A"
B = "B"
)
fmt.Println(A, B)
const ( //只赋值了第一个,下面的就全是a
a = "a"
b
c = "c"
d
e
)
fmt.Println(a, b, c, d, e) // "a a c c c"
// iota 计数器,搭配const使用
// 每次const出现,会让iota自自增长加一
const m = iota // m=0
const (
n = iota // n=0
o // o=1
)
fmt.Println(n, o)
const ( // _ 跳过
a1 = iota
_ ///跳过了1
a3
a4
)
fmt.Println(a1, a3, a4) // 0,203
//iota 声明中间插队
const (
b1 = iota
b2 = 100
b3 = iota
b4
)
fmt.Println(b1, b2, b3, b4) // 0,100,2,3
const (
c1, c2 = iota + 1, iota + 2 //1,2
c3, c4 //2,3
c5, c6 //3,4
)
fmt.Println(c1, c2)
fmt.Println(c3, c4)
fmt.Println(c5, c6)
var age = 18
var AGE = 18
fmt.Println(age, AGE) // 18 18 go严格区分大小写
}
4、类型
golang数据类型
基本类型:整型、浮点型、布尔型、字符串型
复合数据类型:数组、切片、结构体、函数、map、通道(channel)、接口deng
4.1 整型int
int 分为 有符号整型 | 无符号整型 区别在于 有无负号
unsafe.Sizeof() 可查看变量占用字符数量
整型:分为 有符号整型 | 无符号整型 区别在于 有无负号
unsafe.Sizeof() 可查看变量占用字节数量
%v 原样输出 %o八进制输出 %d十进制输出 %x十六进制输出
package main
import (
"fmt"
"unsafe"
)
func main() {
//整形分为 有符号整型|对应的无符号整型
/*
有符号:
int8 -128~127 占一个字节
int16 -32768~32767 占两个字节
int32 占四个字节
int64 占八个字节
*/
/* 无符号:
uint8 0~255 占一个字节
uint16
uint32
uint64 占八个字节
*/
var num int8 = 12
fmt.Printf("num=%v 类型是%T\n", num, num) //num=100 类型是int
//unsafe.Sizeof() 查看变量占用字符数
fmt.Println(unsafe.Sizeof(num)) // 1 占一个字节
// int 不同长度转换
var a1 int32 = 10
var a2 int64 = 21
fmt.Println(int64(a1) + a2) //不同类型需要转换成同一类型加
//高位转地位 可能有问题
var n1 int16 = 200
fmt.Println(int8(n1)) // -56 出现问题
// %d表示10进制输出 %b表示二进制输出 %o八进制输出 %x表示16进制
num1 := 40
fmt.Printf("n1=%v 类型:%T\n", num1, num1)
fmt.Println(unsafe.Sizeof(num1)) // 8
fmt.Printf("num=%v\n", num1) //原样输出
fmt.Printf("num=%o\n", num1) //50
fmt.Printf("num=%d\n", num1) //40
fmt.Printf("num=%x\n", num1) //28
}
4.2 浮点型float
float浮点型存在 float32 和 float64 两种类型 分别占4|8 个字节
go语言中 float运算会存在精度丢失问题,需要使用第三方包来处理
int转float float转换int 会截取整数,需要注意
package main
import (
"fmt"
"unsafe"
)
func main() {
//1、定义float类型
var a float32 = 3.12
fmt.Printf("值:%v--%f\n", a, a) // 3.12 -- 3.120000 保留六位小数点
fmt.Println(unsafe.Sizeof(a)) // 4 float32占用4个字节
var b float64 = 3.12
fmt.Printf("值:%v--%f\n", b, b) // 3.12--3.120000
fmt.Println(unsafe.Sizeof(b)) // 8 float64占用八个字节
//2、 %f 输出float类型 %.2f输出数据保留两位小数
fmt.Printf("%v-%.2f\n", b, b) //3.12-3.12
fmt.Printf("%v-%.4f\n", b, b) //3.12-3.1200
// 64位操作系统中Go语言中浮点默认是float64
f1 := 3.1415926
fmt.Printf("%f--%T\n", f1, f1) //3.141593--float64
//3、GO中科学计数 表示浮点数
var f2 float32 = 3.14e2 //表示3.14*10的二次方
fmt.Printf("%v-%T\n", f2, f2) //314-float32
var f3 float32 = 3.14e-2 //表示3.14/10的二次方
fmt.Printf("%v-%T\n", f3, f3) //0.0314-float32
//3、精度丢失问题
var f4 float64 = 1129.6
fmt.Println(f4 * 100) //112959.99999999999
m1 := 8.2
m2 := 3.8
fmt.Println(m1 - m2) //4.3999999999999995
//4、int类型转换成float
x := 4
x1 := float64(a)
fmt.Printf("a类型:%T,b类型:%T\n", x, x1) //a类型:int,b类型:float64
//5、float类型转换成int
var x2 float64 = 3.13
x3 := int(x2)
fmt.Printf("x2类型:%T,x3类型:%T\n", x2, x3) //x2类型:float64,x3类型:int
}
4.3 布尔型bool
默认值为false 占一个字节
package main
import (
"fmt"
"unsafe"
)
func main() {
/*
go语言中以bool类型声明布尔类型数据,只有true和false两个值
1、布尔类型变量默认为false
2、go语言中不允许将整型强转至布尔型
3、布尔型无法参与数值运算,也无法与其他类型进行转换
*/
var flag bool
fmt.Println(flag) //false 默认值
fmt.Println(unsafe.Sizeof(flag)) //1 占一个字节
fmt.Printf("%v,%T\n", flag, flag) // false,bool
var s string
fmt.Printf("%v\n", s) //默认为空
var i int
fmt.Printf("%v ", i) //默认 0
var f float32
fmt.Printf("%v", f) //默认 0.0
}
5、字符串&字符
5.1 字符串
字符串转译 \转移 一些特殊字符,\\ 转译成\ 多行字符串类似于js的模板字符串
package main
import "fmt"
func main() {
//1、定义字符串
var str1 string = "你好"
var str2 = "你好"
str3 := "你好"
fmt.Printf("%v--%T\n", str1, str1)
fmt.Printf("%v--%T\n", str2, str2)
fmt.Printf("%v--%T\n", str3, str3)
//2、转义字符
str4 := "this \n is str" // 输入 this 换行 is str
fmt.Println(str4)
str5 := "C:\\GO\\Bin"
fmt.Println(str5) // C:\GO\Bin
str6 := "C:\\GO\\\"Bin\""
fmt.Println(str6) // C:\GO\"Bin"
//3、多行字符串 类似于 js模版字符串
str7 := `
你好
换行
呵呵
`
fmt.Println(str7)
}
5.2 字符
字符只能用单引号括起来
字符串不能随意通过索引去修改
中文是通过unicode编码编译
一个汉字占用三个字符,如果遍历存在汉字的字符串 需要配合使用rune
package main
import "fmt"
func main() {
//1、golang定义字符 单引号是定义字符
var a = 'a' //定义字符 只能有一个元素
fmt.Printf("值:%v,类型:%T\n", a, a) //值:97,类型:int32
//2、输出原样字符
fmt.Printf("值:%c,类型:%T\n", a, a) //值:a,类型:int32
//3、输出字符串中的字符
var str = "this"
fmt.Printf("值:%c,类型:%T\n", str[2], str[2]) //值:i,类型:uint8
//4、一个汉字占用3个字节(golang中使用 utf-8编码),一个字母占用一个字节
//unsafe.Sizeof() 无法查看字符串类型占用存储空间 使用len()可以
fmt.Println(len(str)) //4
str2 := "你好golang"
fmt.Println(len(str2)) //12
var s = '国' // unicode编码 国 对应 22269
fmt.Printf("值:%v,类型:%T\n", s, s) //值:22269,类型:int32
//5、遍历输出字符串 for range
for i := 0; i < len(str); i++ { //byte 类型 代表ASCII码的一个字符
fmt.Printf("%v(%c)", str2[i], str2[i]) //28(ä)189(½)160( )229(å) 有中文 导致乱了
}
for _, r := range str2 { //rune类型 代表utf-8的一个字符
fmt.Printf("%v(%c)\n", r, r) //20320(你)22909(好)103(g)111(o)108(l)97(a)110(n)103(g)
}
//5、修改字符串
s1 := "big"
// s1[0] = "s" //直接修改报错
byteStr := []byte(s1) //如果存在汉字不能私用byte
byteStr[0] = 'p'
fmt.Println(string(byteStr)) //pig
s2 := "你好"
runeStr := []rune(s2)
runeStr[0] = '我'
fmt.Println(string(runeStr)) //我好
}
5.3 其它类型转字符串类型
有两种方式:1、使用string.Sprint转换 2、使用第三方包 strconv转换
package main
import (
"fmt"
"strconv"
)
func main() {
//转换建议地位转换成高位 ,如果 地位转高位 可能会存在数据不准 int-> float
var a int8 = 20
var b int16 = 80
fmt.Println(int16(a) + b) //不同种类型需要先转成同一类型 低转高
var f1 float32 = 1.23456789
var f2 float64 = 1.23456789
fmt.Println(float64(f1) + f2) //情况一样
fmt.Println(float64(a) + f2)
//其它类型转string
var i int = 20
var f float64 = 3.14
var t bool = false
var be byte = 'a'
//1、strings.Sprint 转换
str1 := fmt.Sprintf("%d", i)
str2 := fmt.Sprintf("%f", f)
str3 := fmt.Sprintf("%t", t)
str4 := fmt.Sprintf("%c", be)
fmt.Printf("值:%v,类型:%T\n", str1, str1) //值:20,类型:string
fmt.Printf("值:%v,类型:%T\n", str2, str2) //值:3.140000,类型:string
fmt.Printf("值:%v,类型:%T\n", str3, str3) //值:false,类型:string
fmt.Printf("值:%v ,类型:%T\n", str4, str4) //值:a ,类型:string
//2、通过strconv转换
str5 := strconv.FormatInt(int64(i), 10) // 默认结束 int64类型
str6 := strconv.FormatFloat(float64(f), 'f', 2, 64) //接受四个参数 1-变量 2-格式化类型 3-保留小数点 4-格式化类型
str7 := strconv.FormatBool(t)
str8 := strconv.FormatUint(uint64(be), 10)
fmt.Printf("值:%v,类型:%T\n", str5, str5) //值:20,类型:string
fmt.Printf("值:%v,类型:%T\n", str6, str6) //值:3.14,类型:string
fmt.Printf("值:%v,类型:%T\n", str7, str7) //值:false,类型:string
fmt.Printf("值:%v,类型:%T\n", str8, str8) //值:97,类型:string
}
5.4 string型转换成其它类型
package main
import (
"fmt"
"strconv"
)
func main() {
//string类型转换成整型
str := "123456.11"
fmt.Printf("%v--%T\n", str, str) //123456--string
//
/* ParseInt
1、string数据
2、进制
3、位数
*/
num, _ := strconv.ParseInt(str, 10, 32) // 0 返回两个值 如果转换失败 会返回0
fmt.Println(num)
flo, _ := strconv.ParseFloat(str, 64)
fmt.Println(flo) // 123456.11
b, _ := strconv.ParseBool("xxx")
fmt.Printf("值:%v,类型:%T", b, b) //值:false,类型:bool
}
6、运算符
位运算符 分为 &与 | 或 ^ 非 <<左移n位 >>右移n位
package main
import "fmt"
func main() {
//位运算符
/*
& 左右两边都为1返回1
| 左右两边有一个1返回1
^ 左右两边不相同返回1
<< 左移n位就是乘以2的n次方 “a<<b” 把a的各个二进制全部往左移动b位,高位丢弃,低位补0
<< 右移n位就是乘以2的n次方
*/
var (
a = 5 // 0101
b = 10 // 1010
)
fmt.Println("a&b", a&b) //0 0000
fmt.Println("a|b", a|b) //15 1111
fmt.Println("a^b", a^b) //15 1111
fmt.Println("a<<b", a<<b) //5120 0101向左转移10位变成-> 1010000000000
fmt.Println("a<<b", a>>b) //0 向左
}
7、if & for 循环
- if 有局部变量声明的方法
- golang中无while语句,可用for替换
package main
import "fmt"
func main() {
//if语句
//区别在于 上者a是全局变量, 下方b为局部变量
var a = 10
//普通写法 注意判断用 == 一个是赋值
if a == 10 {
fmt.Println(a) //10
}
//另一种写法
if b := 40; b != 40 {
fmt.Println(b)
} else {
fmt.Println("不符合") //不符合
}
// for 循环遍历
for i := 0; i < 10; i++ {
if i < 6 {
fmt.Println(i)
} else {
break //不符合条件跳出循环
}
}
i := 0
for { //类似于 while golang中没有while语句
if i < 7 {
fmt.Println(i)
} else {
break
}
i++
}
}
8、range循环 & fallthrought
for..range循环 返回两个参数 参数1:key 参数2:val 不需要的参数可以用_接受
switch中的fallthought 穿透,加上它可以穿透到下方判断(即使当前case不满足)
package main
import "fmt"
func main() {
var str = "你好,golang"
//for range 遍历字符串
for key, val := range str {
// fmt.Println("key=", key, "val=", val)
/* 汉字使用utf-8输出 字符以 ASCII码值输出
key= 0 val= 20320
key= 3 val= 22909
key= 6 val= 44
key= 7 val= 103
key= 8 val= 111
key= 9 val= 108
key= 10 val= 97
key= 11 val= 110
key= 12 val= 103
*/
fmt.Printf("Key=%v,val=%c\n", key, val)
/*
Ksey=0,val=你
Key=3,val=好
Key=6,val=,
Key=7,val=g
Key=8,val=o
Key=9,val=l
Key=10,val=a
Key=11,val=n
Key=12,val=g
*/
}
// for range 遍历切片
var arr = []string{"php", "java", "js"}
for _, val := range arr {
fmt.Println("val=", val)
}
/*
val= php
val= java
val= js
*/
//golang中 switch的穿透fallthrought
// fallthrought 语法可以执行满足条件的case的下一个case,为了兼容C语言中的case设计的
var a int8 = 2
switch {
case a < 3:
fmt.Println("a<3")
fallthrough //向下穿透打印(即使当前case不符合也会穿透至下方) ( 可以打印出来a<3 a<4 )不加上fallthrough只打印这一行就跳出
case a < 4:
fmt.Println("a<4")
case a < 5:
fmt.Println("a<5") //因为上面a<4未穿透,即跳出 不打印 a<5
}
}
9、break & continue & goto & label:
break 结束当前当前循环 break可以搭配label 来实现跳出多重循环
continue 结束当前循环,开始下一轮循环
goto 配合label使用,无条件跳转至代码处,并执行下列代码
package main
import "fmt"
func main() {
/*
golang中break语句
· 用于跳出循环中的语句,并开始执行循环之后的语句
· break在switch(开关语句)中执行一条case后跳出语句的作用
· 在多重循环中,可以用标号 label标出想 break 的循环
*/
// 双重循环,用break只能跳出当前循环, 但是使用label: 标记,是可以完成跳出多成循环的效果的
label1:
for i := 0; i < 10; i++ {
for j := 0; j < 10; j++ {
if j == 3 {
break label1
}
fmt.Printf("i=%v j=%v\n", i, j)
}
}
/*
i=0 j=0
i=0 j=1
i=0 j=2
*/
//break 跳出当前循环
for i := 0; i < 10; i++ {
if i == 3 {
break
}
fmt.Printf("%d", i)
/* 到3截止
i=0 j=0
i=0 j=1
i=0 j=2
*/
}
//continue 结束当前循环,开始下一次循环
for i := 0; i < 10; i++ {
if i == 3 {
continue
}
fmt.Printf("%d", i) //012456789 跳过了3
}
//goto 语句通过标签进行代码间的无条件跳转,goto语句可以快速跳出循环,避免重复
var n = 30
if n > 20 {
fmt.Println("成年人")
goto test
}
fmt.Println("小屁孩")
fmt.Println("hello world") // 跳过了这两行 直接到 how are you
test:
fmt.Println("hello ,how are you")
}
10、数组&切片
10.1 声明数组的方式
var arr [3]int var strArr = [3]string{"aa","bb","cc"}
var arr2 [...]int 可自动推导长度 数组长度定了就不能去修改
package main
import "fmt"
func main() {
//数组 长度是固定的,不可改变
//1、数组的长度也是类型的一部分
var arr1 [3]int
var arr2 [4]int
var arr3 [3]string
fmt.Printf("arr1:%T,arr2:%T,arr3:%T\n", arr1, arr2, arr3) //arr1:[3]int,arr2:[4]int,arr3:[3]string
//2、数组的初始化
fmt.Println(arr1) // [0 0 0]
fmt.Println(arr3) // [ ]
//1、先声明 再下标赋值
arr1[0] = 1
arr1[1] = 2
arr1[2] = 3
fmt.Println(arr1) //[1 2 3]
var strArr [3]string
strArr[0] = "hello"
strArr[1] = "world"
strArr[2] = "!"
fmt.Println(strArr) //[hello world !]
//2、声明时赋值
var arr4 = [4]int{1, 2, 3}
fmt.Println(arr4) // [1,2,3,0] 第四项没有填充默认补0
//3、不清楚长度时 三个点可以自动推导
var arr5 = [...]int{123, 23, 241}
// arr5[3]=3 这里不能赋值,超出范围
fmt.Println(arr5) // [123 23 241]
fmt.Println(len(arr5)) //3
//4、index类型声明,长度有index最大值确定
arr6 := [...]int{0: 1, 1: 10, 2: 20, 5: 50} //前者是index 后者是val 无用0补气
fmt.Println(len(arr6)) // 6
fmt.Println(arr6) //[1 10 20 0 0 50 ]
//5、数组的循环遍历
arr7 := [...]int{2, 4, 6, 1, 2, 6}
for key, val := range arr7 {
fmt.Println("key:", key, "val:", val)
}
//todo: 查找数组中的最大值和索引
arr8 := [5]int{2, 5, 1, 7, 3}
index := 0
val := arr8[0]
for i := 0; i < len(arr8); i++ {
if arr8[i] > val {
val = arr8[i]
index = i
}
}
fmt.Println(val, index)
}
10.2 值类型|引用类型
数组是值类型,切片是引用类型,其声明区别就是在声明时是否定义长度
package main
import "fmt"
func main() {
// 值类型: 改变副本值,不会改变原来的值 相当于复制了一层
// 引用类型: 改变副本值,会改变原来的值 相当于共享一块数据 都指向同一块内存地址
// 基本数据类型 和 数组都是值类型
//数组 定义时定了长度
var a = 10
var b = a
fmt.Println(a == b) // true
var arr1 = [3]int{1, 0}
var arr2 = [3]int{1}
fmt.Println(arr1 == arr2) // true
arr1[2] = 55
fmt.Println(arr1, arr2)
var arr3 = [2]int{1, 2}
var arr4 = arr3
arr3[1] = 10
fmt.Println(arr3 == arr4) // false
fmt.Println(arr3, arr4) // [1 10] [1 2] 修改了arr3并没有去影响到arr4
// 切片 是引用类型 定义没定义长度
var arr5 = []int{1, 2, 3, 45}
arr6 := arr5
arr5[1] = 10
fmt.Println(arr5, arr6) //1 10 3 45] [1 10 3 45] arr6的值也被影响了, 这是因为arr6是引用了arr5的同一内存地址
}
10.3 多维数组
var arr = [2][3]string{{"1"},{""2}} 声明二维数组
package main
import "fmt"
func main() {
//1、多维数组定义
// var arr1 = [3]int{1,2,3} 一维数组
var arr2 = [2][3]string{ //两行三列
{"吃饭", "睡觉", "打豆豆"},
{"唱歌", "跳舞", "看医生"},
}
fmt.Println(arr2) // [[吃饭 睡觉 打豆豆] [唱歌 跳舞 看医生]]
fmt.Println(arr2[0][2]) // 打豆豆
//2、循环遍历多位数组
for _, item1 := range arr2 {
for _, item2 := range item1 {
fmt.Printf("%v\\", item2) // 吃饭\睡觉\打豆豆\唱歌\跳舞\看医生\
}
}
}
10.4 切片
切片定义类似数组,不定义长度的数组即位切片
切片为引用类型
长度:切片包含的元素个数
容量:从第一个元素开始数,到底层数组元素末尾的个数
切片s的长度和容量可以通过len(s)和cap(s)来获取
package main
import "fmt"
func main() {
// 1、切片声明 和 数组区别在于没有确认长度
var arr1 = []int{1, 2, 3}
fmt.Println(arr1) //[1,2,3]
fmt.Printf("值:%v,类型:%T,长度:%v\n", arr1, arr1, len(arr1)) //值:[1 2 3],类型:[]int,长度:3
var arr2 = []int{1: 2, 2: 3, 3: 4} //空余补0
fmt.Printf("值:%v,类型:%T,长度:%v\n", arr2, arr2, len(arr2)) //值:[0 2 3 4],类型:[]int,长度:4
var arr3 []int
fmt.Println(arr3 == nil) //true golang中切片声明后,默认值为nil
//2、切片循环遍历
var strSlice = []string{"a", "b", "c", "d"}
for i, v := range strSlice {
fmt.Printf("i=%v,v=%v|", i, v) // i=0,v=a|i=1,v=b|i=2,v=c|i=3,v=d|
}
//3、基于数组定义切片
a := [5]int{1, 2, 3, 4, 5}
b := a[:] //获取数组里面所有的值
fmt.Printf("类型:%T,值:%v", b, b) //[[]int,值:[1 2 3 4 5] []int 是切片
//切片截取
c := a[1:4] //获取索引1-4(包含1,不包含4 【1,4) )的值 左闭右开
fmt.Println(c) //[2 3 4]
d := a[2:] //包含索引2之后的数据
fmt.Println(d) // [3 4 5]
e := a[:3] // 索引3之前的数据
fmt.Println(e) //[1 2 3]
//4、基于切片定义切片
var numSlice = []int{1, 2, 3, 4, 5}
f := numSlice[1:4]
fmt.Println(f) //[2 3 4]
g := numSlice[2:]
fmt.Println(g) //[3 4 5]
h := numSlice[:3]
fmt.Println(h) //[1 2 3]
//5、关于切片的长度和容量
/*
长度:切片包含的元素个数
容量:从第一个元素开始数,到底层数组元素末尾的个数
切片s的长度和容量可以通过len(s)和cap(s)来获取
*/
s := []int{2, 3, 5, 7, 8, 88}
fmt.Printf("s长度:%v,s容量:%v\n", len(s), cap(s)) //s长度:6,s容量:6
t := s[2:] //5, 7, 8, 88
fmt.Printf("s长度:%v,s容量:%v\n", len(t), cap(t)) //s长度:4,s容量:4
j := s[2:5] //5, 7, 8 容量4的由来 5,7,8,88
fmt.Printf("s长度:%v,s容量:%v\n", len(j), cap(j)) //s长度:3,s容量:4
}
10.5 make©&append
package main
import "fmt"
func main() {
/*
切片声明和初始化的几种方法
*/
//1、var定义
// var slice1 []int
//2、make函数创建 make([]T,size,cap)
var slice2 = make([]int, 5, 10) // [0 0 0 0 0] 参数1:类型 参数2:长度 参数3:容量
fmt.Println(slice2)
fmt.Printf("长度:%v,容量:%v\n", len(slice2), cap(slice2)) //长度:5,容量:10
/* slice2[5] = 2 报错 无法使用下标给切片扩容
fmt.Println(slice2) */
//3、切片扩容 使用append追加扩容
sl2ice2 := append(slice2, 1)
fmt.Println(sl2ice2) //[0 0 0 0 0 1]
//4、append方法合并切片
sliceA := []string{"php", "java"}
sliceB := []string{"nodejs", "js"}
sliceA = append(sliceA, sliceB...)
fmt.Println(sliceA) //[php java nodejs js]
//5、切片扩容策略
var sliceC []int
for i := 1; i <= 10; i++ {
sliceC = append(sliceC, i)
fmt.Printf("长度:%v,容量:%v\n", len(sliceC), cap(sliceC))
/*
长度:1,容量:1
长度:2,容量:2
长度:3,容量:4
长度:4,容量:4
长度:5,容量:8
长度:6,容量:8
长度:7,容量:8
长度:8,容量:8
长度:9,容量:16
长度:10,容量:16
*/
}
/*
6、值类型:改变变量富文本的值,不会改变变量本身的值
引用类型:改变变量赋文本的值,会改变变量本身的值
*/
sliceD := []int{1, 2, 3, 4}
sliceE := sliceD
sliceE[0] = 100
fmt.Println("sliceD=", sliceD) //sliceD= [100 2 3 4] 改变sliceE 的值 sliceD也变了
sliceF := []int{1, 2, 3, 4}
sliceG := make([]int, 5, 5)
copy(sliceG, sliceF) // 将F的值复制给D
sliceF[1] = 999
fmt.Println("sliceF=", sliceF, "sliceG=", sliceG) //sliceF= [1 999 3 4] sliceG= [1 2 3 4 0]
//7、slice切片删除元素
//append方法 删除索引为2的元素, 注意:append合并切片的时候最后一个元素需要...
a := []int{1, 2, 3, 4, 5}
a = append(a[:2], a[3:]...)
fmt.Println(a) //[1 2 4 5]
//8、字符串转换
str1 := "hello golang"
byteStr := []byte(str1)
byteStr[0] = 'H'
fmt.Println(string(byteStr)) //Hello golang
str2 := "你好golang"
runStr := []rune(str2)
runStr[0] = '大'
fmt.Println(string(runStr)) //大好golang
}
10.6 切片排序&sort包
package main
import (
"fmt"
"sort"
)
func main() {
//找出下标和为8的两个数
var arr = [...]int{1, 3, 5, 7, 8}
for i := 0; i < len(arr); i++ {
for j := i + 1; j < len(arr); j++ {
if arr[i]+arr[j] == 8 {
fmt.Println("i=", i, "j=", j)
}
}
}
/*
选择排序:进行从小到大排序 [9,4,6,1,8,7]
*/
var arr2 = []int{9, 4, 6, 1, 8, 7}
for i := 0; i < len(arr2); i++ {
for j := i + 1; j < len(arr2); j++ {
if arr2[i] > arr2[j] {
arr2[i], arr2[j] = arr2[j], arr2[i] // 交换位置
}
}
}
fmt.Println(arr2) //[1 4 6 7 8 9]
/*
冒泡排序:进行从小到大排序 [2,6,4,1,9,3]
*/
var arr3 = []int{2, 6, 4, 1, 9, 3}
for i := 0; i < len(arr3); i++ {
for j := i + 1; j < len(arr3); j++ {
if arr3[i] > arr3[j] {
temp := arr3[i]
arr3[i] = arr3[j]
arr3[j] = temp
}
}
}
fmt.Println(arr3)
/*
sort包
*/
intList := []int{2, 5, 7, 3, 1, 0}
float64List := []float64{2.5, 5.7, 3.1, 1.0, 0.0}
stringList := []string{"a", "b", "g", "d", "e", "c"}
//升序
sort.Ints(intList)
sort.Float64s(float64List)
sort.Strings(stringList)
fmt.Println(intList)
fmt.Println(float64List)
fmt.Println(stringList)
/*
[0 1 2 3 5 7]
[0 1 2.5 3.1 5.7]
[a b c d e g]
*/
//降序
sort.Sort(sort.Reverse((sort.IntSlice(intList))))
sort.Sort(sort.Reverse((sort.Float64Slice(float64List))))
sort.Sort(sort.Reverse((sort.StringSlice(stringList))))
fmt.Println(intList)
fmt.Println(float64List)
fmt.Println(stringList)
/*
[7 5 3 2 1 0]
[5.7 3.1 2.5 1 0]
[g e d c b a]
*/
}
11、map类型
map为引用类型
map的curd操作,查 v,ok := mapObj["key"] 成功返回value true 失败返回 空 false
delete(mapObj,key) 删除map数据中的键值对
strings包的Split 分割 类似于js的split
⚠️ map的range遍历是随机的 代码中有方法让其按key变量
package main
import (
"fmt"
"sort"
"strings"
)
func main() {
// map为引用类型
// 1、 make创建map类型的数据
var userInfo = make(map[string]string)
userInfo["name"] = "zs"
userInfo["age"] = "18"
fmt.Println(userInfo) //map[age:18 name:zs]
//2、声明时初始化值
userInfo2 := map[string]string{
"username": "ls",
"age": "21",
}
fmt.Println(userInfo2) //map[age:21 username:ls]
//3、循环遍历map类型数据
for k, v := range userInfo2 {
fmt.Printf("key:%v,value:%v\n", k, v)
/*
key:username,value:ls
key:age,value:21
*/
}
//4、map类型数据的curd
userInfo3 := make(map[string]string)
userInfo3["name"] = "zw" //增
userInfo3["name"] = "qw" //改
v, ok := userInfo3["age"] //查
fmt.Println(v, ok) // 空 false
delete(userInfo3, "name")
fmt.Println(userInfo3) // map[]
//5、map类型的切片 map不初始化值为nil
var people = make([]map[string]string, 3, 3) // map类型切片
fmt.Println(people[0]) // map[] 不初始化值 默认值时nil
fmt.Println(people[0] == nil) // true
if people[0] == nil {
people[0] = make(map[string]string)
people[0]["name"] = "ls"
people[0]["age"] = "21"
}
for k, v := range people {
fmt.Println("k=", k, "v=", v)
}
/*
k= 0 v= map[age:21 name:ls]
k= 1 v= map[]
k= 2 v= map[]
*/
//6、map类型存放多多值类型
var course = make(map[string][]string)
course["java"] = []string{"springMVC", "myBatis", "odbc"}
course["javascript"] = []string{"nodejs", "vue", "react"}
for k, v := range course {
fmt.Println("k=", k, "v=", v)
}
/*
k= java v= [springMVC myBatis odbc]
k= javascript v= [nodejs vue react]
*/
//7、map的排序
map1 := make(map[int]int, 10)
map1[10] = 100
map1[4] = 43
map1[6] = 50
map1[1] = 13
fmt.Println(map1) //map[1:200 2:43 3:50 4:13]
for k, v := range map1 {
fmt.Println("k=", k, "v=", v)
}
/* map遍历的值是随机的 不会从第一项开始 比如 map[1]
k= 4 v= 43
k= 6 v= 50
k= 1 v= 13
k= 10 v= 100
*/
//疑问?如何实现按照key升序排序呢
//1、把map的key放在切片里
var slice []int
for k, _ := range map1 {
slice = append(slice, k)
}
fmt.Println(slice) // [10 4 6 1]
//2、让key进行升序排序
sort.Ints(slice)
fmt.Println(slice, "slice")
//循环slice输出map对应key值
for _, v := range slice {
fmt.Printf("%v\t", map1[v]) //13 43 50 100
}
//8、字符串 split分割成切片
var str = "so beautiful you are, cute girl!"
var sliceStr = strings.Split(str, " ") //使用strings包 分割str生产切片,以空格分割
fmt.Println(sliceStr) // [so beautiful you are, cute girl!]
}
12、func 函数
12.1 可变参数
有些时候参数数量不确定 使用 x ...int x可以接受之后所有的int成一个数组
package main
import "fmt"
// 函数
func sumTowNum(a int, b int) int {
return a + b
}
/*
1、参数问题
*/
// 参数类型一致 简写
func sumTowNum2(a, b, c int) int {
return a + b + c
}
// 参数数量不固定 类似于 js中的 ...args 接受参数成数组了
func sumTowNum3(x ...int) {
fmt.Println(x) // [1 2 3 4]
for _, v := range x {
fmt.Println(v) // 1 2 3 4
}
}
// x 接受第一个参数 , y接受其余参数
func sumTowNum4(x int, y ...int) {
fmt.Println("x=", x, "y=", y) // x= 1 y= [11 22 33 44]
}
func main() {
fmt.Println(sumTowNum(1, 2)) //3
fmt.Println(sumTowNum2(1, 2, 3)) //6
sumTowNum3(1, 2, 3, 4)
sumTowNum4(1, 11, 22, 33, 44)
}
12.2 函数返回值
返回多个值,不需要的使用_匿名接受
package main
import "fmt"
/*
函数返回多个值
*/
func cacl(x, y int) (int, int) {
return x + y, x - y
}
// 返回值命名: 函数自定义可以返回多个命名,并在函数体中直接使用这个变量,最后通过return 返回
func cacl2(x, y int) (sum, sub int) {
sum = x + y
sub = x - y
return sub, sum
}
func main() {
fmt.Println(cacl(1, 2)) //3 -1
fmt.Println(cacl2(1, 2)) //-1 3 //返回值sub sum取反返回
a, _ := cacl2(1, 2)
fmt.Println(a) // 匿名接受第一个返回值
}
12.3 定义函数
package main
import "fmt"
// 1、定义函数类型
type calc func(int, int) int
type myInt int //自定义类型
func add(x, y int) int {
return x + y
}
func sub(x, y int) int {
return x - y
}
func test() {
fmt.Println("test...")
}
type cbType func(int, int) int
// 2、方法作为另一个函数的参数 改函数接受了两个int类型参数,第三个参数是一个 cbType类型的函数 有点像callback函数
func receiveCb(x, y int, cb cbType) int {
return cb(x, y)
}
func main() {
// fmt.Println(factorial(8))
var c calc
c = add
fmt.Printf("c的类型:%T\n", c) // main.calc
d := add
fmt.Printf("d的类型:%T\n", d) //func(int, int) int 类型推倒
var a1 int = 100
var a2 myInt = 200
fmt.Printf("a的类型:%T,b的类型:%T\n", a1, a2) //a的类型:int,b的类型:main.myInt
fmt.Println(myInt(a1) + a2) // 虽然都是Int数字类型 但是一个是myInt 仍然需要转换成同一类型
fmt.Println(receiveCb(100, 200, sub))
}
12.4 函数的闭包
package main
import "fmt"
// 函数的递归
func factorial(n int) int {
if n == 0 {
return 1
}
return n * factorial(n-1)
}
// 实现阶层相加
func add(n int) int {
if n == 0 {
return 0
}
return n + add(n-1)
}
// 闭包
func add2() func(x int) int {
var i = 10
return func(x int) int {
i += x
return i
}
}
func main() {
//1、匿名函数
func() {
fmt.Println("test") //test
}()
//接受参数的匿名自执行函数 类似于js
func(x, y int) {
fmt.Println(x + y)
}(1, 6) //7
var myFn = func() {
fmt.Println("myFn") // myFn
}
myFn()
fmt.Println(factorial(5)) // 120
fmt.Println(add(100)) //5050
var test = add2()
fmt.Printf("%T\n", test) // test是返回的那个函数
fmt.Println(test(10)) //20
fmt.Println(test(10)) //30
fmt.Println(test(10)) //40
}
13、panic&recover处理异常
golang中没有异常处理机制 panic可以在任意地方抛出错误,recover只能在defer中的函数使用
panic抛出错误,如果不接受,程序会直接报错
package main
import "fmt"
/*
Go 语言中没有异常机制, 可以使用 panic/recover 模式来处理错误
panic 可以在任何地方引发, 但 recover 只能在 defer调用的函数中有效
*/
func fn1() {
fmt.Println("fn1")
}
func fn2() {
defer func() {
err := recover()
if err != nil { //err 不等于nil 说明有异常
fmt.Println(err) //抛出异常
}
}()
panic("抛出异常")
}
func fn3(a, b int) int {
defer func() {
err := recover()
if err != nil {
fmt.Println("err:", err) //err: runtime error: integer divide by zero
}
}()
return a / b
}
func main() {
fn1()
fn2()
fn3(10, 0)
fmt.Println("结束")
}
14、time包&时间戳
14.1 格式化
时间格式化模板不再是常用的 YYYY-MM-DD
需要使用Go的诞生时间 2006-01-02 15:04:05
小时中 15表示24小时制。03 表示 12小时制
package main
import (
"fmt"
"time"
)
func main() {
//1、获取当前日期
timeObj := time.Now() //2023-12-20 23:02:42.596486 +0800 CST m=+0.000429084
fmt.Println(timeObj)
Year := timeObj.Year()
Month := timeObj.Month()
Day := timeObj.Day()
Hour := timeObj.Hour()
Minute := timeObj.Minute()
Second := timeObj.Second()
//%02d 表示宽度, 整数不够2列就补0
fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", Year, Month, Day, Hour, Minute, Second)
/*
时间类型有一个自带的方法Format进行格式化
需要注意的是Go语言中格式化时间模板不是常见的YYYY-MM-DD HH:mm:ss
而是使用Go的诞生时间2006年1月2号15点04分(记忆口诀为 2006 1 2 3 4)
2006年
01 月
02 日
03 时 12小时制 15 24小时制
04 分
05 秒
*/
fmt.Println(timeObj.Format("2006/01/02 15//04/05")) // 2023/12/20 23//13/55
//2、获取当前时间戳
unixTime := timeObj.Unix()
fmt.Println("当前时间戳", unixTime) //1703085511
//3、时间戳转换成日期
var timeNum int64 = 1703085511
timeTest := time.Unix(timeNum, 0)
fmt.Println(timeTest, timeTest.Format("2006-01-02 03-04-05")) //2023-12-20 23:18:31 +0800 CST | 2023-12-20 11-18-31
//4、字符串转换时间戳
//step1、先转成时间格式
var str = "2023-12-21 12:06:47"
strObj, _ := time.ParseInLocation("2006-01-02 03:04:05", str, time.Local)
fmt.Println(strObj) //2023-12-21 12:06:47 +0800 CST
//step2、时间格式转成时间戳
fmt.Println(strObj.Unix()) //1703131607
//5、time包定义的时间间隔类型的常量
/*
const (
Nanosecond Duration = 1
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
)
*/
fmt.Println(time.Nanosecond)
fmt.Println(time.Microsecond)
//6、时间操作函数
var timeObj2 = time.Now()
fmt.Println(timeObj2.Add((time.Hour))) //增加了一小时
}
14.2 定时器
package main
import (
"fmt"
"time"
)
/*
golang定时器
*/
func main() {
//1、time.Now实现
ticker := time.NewTicker(time.Second)
n := 5
//ticker.C
for t := range ticker.C {
n--
fmt.Println(t)
if n == 0 {
ticker.Stop() //终止定时器执行
break
}
}
//2、time.Sleep() 休眠
fmt.Println("a1")
time.Sleep(time.Second)
fmt.Println("a2")
time.Sleep(time.Second)
fmt.Println("a3")
time.Sleep(time.Second)
fmt.Println("a4")
for {
time.Sleep(time.Second)
fmt.Println("我在执行定时任务")
}
}
15、指针
15.1 定义|&|*
package main
import "fmt"
func main() {
/*
1、指针
·变量的本质是给存储数据的内存地址起了一个好记的别名,比如定义一个变量 a:=10,这时直接通过
a这个变量来读取内存中保存10的这个值。在计算机底层a这个变量其实对应了一个内存地址
·指针也是一个变量,但它是一种特殊的变量,它存储的数据不是一个普通的值,而是另一个变量的内存地址
*/
var a int = 10
fmt.Printf("a的类型是:%T,a的值是:%v,a的地址是:%p\n", a, a, &a)
//a的类型是:int,a的值是:10,a的地址是:0x1400009c018
var p = &a
fmt.Printf("p的类型是:%T,p的值是:%v\n", p, p)
//p的类型是:*int,p的值是:0x1400000e0a8
/*
2、指针地址和指针类型
每个变量在运行时都会有一个地址,改地址代表变量在内存中的位置,Go中&字符放在变量前面
对变量进行去地址操作。Go中值类型(int,float,bool,string,array,struct)都有对应
指针类型,如*int,*int64,*string
*/
var b int = 100
var b1 = &b
fmt.Printf("b的地址:%p,b1的地址:%p,b的值:%v\n", b1, &b1, *b1)
//b的地址:0x14000110030,b1的地址:0x14000116020,b的值:100
/*
3、地址取值 *取值 可以取出改变量对应地址存储的值
通过 *修改的值会影响其指定的地址存储值,因为其本就是引用 类型
*/
var c = "hello golang"
c1 := &c
c2 := &c1
fmt.Printf("c的地址:%p,c1的地址:%p,c2的地址:%p,c的值:%v\n", c1, c2, &c2, *c1)
fmt.Printf("c的地址:%p,c1的地址:%p,c2的地址:%p,c的值:%v\n", &c, &c1, &c2, c)
// c的地址:0x1400008e020,c1的地址:0x140000a2028,c2的地址:0x140000a2030,c的值:hello golang
// c的地址:0x1400008e020,c1的地址:0x140000a2028,c2的地址:0x140000a2030,c的值:hello golang
*c1 = "hello js"
fmt.Printf("c的新值:%v\n", c) //c的新值:hello js
}
15.2 声明指针
package main
import "fmt"
func fn1(x int) int {
x = 10
return x
}
func fn2(x *int) {
*x = 20
}
func main() {
var a int = 5
fn1(5)
fmt.Println(a) //5 修改的是函数内部的值,不影响外部的a
fn2(&a)
fmt.Println(a) //20 通过指针修改了外部的值
/*
声明指针
*/
/* 错误写法
var b *int
*b = 100
fmt.Println(*b)
*/
//new初始化
var c = new(int)
fmt.Printf("c的值:%v,类型:%T,指针指向的地址数据值是:%v\n", c, c, *c)
//c的值:0x1400009c020,类型:*int,指针指向的地址数据值是:0
var d *int
d = new(int)
*d = 100
fmt.Println(*d) //100
}
11、声明变量的默认值 nil
当我们声明了一个变量,但还没有赋值时,golang会自动给变量赋值一个默认零值
bool -- false
numbers -- 0
string -- ""
pointers -- nil
slices -- nil
maps -- nil
channels -- nil
functions -- nil
interfaces -- nil